DF-3003 / fix.diff
--- 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); |