DragonFlyBSD Kernel Audit
DF-0850 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/ext2fs/ext2_htree.c b/sys/vfs/ext2fs/ext2_htree.c
--- a/sys/vfs/ext2fs/ext2_htree.c
+++ b/sys/vfs/ext2fs/ext2_htree.c
@@ -331,12 +331,30 @@
 		level_info->h_entry = found;
 		if (levels == 0)
 			return (0);
+		/*
+		 * Reject descent into block 0 (the directory root, already
+		 * locked above).  A crafted image can set a htree entry's
+		 * block to 0, causing ext2_blkatoff to re-lock the root
+		 * buffer and panic with "lockmgr: locking against myself".
+		 */
+		if (ext2_htree_get_block(found) == 0)
+			goto error;
 		levels--;
 		if (ext2_blkatoff(vp,
 		    ext2_htree_get_block(found) * m_fs->e2fs_bsize,
 		    NULL, &bp) != 0)
 			goto error;
 		entp = ((struct ext2fs_htree_node *)bp->b_data)->h_entries;
+		/*
+		 * Validate the interior node's limit field against the
+		 * filesystem-computed limit, mirroring the root-level check
+		 * above.  Without this, an attacker-controlled on-disk limit
+		 * (e.g. 0xFFFF) defeats the cnt > get_limit() guard on the
+		 * next iteration, allowing an OOB binary-search read past
+		 * the bp->b_data buffer and a subsequent bogus block lookup.
+		 */
+		if (ext2_htree_get_limit(entp) != ext2_htree_node_limit(ip))
+			goto error;
 		info->h_levels_num++;
 		info->h_levels[info->h_levels_num - 1].h_bp = bp;
 	}