Skip to content

Commit a9438b4

Browse files
jankaraaxboe
authored andcommitted
writeback: Add asserts for adding freed inode to lists
In the past we had several use-after-free issues with inodes getting added to writeback lists after evict() removed them. These are painful to debug so add some asserts to catch the problem earlier. The only non-obvious change in the commit is that we need to tweak redirty_tail_locked() to avoid triggering assertion in inode_io_list_move_locked(). Signed-off-by: Jan Kara <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jens Axboe <[email protected]>
1 parent eb70814 commit a9438b4

File tree

1 file changed

+14
-1
lines changed

1 file changed

+14
-1
lines changed

fs/fs-writeback.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ static bool inode_io_list_move_locked(struct inode *inode,
121121
{
122122
assert_spin_locked(&wb->list_lock);
123123
assert_spin_locked(&inode->i_lock);
124+
WARN_ON_ONCE(inode->i_state & I_FREEING);
124125

125126
list_move(&inode->i_io_list, head);
126127

@@ -280,6 +281,7 @@ static void inode_cgwb_move_to_attached(struct inode *inode,
280281
{
281282
assert_spin_locked(&wb->list_lock);
282283
assert_spin_locked(&inode->i_lock);
284+
WARN_ON_ONCE(inode->i_state & I_FREEING);
283285

284286
inode->i_state &= ~I_SYNC_QUEUED;
285287
if (wb != &wb->bdi->wb)
@@ -1129,6 +1131,7 @@ static void inode_cgwb_move_to_attached(struct inode *inode,
11291131
{
11301132
assert_spin_locked(&wb->list_lock);
11311133
assert_spin_locked(&inode->i_lock);
1134+
WARN_ON_ONCE(inode->i_state & I_FREEING);
11321135

11331136
inode->i_state &= ~I_SYNC_QUEUED;
11341137
list_del_init(&inode->i_io_list);
@@ -1294,6 +1297,17 @@ static void redirty_tail_locked(struct inode *inode, struct bdi_writeback *wb)
12941297
{
12951298
assert_spin_locked(&inode->i_lock);
12961299

1300+
inode->i_state &= ~I_SYNC_QUEUED;
1301+
/*
1302+
* When the inode is being freed just don't bother with dirty list
1303+
* tracking. Flush worker will ignore this inode anyway and it will
1304+
* trigger assertions in inode_io_list_move_locked().
1305+
*/
1306+
if (inode->i_state & I_FREEING) {
1307+
list_del_init(&inode->i_io_list);
1308+
wb_io_lists_depopulated(wb);
1309+
return;
1310+
}
12971311
if (!list_empty(&wb->b_dirty)) {
12981312
struct inode *tail;
12991313

@@ -1302,7 +1316,6 @@ static void redirty_tail_locked(struct inode *inode, struct bdi_writeback *wb)
13021316
inode->dirtied_when = jiffies;
13031317
}
13041318
inode_io_list_move_locked(inode, wb, &wb->b_dirty);
1305-
inode->i_state &= ~I_SYNC_QUEUED;
13061319
}
13071320

13081321
static void redirty_tail(struct inode *inode, struct bdi_writeback *wb)

0 commit comments

Comments
 (0)