Commit 492888df authored by Jan Kara's avatar Jan Kara Committed by Theodore Ts'o

ext4: fix data races when using cached status extents

When using cached extent stored in extent status tree in tree->cache_es
another process holding ei->i_es_lock for reading can be racing with us
setting new value of tree->cache_es. If the compiler would decide to
refetch tree->cache_es at an unfortunate moment, it could result in a
bogus in_range() check. Fix the possible race by using READ_ONCE() when
using tree->cache_es only under ei->i_es_lock for reading.

Cc: stable@kernel.org
Reported-by: syzbot+4a03518df1e31b537066@syzkaller.appspotmail.com
Link: https://lore.kernel.org/all/000000000000d3b33905fa0fd4a6@google.comSuggested-by: default avatarDmitry Vyukov <dvyukov@google.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/20230504125524.10802-1-jack@suse.czSigned-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent 00d873c1
...@@ -267,15 +267,13 @@ static void __es_find_extent_range(struct inode *inode, ...@@ -267,15 +267,13 @@ static void __es_find_extent_range(struct inode *inode,
/* see if the extent has been cached */ /* see if the extent has been cached */
es->es_lblk = es->es_len = es->es_pblk = 0; es->es_lblk = es->es_len = es->es_pblk = 0;
if (tree->cache_es) { es1 = READ_ONCE(tree->cache_es);
es1 = tree->cache_es; if (es1 && in_range(lblk, es1->es_lblk, es1->es_len)) {
if (in_range(lblk, es1->es_lblk, es1->es_len)) {
es_debug("%u cached by [%u/%u) %llu %x\n", es_debug("%u cached by [%u/%u) %llu %x\n",
lblk, es1->es_lblk, es1->es_len, lblk, es1->es_lblk, es1->es_len,
ext4_es_pblock(es1), ext4_es_status(es1)); ext4_es_pblock(es1), ext4_es_status(es1));
goto out; goto out;
} }
}
es1 = __es_tree_search(&tree->root, lblk); es1 = __es_tree_search(&tree->root, lblk);
...@@ -293,7 +291,7 @@ static void __es_find_extent_range(struct inode *inode, ...@@ -293,7 +291,7 @@ static void __es_find_extent_range(struct inode *inode,
} }
if (es1 && matching_fn(es1)) { if (es1 && matching_fn(es1)) {
tree->cache_es = es1; WRITE_ONCE(tree->cache_es, es1);
es->es_lblk = es1->es_lblk; es->es_lblk = es1->es_lblk;
es->es_len = es1->es_len; es->es_len = es1->es_len;
es->es_pblk = es1->es_pblk; es->es_pblk = es1->es_pblk;
...@@ -931,15 +929,13 @@ int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk, ...@@ -931,15 +929,13 @@ int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,
/* find extent in cache firstly */ /* find extent in cache firstly */
es->es_lblk = es->es_len = es->es_pblk = 0; es->es_lblk = es->es_len = es->es_pblk = 0;
if (tree->cache_es) { es1 = READ_ONCE(tree->cache_es);
es1 = tree->cache_es; if (es1 && in_range(lblk, es1->es_lblk, es1->es_len)) {
if (in_range(lblk, es1->es_lblk, es1->es_len)) {
es_debug("%u cached by [%u/%u)\n", es_debug("%u cached by [%u/%u)\n",
lblk, es1->es_lblk, es1->es_len); lblk, es1->es_lblk, es1->es_len);
found = 1; found = 1;
goto out; goto out;
} }
}
node = tree->root.rb_node; node = tree->root.rb_node;
while (node) { while (node) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment