| From 6d1e41d837f1202a7d1b80929eaa2f2119b770d9 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 2 Apr 2021 17:11:45 +0800 |
| Subject: writeback: fix obtain a reference to a freeing memcg css |
| |
| From: Muchun Song <songmuchun@bytedance.com> |
| |
| [ Upstream commit 8b0ed8443ae6458786580d36b7d5f8125535c5d4 ] |
| |
| The caller of wb_get_create() should pin the memcg, because |
| wb_get_create() relies on this guarantee. The rcu read lock |
| only can guarantee that the memcg css returned by css_from_id() |
| cannot be released, but the reference of the memcg can be zero. |
| |
| rcu_read_lock() |
| memcg_css = css_from_id() |
| wb_get_create(memcg_css) |
| cgwb_create(memcg_css) |
| // css_get can change the ref counter from 0 back to 1 |
| css_get(memcg_css) |
| rcu_read_unlock() |
| |
| Fix it by holding a reference to the css before calling |
| wb_get_create(). This is not a problem I encountered in the |
| real world. Just the result of a code review. |
| |
| Fixes: 682aa8e1a6a1 ("writeback: implement unlocked_inode_to_wb transaction and use it for stat updates") |
| Link: https://lore.kernel.org/r/20210402091145.80635-1-songmuchun@bytedance.com |
| Signed-off-by: Muchun Song <songmuchun@bytedance.com> |
| Acked-by: Michal Hocko <mhocko@suse.com> |
| Acked-by: Tejun Heo <tj@kernel.org> |
| Signed-off-by: Jan Kara <jack@suse.cz> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/fs-writeback.c | 9 +++++++-- |
| 1 file changed, 7 insertions(+), 2 deletions(-) |
| |
| diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c |
| index 3a0d7b8af141..22e9c88f3960 100644 |
| --- a/fs/fs-writeback.c |
| +++ b/fs/fs-writeback.c |
| @@ -510,9 +510,14 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id) |
| /* find and pin the new wb */ |
| rcu_read_lock(); |
| memcg_css = css_from_id(new_wb_id, &memory_cgrp_subsys); |
| - if (memcg_css) |
| - isw->new_wb = wb_get_create(bdi, memcg_css, GFP_ATOMIC); |
| + if (memcg_css && !css_tryget(memcg_css)) |
| + memcg_css = NULL; |
| rcu_read_unlock(); |
| + if (!memcg_css) |
| + goto out_free; |
| + |
| + isw->new_wb = wb_get_create(bdi, memcg_css, GFP_ATOMIC); |
| + css_put(memcg_css); |
| if (!isw->new_wb) |
| goto out_free; |
| |
| -- |
| 2.30.2 |
| |