Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

hfsplus: validate b-tree node 0 bitmap at mount time

Syzkaller reported an issue with corrupted HFS+ images where the b-tree
allocation bitmap indicates that the header node (Node 0) is free. Node 0
must always be allocated as it contains the b-tree header record and the
allocation bitmap itself. Violating this invariant leads to allocator
corruption, which cascades into kernel panics or undefined behavior when
the filesystem attempts to allocate blocks.

Prevent trusting a corrupted allocator state by adding a validation check
during hfs_btree_open(). Introduce the hfs_bmap_test_bit() helper
(utilizing the newly added map-access API) to safely verify that the MSB
of the first bitmap byte (representing Node 0) is marked as allocated.
The helper returns a boolean, allowing the caller to safely catch both
structural IO errors and illegally cleared bits in a single check.

If corruption is detected, print a warning identifying the specific
corrupted tree and force the filesystem to mount read-only (SB_RDONLY).
This prevents kernel panics from corrupted images while enabling data
recovery.

As a minor cleanup to support the warning logs, replace the verbose CNID
logic with cleaner macro definitions (using official structural names like
"Extents Overflow File") and a dedicated string lookup helper.

Reported-by: syzbot+1c8ff72d0cd8a50dfeaa@syzkaller.appspotmail.com
Link: https://syzkaller.appspot.com/bug?extid=1c8ff72d0cd8a50dfeaa
Link: https://lore.kernel.org/all/20260315172005.2066677-1-shardul.b@mpiricsoftware.com/
Signed-off-by: Shardul Bankar <shardul.b@mpiricsoftware.com>
Reviewed-by: Viacheslav Dubeyko <slava@dubeyko.com>
Tested-by: Viacheslav Dubeyko <slava@dubeyko.com>
Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
Link: https://lore.kernel.org/r/20260318073823.3933718-3-shardul.b@mpiricsoftware.com
Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>

authored by

Shardul Bankar and committed by
Viacheslav Dubeyko
8ad2c6a3 a8eed0ba

+60
+60
fs/hfsplus/btree.c
··· 186 186 } 187 187 188 188 /** 189 + * hfs_bmap_test_bit - test a bit in the b-tree map 190 + * @node: the b-tree node containing the map record 191 + * @node_bit_idx: the relative bit index within the node's map record 192 + * 193 + * Returns true if set, false if clear or on failure. 194 + */ 195 + static bool hfs_bmap_test_bit(struct hfs_bnode *node, u32 node_bit_idx) 196 + { 197 + struct hfs_bmap_ctx ctx; 198 + struct page *page; 199 + u8 *bmap, byte, mask; 200 + 201 + page = hfs_bmap_get_map_page(node, &ctx, node_bit_idx / BITS_PER_BYTE); 202 + if (IS_ERR(page)) 203 + return false; 204 + 205 + bmap = kmap_local_page(page); 206 + byte = bmap[ctx.off]; 207 + kunmap_local(bmap); 208 + 209 + mask = 1 << (7 - (node_bit_idx % BITS_PER_BYTE)); 210 + return (byte & mask) != 0; 211 + } 212 + 213 + 214 + /** 189 215 * hfs_bmap_clear_bit - clear a bit in the b-tree map 190 216 * @node: the b-tree node containing the map record 191 217 * @node_bit_idx: the relative bit index within the node's map record ··· 244 218 return 0; 245 219 } 246 220 221 + #define HFS_EXTENT_TREE_NAME "Extents Overflow File" 222 + #define HFS_CATALOG_TREE_NAME "Catalog File" 223 + #define HFS_ATTR_TREE_NAME "Attributes File" 224 + #define HFS_UNKNOWN_TREE_NAME "Unknown B-tree" 225 + 226 + static const char *hfs_btree_name(u32 cnid) 227 + { 228 + switch (cnid) { 229 + case HFSPLUS_EXT_CNID: 230 + return HFS_EXTENT_TREE_NAME; 231 + case HFSPLUS_CAT_CNID: 232 + return HFS_CATALOG_TREE_NAME; 233 + case HFSPLUS_ATTR_CNID: 234 + return HFS_ATTR_TREE_NAME; 235 + default: 236 + return HFS_UNKNOWN_TREE_NAME; 237 + } 238 + } 239 + 247 240 /* Get a reference to a B*Tree and do some initial checks */ 248 241 struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) 249 242 { 250 243 struct hfs_btree *tree; 251 244 struct hfs_btree_header_rec *head; 252 245 struct address_space *mapping; 246 + struct hfs_bnode *node; 253 247 struct inode *inode; 254 248 struct page *page; 255 249 unsigned int size; ··· 377 331 378 332 kunmap_local(head); 379 333 put_page(page); 334 + 335 + node = hfs_bnode_find(tree, HFSPLUS_TREE_HEAD); 336 + if (IS_ERR(node)) 337 + goto free_inode; 338 + 339 + if (!hfs_bmap_test_bit(node, 0)) { 340 + pr_warn("(%s): %s (cnid 0x%x) map record invalid or bitmap corruption detected, forcing read-only.\n", 341 + sb->s_id, hfs_btree_name(id), id); 342 + pr_warn("Run fsck.hfsplus to repair.\n"); 343 + sb->s_flags |= SB_RDONLY; 344 + } 345 + 346 + hfs_bnode_put(node); 347 + 380 348 return tree; 381 349 382 350 fail_page: