⬢ DragonFlyBSD Kernel Audit
DF-5785 / fix.diff
← back to finding ↓ download raw
--- 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);