DF-5784 / fix.diff
--- a/sys/vfs/hammer2/hammer2_chain.c 2026-10-12 15:21:41.020454194 +0000 +++ b/sys/vfs/hammer2/hammer2_chain.c 2026-10-12 15:21:41.020454194 +0000 @@ -4906,6 +4906,39 @@ } /* + * Media sanity check for a blockref array element. Returns non-zero if + * the element is safe to use as an index. + * + * (1) The type must be a real media type (the pseudo-types VOLUME and + * FREEMAP and the INVALID type never appear in a blockref array; + * anything else would panic hammer2_chain_alloc()). + * + * (2) The key range must not wrap the 64-bit keyspace, i.e. + * key + (1 << keybits) - 1 must be >= key. A wrapped range makes + * hammer2_chain_cmp() report the chain as 'fully to the left' of + * every other chain regardless of the other chain's key, breaking + * the in-memory rbtree ordering (duplicated chains as lookups are + * unable to find the mis-placed ones), and makes iteration keys + * regress (key_next = key + (1<<keybits) wraps below key_beg), so + * directory scans can never advance past the element. + * + * keybits == 64 (whole-keyspace dirents) is allowed: it does not wrap + * under this test. + */ +static int +hammer2_bref_sane(const hammer2_blockref_t *bref) +{ + if (bref->type < HAMMER2_BREF_TYPE_INODE || + bref->type > HAMMER2_BREF_TYPE_FREEMAP_LEAF) + return (0); + if (bref->keybits < 64 && + bref->key > HAMMER2_KEY_MAX - + (((hammer2_key_t)1 << bref->keybits) - 1)) + return (0); + return (1); +} + +/* * Returns the index of the nearest element in the blockref array >= elm. * Returns (count) if no element could be found. * @@ -4973,11 +5006,13 @@ * elements. */ while (i < count) { - if (scan->type != HAMMER2_BREF_TYPE_EMPTY) { + if (scan->type != HAMMER2_BREF_TYPE_EMPTY && + hammer2_bref_sane(scan)) { scan_end = scan->key + ((hammer2_key_t)1 << scan->keybits) - 1; if (scan->key > key_beg || scan_end >= key_beg) break; + /* else fall through and advance */ } if (i >= limit) return (count); |