aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2016-03-18 13:50:03 -0400
committerJens Axboe <axboe@fb.com>2016-03-20 11:44:18 -0400
commit614a4e3773148a31f58dc174bbf578ceb63510c2 (patch)
treed4f4a24bcdb54e92b6e800fd72c073f0bf08f2d2
parent897bb0c7f1ea82d7cc882b19790b5e1df00ffc29 (diff)
writeback, cgroup: fix premature wb_put() in locked_inode_to_wb_and_lock_list()
locked_inode_to_wb_and_lock_list() wb_get()'s the wb associated with the target inode, unlocks inode, locks the wb's list_lock and verifies that the inode is still associated with the wb. To prevent the wb going away between dropping inode lock and acquiring list_lock, the wb is pinned while inode lock is held. The wb reference is put right after acquiring list_lock citing that the wb won't be dereferenced anymore. This isn't true. If the inode is still associated with the wb, the inode has reference and it's safe to return the wb; however, if inode has been switched, the wb still needs to be unlocked which is a dereference and can lead to use-after-free if it it races with wb destruction. Fix it by putting the reference after releasing list_lock. Signed-off-by: Tejun Heo <tj@kernel.org> Fixes: 87e1d789bf55 ("writeback: implement [locked_]inode_to_wb_and_lock_list()") Cc: stable@vger.kernel.org # v4.2+ Tested-by: Tahsin Erdogan <tahsin@google.com> Signed-off-by: Jens Axboe <axboe@fb.com>
-rw-r--r--fs/fs-writeback.c8
1 files changed, 5 insertions, 3 deletions
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 5c46ed9f3e14..7b9582ed26f2 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -281,13 +281,15 @@ locked_inode_to_wb_and_lock_list(struct inode *inode)
281 wb_get(wb); 281 wb_get(wb);
282 spin_unlock(&inode->i_lock); 282 spin_unlock(&inode->i_lock);
283 spin_lock(&wb->list_lock); 283 spin_lock(&wb->list_lock);
284 wb_put(wb); /* not gonna deref it anymore */
285 284
286 /* i_wb may have changed inbetween, can't use inode_to_wb() */ 285 /* i_wb may have changed inbetween, can't use inode_to_wb() */
287 if (likely(wb == inode->i_wb)) 286 if (likely(wb == inode->i_wb)) {
288 return wb; /* @inode already has ref */ 287 wb_put(wb); /* @inode already has ref */
288 return wb;
289 }
289 290
290 spin_unlock(&wb->list_lock); 291 spin_unlock(&wb->list_lock);
292 wb_put(wb);
291 cpu_relax(); 293 cpu_relax();
292 spin_lock(&inode->i_lock); 294 spin_lock(&inode->i_lock);
293 } 295 }