| From 492888df0c7b42fc0843631168b0021bc4caee84 Mon Sep 17 00:00:00 2001 |
| From: Jan Kara <jack@suse.cz> |
| Date: Thu, 4 May 2023 14:55:24 +0200 |
| Subject: ext4: fix data races when using cached status extents |
| |
| From: Jan Kara <jack@suse.cz> |
| |
| commit 492888df0c7b42fc0843631168b0021bc4caee84 upstream. |
| |
| 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.com |
| Suggested-by: Dmitry Vyukov <dvyukov@google.com> |
| Signed-off-by: Jan Kara <jack@suse.cz> |
| Link: https://lore.kernel.org/r/20230504125524.10802-1-jack@suse.cz |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/ext4/extents_status.c | 30 +++++++++++++----------------- |
| 1 file changed, 13 insertions(+), 17 deletions(-) |
| |
| --- a/fs/ext4/extents_status.c |
| +++ b/fs/ext4/extents_status.c |
| @@ -267,14 +267,12 @@ static void __es_find_extent_range(struc |
| |
| /* see if the extent has been cached */ |
| es->es_lblk = es->es_len = es->es_pblk = 0; |
| - if (tree->cache_es) { |
| - es1 = tree->cache_es; |
| - if (in_range(lblk, es1->es_lblk, es1->es_len)) { |
| - es_debug("%u cached by [%u/%u) %llu %x\n", |
| - lblk, es1->es_lblk, es1->es_len, |
| - ext4_es_pblock(es1), ext4_es_status(es1)); |
| - goto out; |
| - } |
| + es1 = READ_ONCE(tree->cache_es); |
| + if (es1 && in_range(lblk, es1->es_lblk, es1->es_len)) { |
| + es_debug("%u cached by [%u/%u) %llu %x\n", |
| + lblk, es1->es_lblk, es1->es_len, |
| + ext4_es_pblock(es1), ext4_es_status(es1)); |
| + goto out; |
| } |
| |
| es1 = __es_tree_search(&tree->root, lblk); |
| @@ -293,7 +291,7 @@ out: |
| } |
| |
| if (es1 && matching_fn(es1)) { |
| - tree->cache_es = es1; |
| + WRITE_ONCE(tree->cache_es, es1); |
| es->es_lblk = es1->es_lblk; |
| es->es_len = es1->es_len; |
| es->es_pblk = es1->es_pblk; |
| @@ -931,14 +929,12 @@ int ext4_es_lookup_extent(struct inode * |
| |
| /* find extent in cache firstly */ |
| es->es_lblk = es->es_len = es->es_pblk = 0; |
| - if (tree->cache_es) { |
| - es1 = tree->cache_es; |
| - if (in_range(lblk, es1->es_lblk, es1->es_len)) { |
| - es_debug("%u cached by [%u/%u)\n", |
| - lblk, es1->es_lblk, es1->es_len); |
| - found = 1; |
| - goto out; |
| - } |
| + es1 = READ_ONCE(tree->cache_es); |
| + if (es1 && in_range(lblk, es1->es_lblk, es1->es_len)) { |
| + es_debug("%u cached by [%u/%u)\n", |
| + lblk, es1->es_lblk, es1->es_len); |
| + found = 1; |
| + goto out; |
| } |
| |
| node = tree->root.rb_node; |