The classic HFS driver has the same B-tree node leak as hfsplus: on the hfs_btree_open() error path after hfs_bnode_find(tree, HFS_TREE_HEAD), an errored head node left in tree->node_hash is not freed because free_tree: does a bare kfree(tree) instead of walking the hash. Apply the same fix: factor the node-hash freeing into hfs_bnode_hash_free() and call it from hfs_btree_close() and the hfs_btree_open() error path. Found via coverage-guided fuzzing (syzkaller + kmemleak) by Noroxi. Signed-off-by: Mahmut Emin Kurhan --- fs/hfs/btree.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c index 41b4e8fc9..51d1e19a4 100644 --- a/fs/hfs/btree.c +++ b/fs/hfs/btree.c @@ -131,6 +131,24 @@ static int hfs_bmap_clear_bit(struct hfs_bnode *node, u32 node_bit_idx) } /* Get a reference to a B*Tree and do some initial checks */ +static void hfs_bnode_hash_free(struct hfs_btree *tree) +{ + struct hfs_bnode *node; + int i; + + for (i = 0; i < NODE_HASH_SIZE; i++) { + while ((node = tree->node_hash[i])) { + tree->node_hash[i] = node->next_hash; + if (atomic_read(&node->refcnt)) + pr_err("node %d:%d still has %d user(s)!\n", + node->tree->cnid, node->this, + atomic_read(&node->refcnt)); + hfs_bnode_free(node); + tree->node_hash_cnt--; + } + } +} + struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id, btree_keycmp keycmp) { struct hfs_btree *tree; @@ -296,6 +314,7 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id, btree_keycmp ke tree->inode->i_mapping->a_ops = &hfs_aops; iput(tree->inode); free_tree: + hfs_bnode_hash_free(tree); kfree(tree); return NULL; } @@ -303,23 +322,10 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id, btree_keycmp ke /* Release resources used by a btree */ void hfs_btree_close(struct hfs_btree *tree) { - struct hfs_bnode *node; - int i; - if (!tree) return; - for (i = 0; i < NODE_HASH_SIZE; i++) { - while ((node = tree->node_hash[i])) { - tree->node_hash[i] = node->next_hash; - if (atomic_read(&node->refcnt)) - pr_err("node %d:%d still has %d user(s)!\n", - node->tree->cnid, node->this, - atomic_read(&node->refcnt)); - hfs_bnode_free(node); - tree->node_hash_cnt--; - } - } + hfs_bnode_hash_free(tree); iput(tree->inode); kfree(tree); } -- 2.43.0