History

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.

Jan Kara committed 3 months ago

Commit b0bca4e1 parent b637f521 file · +8 −2Browse files

Showing the changes to fs/buffer.c (1 of 1 file). Show all

Unified · Split

fs/buffer.c+8−2
@@ -1123,12 +1123,18 @@ EXPORT_SYMBOL(mark_buffer_dirty); · 1122 unmodified lines
11231123
11241124 void mark_buffer_write_io_error(struct buffer_head *bh)
11251125 {
1126+ struct mapping_metadata_bhs *mmb;
1127+
11261128 set_buffer_write_io_error(bh);
11271129 /* FIXME: do we need to set this in both places? */
11281130 if (bh->b_folio && bh->b_folio->mapping)
11291131 mapping_set_error(bh->b_folio->mapping, -EIO);
1130− if (bh->b_mmb)
1131− mapping_set_error(bh->b_mmb->mapping, -EIO);
1132+ /* Protect us from mmb & inode getting freed while we work on it */
1133+ rcu_read_lock();
1134+ mmb = READ_ONCE(bh->b_mmb);
1135+ if (mmb)
1136+ mapping_set_error(mmb->mapping, -EIO);
1137+ rcu_read_unlock();
11321138 }
11331139 EXPORT_SYMBOL(mark_buffer_write_io_error);
11341140