DragonFlyBSD Kernel Audit
DF-3003 / fix.diff
← back to finding ↓ download raw
--- sys/vfs/hammer/hammer_crc.h.orig
+++ sys/vfs/hammer/hammer_crc.h
@@ -292,6 +292,16 @@
 static __inline int
 hammer_crc_test_leaf(uint32_t vol_version, void *data, hammer_btree_leaf_elm_t leaf)
 {
+	/*
+	 * An INODE record whose data_len is not sizeof(struct
+	 * hammer_inode_data) is corrupt.  hammer_crc_get_leaf() returns 0
+	 * for it, which must never be interpreted as a valid CRC match
+	 * against a stored data_crc of 0 (crafted image, DF-3003).
+	 */
+	if (leaf->base.rec_type == HAMMER_RECTYPE_INODE &&
+	    leaf->data_len != 0 &&
+	    leaf->data_len != (int32_t)sizeof(struct hammer_inode_data))
+		return(0);
 	if (leaf->data_crc == hammer_crc_get_leaf(vol_version, data, leaf))
 		return(1);
 	if (vol_version >= HAMMER_VOL_VERSION_SEVEN) {
--- sys/vfs/hammer/hammer_btree.c.orig
+++ sys/vfs/hammer/hammer_btree.c
@@ -731,6 +731,28 @@
 		return(0);
 
 	/*
+	 * Sanity-check the on-disk record length.  A crafted filesystem
+	 * can otherwise place cursor->data near the end of its 16KB buffer
+	 * and/or claim a bogus length, causing out-of-bounds reads and,
+	 * for inode records, the in-place atime/mtime update in
+	 * hammer_update_itimes() to write past the record's 16KB block
+	 * (DF-3003).
+	 */
+	if (elm->leaf.base.rec_type == HAMMER_RECTYPE_INODE &&
+	    data_len != (int32_t)sizeof(struct hammer_inode_data)) {
+		hkprintf("bad inode record data_len %d\n", data_len);
+		return(EIO);
+	}
+	if (data_len > 0 &&
+	    (data_off & HAMMER_BUFMASK) + data_len >
+	    (hammer_off_t)HAMMER_BUFSIZE_DOALIGN((uint32_t)data_len)) {
+		hkprintf("record data crosses buffer boundary "
+			 "(off=%016jx len=%d)\n",
+			 (intmax_t)data_off, data_len);
+		return(EIO);
+	}
+
+	/*
 	 * Load the data
 	 */
 	KKASSERT(data_len >= 0 && data_len <= HAMMER_XBUFSIZE);