Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 525c802d authored by Jan Kara's avatar Jan Kara Committed by Greg Kroah-Hartman
Browse files

ext4: fix data races when using cached status extents



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: default avatar <syzbot+4a03518df1e31b537066@syzkaller.appspotmail.com>
Link: https://lore.kernel.org/all/000000000000d3b33905fa0fd4a6@google.com


Suggested-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.cz


Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 4f4fd982
Loading
Loading
Loading
Loading
+13 −17
Original line number Diff line number Diff line
@@ -269,15 +269,13 @@ static void __es_find_extent_range(struct inode *inode,

	/* 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)) {
	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);

@@ -295,7 +293,7 @@ static void __es_find_extent_range(struct inode *inode,
	}

	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;
@@ -916,15 +914,13 @@ int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,

	/* 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)) {
	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;
	while (node) {