4.20-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cong Wang xiyou.wangcong@gmail.com
[ Upstream commit 8015d93ebd27484418d4952284fd02172fa4b0b2 ]
tcindex_destroy() invokes tcindex_destroy_element() via a walker to delete each filter result in its perfect hash table, and tcindex_destroy_element() calls tcindex_delete() which schedules tcf RCU works to do the final deletion work. Unfortunately this races with the RCU callback __tcindex_destroy(), which could lead to use-after-free as reported by Adrian.
Fix this by migrating this RCU callback to tcf RCU work too, as that workqueue is ordered, we will not have use-after-free.
Note, we don't need to hold netns refcnt because we don't call tcf_exts_destroy() here.
Fixes: 27ce4f05e2ab ("net_sched: use tcf_queue_work() in tcindex filter") Reported-by: Adrian bugs@abtelecom.ro Cc: Ben Hutchings ben@decadent.org.uk Cc: Jamal Hadi Salim jhs@mojatatu.com Cc: Jiri Pirko jiri@resnulli.us Signed-off-by: Cong Wang xiyou.wangcong@gmail.com Signed-off-by: David S. Miller davem@davemloft.net Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- net/sched/cls_tcindex.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-)
--- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -48,7 +48,7 @@ struct tcindex_data { u32 hash; /* hash table size; 0 if undefined */ u32 alloc_hash; /* allocated size */ u32 fall_through; /* 0: only classify if explicit match */ - struct rcu_head rcu; + struct rcu_work rwork; };
static inline int tcindex_filter_is_set(struct tcindex_filter_result *r) @@ -229,9 +229,11 @@ static int tcindex_destroy_element(struc return tcindex_delete(tp, arg, &last, NULL); }
-static void __tcindex_destroy(struct rcu_head *head) +static void tcindex_destroy_work(struct work_struct *work) { - struct tcindex_data *p = container_of(head, struct tcindex_data, rcu); + struct tcindex_data *p = container_of(to_rcu_work(work), + struct tcindex_data, + rwork);
kfree(p->perfect); kfree(p->h); @@ -258,9 +260,11 @@ static int tcindex_filter_result_init(st return tcf_exts_init(&r->exts, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE); }
-static void __tcindex_partial_destroy(struct rcu_head *head) +static void tcindex_partial_destroy_work(struct work_struct *work) { - struct tcindex_data *p = container_of(head, struct tcindex_data, rcu); + struct tcindex_data *p = container_of(to_rcu_work(work), + struct tcindex_data, + rwork);
kfree(p->perfect); kfree(p); @@ -478,7 +482,7 @@ tcindex_set_parms(struct net *net, struc }
if (oldp) - call_rcu(&oldp->rcu, __tcindex_partial_destroy); + tcf_queue_work(&oldp->rwork, tcindex_partial_destroy_work); return 0;
errout_alloc: @@ -570,7 +574,7 @@ static void tcindex_destroy(struct tcf_p walker.fn = tcindex_destroy_element; tcindex_walk(tp, &walker);
- call_rcu(&p->rcu, __tcindex_destroy); + tcf_queue_work(&p->rwork, tcindex_destroy_work); }