]>
Commit | Line | Data |
---|---|---|
b7ca0ba0 SL |
1 | From 8ebfc9053ad1cff4e1fc4292ac4109168ec7b403 Mon Sep 17 00:00:00 2001 |
2 | From: Sasha Levin <sashal@kernel.org> | |
3 | Date: Wed, 13 Mar 2024 19:58:26 +0900 | |
4 | Subject: nilfs2: fix failure to detect DAT corruption in btree and direct | |
5 | mappings | |
6 | ||
7 | From: Ryusuke Konishi <konishi.ryusuke@gmail.com> | |
8 | ||
9 | [ Upstream commit f2f26b4a84a0ef41791bd2d70861c8eac748f4ba ] | |
10 | ||
11 | Patch series "nilfs2: fix kernel bug at submit_bh_wbc()". | |
12 | ||
13 | This resolves a kernel BUG reported by syzbot. Since there are two | |
14 | flaws involved, I've made each one a separate patch. | |
15 | ||
16 | The first patch alone resolves the syzbot-reported bug, but I think | |
17 | both fixes should be sent to stable, so I've tagged them as such. | |
18 | ||
19 | This patch (of 2): | |
20 | ||
21 | Syzbot has reported a kernel bug in submit_bh_wbc() when writing file data | |
22 | to a nilfs2 file system whose metadata is corrupted. | |
23 | ||
24 | There are two flaws involved in this issue. | |
25 | ||
26 | The first flaw is that when nilfs_get_block() locates a data block using | |
27 | btree or direct mapping, if the disk address translation routine | |
28 | nilfs_dat_translate() fails with internal code -ENOENT due to DAT metadata | |
29 | corruption, it can be passed back to nilfs_get_block(). This causes | |
30 | nilfs_get_block() to misidentify an existing block as non-existent, | |
31 | causing both data block lookup and insertion to fail inconsistently. | |
32 | ||
33 | The second flaw is that nilfs_get_block() returns a successful status in | |
34 | this inconsistent state. This causes the caller __block_write_begin_int() | |
35 | or others to request a read even though the buffer is not mapped, | |
36 | resulting in a BUG_ON check for the BH_Mapped flag in submit_bh_wbc() | |
37 | failing. | |
38 | ||
39 | This fixes the first issue by changing the return value to code -EINVAL | |
40 | when a conversion using DAT fails with code -ENOENT, avoiding the | |
41 | conflicting condition that leads to the kernel bug described above. Here, | |
42 | code -EINVAL indicates that metadata corruption was detected during the | |
43 | block lookup, which will be properly handled as a file system error and | |
44 | converted to -EIO when passing through the nilfs2 bmap layer. | |
45 | ||
46 | Link: https://lkml.kernel.org/r/20240313105827.5296-1-konishi.ryusuke@gmail.com | |
47 | Link: https://lkml.kernel.org/r/20240313105827.5296-2-konishi.ryusuke@gmail.com | |
48 | Fixes: c3a7abf06ce7 ("nilfs2: support contiguous lookup of blocks") | |
49 | Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com> | |
50 | Reported-by: syzbot+cfed5b56649bddf80d6e@syzkaller.appspotmail.com | |
51 | Closes: https://syzkaller.appspot.com/bug?extid=cfed5b56649bddf80d6e | |
52 | Tested-by: Ryusuke Konishi <konishi.ryusuke@gmail.com> | |
53 | Cc: <stable@vger.kernel.org> | |
54 | Signed-off-by: Andrew Morton <akpm@linux-foundation.org> | |
55 | Signed-off-by: Sasha Levin <sashal@kernel.org> | |
56 | --- | |
57 | fs/nilfs2/btree.c | 9 +++++++-- | |
58 | fs/nilfs2/direct.c | 9 +++++++-- | |
59 | 2 files changed, 14 insertions(+), 4 deletions(-) | |
60 | ||
61 | diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c | |
62 | index 65cd599cb2ab6..4905b7cd7bf33 100644 | |
63 | --- a/fs/nilfs2/btree.c | |
64 | +++ b/fs/nilfs2/btree.c | |
65 | @@ -724,7 +724,7 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *btree, | |
66 | dat = nilfs_bmap_get_dat(btree); | |
67 | ret = nilfs_dat_translate(dat, ptr, &blocknr); | |
68 | if (ret < 0) | |
69 | - goto out; | |
70 | + goto dat_error; | |
71 | ptr = blocknr; | |
72 | } | |
73 | cnt = 1; | |
74 | @@ -743,7 +743,7 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *btree, | |
75 | if (dat) { | |
76 | ret = nilfs_dat_translate(dat, ptr2, &blocknr); | |
77 | if (ret < 0) | |
78 | - goto out; | |
79 | + goto dat_error; | |
80 | ptr2 = blocknr; | |
81 | } | |
82 | if (ptr2 != ptr + cnt || ++cnt == maxblocks) | |
83 | @@ -782,6 +782,11 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *btree, | |
84 | out: | |
85 | nilfs_btree_free_path(path); | |
86 | return ret; | |
87 | + | |
88 | + dat_error: | |
89 | + if (ret == -ENOENT) | |
90 | + ret = -EINVAL; /* Notify bmap layer of metadata corruption */ | |
91 | + goto out; | |
92 | } | |
93 | ||
94 | static void nilfs_btree_promote_key(struct nilfs_bmap *btree, | |
95 | diff --git a/fs/nilfs2/direct.c b/fs/nilfs2/direct.c | |
96 | index f353101955e3b..7faf8c285d6c9 100644 | |
97 | --- a/fs/nilfs2/direct.c | |
98 | +++ b/fs/nilfs2/direct.c | |
99 | @@ -66,7 +66,7 @@ static int nilfs_direct_lookup_contig(const struct nilfs_bmap *direct, | |
100 | dat = nilfs_bmap_get_dat(direct); | |
101 | ret = nilfs_dat_translate(dat, ptr, &blocknr); | |
102 | if (ret < 0) | |
103 | - return ret; | |
104 | + goto dat_error; | |
105 | ptr = blocknr; | |
106 | } | |
107 | ||
108 | @@ -79,7 +79,7 @@ static int nilfs_direct_lookup_contig(const struct nilfs_bmap *direct, | |
109 | if (dat) { | |
110 | ret = nilfs_dat_translate(dat, ptr2, &blocknr); | |
111 | if (ret < 0) | |
112 | - return ret; | |
113 | + goto dat_error; | |
114 | ptr2 = blocknr; | |
115 | } | |
116 | if (ptr2 != ptr + cnt) | |
117 | @@ -87,6 +87,11 @@ static int nilfs_direct_lookup_contig(const struct nilfs_bmap *direct, | |
118 | } | |
119 | *ptrp = ptr; | |
120 | return cnt; | |
121 | + | |
122 | + dat_error: | |
123 | + if (ret == -ENOENT) | |
124 | + ret = -EINVAL; /* Notify bmap layer of metadata corruption */ | |
125 | + return ret; | |
126 | } | |
127 | ||
128 | static __u64 | |
129 | -- | |
130 | 2.43.0 | |
131 |