summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2026-07-27 12:49:20 +0200
committerChristian Brauner <brauner@kernel.org>2026-07-27 16:25:31 +0200
commitb0bca4e95b03438cd2c20bb7be3e6e109d1a01fa (patch)
treef90b8646ec3c1d69a355415cee2b5db54e6eebc4
parentb637f52f2f265b001834c5efcc6e1d7165ca52f7 (diff)
downloadlinux-next-b0bca4e95b03438cd2c20bb7be3e6e109d1a01fa.tar.gz
linux-next-b0bca4e95b03438cd2c20bb7be3e6e109d1a01fa.zip
fs: Fix possible UAF in mark_buffer_write_io_error()
When filesystem is freeing inode it calls mmb_invalidate() which removes bhs from inode's metadata bh tracking and clears b_mmb for them. However if the inode is getting deleted, we don't bother with calling mmb_sync() before and thus these buffers can be under IO and we can be racing with IO completion handler calling mark_buffer_write_io_error(). This race can lead to mark_buffer_write_io_error() either hitting NULL pointer reference or trying to operate on already freed inode. Protect the mapping handling with RCU to make sure mmb and inode aren't freed before we are done with them. Reported-by: Sashiko <sashiko-bot@kernel.org> Signed-off-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260727104923.3828017-22-jack@suse.cz Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
-rw-r--r--fs/buffer.c10
1 files changed, 8 insertions, 2 deletions
diff --git a/fs/buffer.c b/fs/buffer.c
index 9af5f061a1f8..daaa6614a6d6 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -1123,12 +1123,18 @@ EXPORT_SYMBOL(mark_buffer_dirty);
void mark_buffer_write_io_error(struct buffer_head *bh)
{
+ struct mapping_metadata_bhs *mmb;
+
set_buffer_write_io_error(bh);
/* FIXME: do we need to set this in both places? */
if (bh->b_folio && bh->b_folio->mapping)
mapping_set_error(bh->b_folio->mapping, -EIO);
- if (bh->b_mmb)
- mapping_set_error(bh->b_mmb->mapping, -EIO);
+ /* Protect us from mmb & inode getting freed while we work on it */
+ rcu_read_lock();
+ mmb = READ_ONCE(bh->b_mmb);
+ if (mmb)
+ mapping_set_error(mmb->mapping, -EIO);
+ rcu_read_unlock();
}
EXPORT_SYMBOL(mark_buffer_write_io_error);