| From 6ca1b1d703641f63596a6008453133bc41c35fcf Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 14 Jul 2021 21:27:08 -0700 |
| Subject: hfs: add lock nesting notation to hfs_find_init |
| |
| From: Desmond Cheong Zhi Xi <desmondcheongzx@gmail.com> |
| |
| [ Upstream commit b3b2177a2d795e35dc11597b2609eb1e7e57e570 ] |
| |
| Syzbot reports a possible recursive lock in [1]. |
| |
| This happens due to missing lock nesting information. From the logs, we |
| see that a call to hfs_fill_super is made to mount the hfs filesystem. |
| While searching for the root inode, the lock on the catalog btree is |
| grabbed. Then, when the parent of the root isn't found, a call to |
| __hfs_bnode_create is made to create the parent of the root. This |
| eventually leads to a call to hfs_ext_read_extent which grabs a lock on |
| the extents btree. |
| |
| Since the order of locking is catalog btree -> extents btree, this lock |
| hierarchy does not lead to a deadlock. |
| |
| To tell lockdep that this locking is safe, we add nesting notation to |
| distinguish between catalog btrees, extents btrees, and attributes |
| btrees (for HFS+). This has already been done in hfsplus. |
| |
| Link: https://syzkaller.appspot.com/bug?id=f007ef1d7a31a469e3be7aeb0fde0769b18585db [1] |
| Link: https://lkml.kernel.org/r/20210701030756.58760-4-desmondcheongzx@gmail.com |
| Signed-off-by: Desmond Cheong Zhi Xi <desmondcheongzx@gmail.com> |
| Reported-by: syzbot+b718ec84a87b7e73ade4@syzkaller.appspotmail.com |
| Tested-by: syzbot+b718ec84a87b7e73ade4@syzkaller.appspotmail.com |
| Reviewed-by: Viacheslav Dubeyko <slava@dubeyko.com> |
| Cc: Al Viro <viro@zeniv.linux.org.uk> |
| Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Cc: Gustavo A. R. Silva <gustavoars@kernel.org> |
| Cc: Shuah Khan <skhan@linuxfoundation.org> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/hfs/bfind.c | 14 +++++++++++++- |
| fs/hfs/btree.h | 7 +++++++ |
| 2 files changed, 20 insertions(+), 1 deletion(-) |
| |
| diff --git a/fs/hfs/bfind.c b/fs/hfs/bfind.c |
| index de69d8a24f6d..7f2ef95dcd05 100644 |
| --- a/fs/hfs/bfind.c |
| +++ b/fs/hfs/bfind.c |
| @@ -24,7 +24,19 @@ int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd) |
| fd->key = ptr + tree->max_key_len + 2; |
| hfs_dbg(BNODE_REFS, "find_init: %d (%p)\n", |
| tree->cnid, __builtin_return_address(0)); |
| - mutex_lock(&tree->tree_lock); |
| + switch (tree->cnid) { |
| + case HFS_CAT_CNID: |
| + mutex_lock_nested(&tree->tree_lock, CATALOG_BTREE_MUTEX); |
| + break; |
| + case HFS_EXT_CNID: |
| + mutex_lock_nested(&tree->tree_lock, EXTENTS_BTREE_MUTEX); |
| + break; |
| + case HFS_ATTR_CNID: |
| + mutex_lock_nested(&tree->tree_lock, ATTR_BTREE_MUTEX); |
| + break; |
| + default: |
| + return -EINVAL; |
| + } |
| return 0; |
| } |
| |
| diff --git a/fs/hfs/btree.h b/fs/hfs/btree.h |
| index 2715f416b5a8..308b5f1af65b 100644 |
| --- a/fs/hfs/btree.h |
| +++ b/fs/hfs/btree.h |
| @@ -12,6 +12,13 @@ typedef int (*btree_keycmp)(const btree_key *, const btree_key *); |
| |
| #define NODE_HASH_SIZE 256 |
| |
| +/* B-tree mutex nested subclasses */ |
| +enum hfs_btree_mutex_classes { |
| + CATALOG_BTREE_MUTEX, |
| + EXTENTS_BTREE_MUTEX, |
| + ATTR_BTREE_MUTEX, |
| +}; |
| + |
| /* A HFS BTree held in memory */ |
| struct hfs_btree { |
| struct super_block *sb; |
| -- |
| 2.30.2 |
| |