--- a/sys/libkern/linux_idr.c +++ b/sys/libkern/linux_idr.c @@ -131,9 +131,20 @@ /* Must be 2^n - 1 */ #define IDR_DEFAULT_SIZE 255 +/* + * Maximum id we will grow the table for. The growth loop below computes + * nf = 2 * nf + 1 in a signed int: for want >= (1<<30)-1 the loop can + * overflow, wrap nf to -1 and spin forever (with the idr token held), + * and any want >= (1<<29)-1 asks the allocator for a >= 8GB node array + * under M_WAITOK. No in-tree consumer comes anywhere near this many + * ids (drm object handles, 16-bit amdgpu PASIDs). Fail the caller + * instead of hanging or exhausting the machine. + */ +#define IDR_MAX_IDS ((1 << 24) - 1) + MALLOC_DEFINE(M_IDR, "idr", "Integer ID management"); -static void idr_grow(struct idr *idp, int want); +static int idr_grow(struct idr *idp, int want); static void idr_reserve(struct idr *idp, int id, int incr); static int idr_find_free(struct idr *idp, int want, int lim); @@ -271,8 +282,12 @@ /* * Grow if necessary (or if forced by the loop) */ - if (want >= idp->idr_count) - idr_grow(idp, want); + if (want >= idp->idr_count) { + if (idr_grow(idp, want)) { + result = 0; /* failure */ + break; + } + } /* * Check if a spot is available, break and return 0 if true, @@ -371,8 +386,12 @@ lwkt_gettoken(&idp->idr_token); grow_again: - if (want >= idp->idr_count) - idr_grow(idp, want); + if (want >= idp->idr_count) { + if (idr_grow(idp, want)) { + result = -ENOMEM; + goto done; + } + } /* * Check if a spot is available, break and return 0 if true, @@ -417,12 +436,15 @@ * * Caller must hold a blockable lock. */ -static void +static int idr_grow(struct idr *idp, int want) { struct idr_node *oldnodes, *newnodes; int nf; + if (want >= IDR_MAX_IDS) + return (ENOMEM); + /* We want 2^n - 1 descriptors */ nf = idp->idr_count; do { @@ -440,7 +462,7 @@ /* We might race another grow */ if (nf <= idp->idr_count) { kfree(newnodes, M_IDR); - return; + return (0); } /* @@ -458,6 +480,7 @@ kfree(oldnodes, M_IDR); } idp->idr_nexpands++; + return (0); } void *