diff options
author | Cong Wang <xiyou.wangcong@gmail.com> | 2019-02-16 10:58:27 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-02-20 20:11:10 -0800 |
commit | 51dcb69de67a5141a4f39b03402b2c5160e91040 (patch) | |
tree | 75927f0a5585b3610cd09d91c211fb85a5701b7b /net/sched/cls_tcindex.c | |
parent | 3d210534cc93dab39a9d1a8d674aa2872c268d38 (diff) | |
download | linux-stable-51dcb69de67a5141a4f39b03402b2c5160e91040.tar.gz linux-stable-51dcb69de67a5141a4f39b03402b2c5160e91040.tar.bz2 linux-stable-51dcb69de67a5141a4f39b03402b2c5160e91040.zip |
net_sched: fix a memory leak in cls_tcindex
(cherry picked from commit 033b228e7f26b29ae37f8bfa1bc6b209a5365e9f)
When tcindex_destroy() destroys all the filter results in
the perfect hash table, it invokes the walker to delete
each of them. However, results with class==0 are skipped
in either tcindex_walk() or tcindex_delete(), which causes
a memory leak reported by kmemleak.
This patch fixes it by skipping the walker and directly
deleting these filter results so we don't miss any filter
result.
As a result of this change, we have to initialize exts->net
properly in tcindex_alloc_perfect_hash(). For net-next, we
need to consider whether we should initialize ->net in
tcf_exts_init() instead, before that just directly test
CONFIG_NET_CLS_ACT=y.
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>
Diffstat (limited to 'net/sched/cls_tcindex.c')
-rw-r--r-- | net/sched/cls_tcindex.c | 37 |
1 files changed, 24 insertions, 13 deletions
diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index 81a433ae31b3..fbf3519a12d8 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -221,14 +221,6 @@ found: return 0; } -static int tcindex_destroy_element(struct tcf_proto *tp, - void *arg, struct tcf_walker *walker) -{ - bool last; - - return tcindex_delete(tp, arg, &last, false, NULL); -} - static void tcindex_destroy_work(struct work_struct *work) { struct tcindex_data *p = container_of(to_rcu_work(work), @@ -568,13 +560,32 @@ static void tcindex_destroy(struct tcf_proto *tp, bool rtnl_held, struct netlink_ext_ack *extack) { struct tcindex_data *p = rtnl_dereference(tp->root); - struct tcf_walker walker; + int i; pr_debug("tcindex_destroy(tp %p),p %p\n", tp, p); - walker.count = 0; - walker.skip = 0; - walker.fn = tcindex_destroy_element; - tcindex_walk(tp, &walker, true); + + if (p->perfect) { + for (i = 0; i < p->hash; i++) { + struct tcindex_filter_result *r = p->perfect + i; + + tcf_unbind_filter(tp, &r->res); + if (tcf_exts_get_net(&r->exts)) + tcf_queue_work(&r->rwork, + tcindex_destroy_rexts_work); + else + __tcindex_destroy_rexts(r); + } + } + + for (i = 0; p->h && i < p->hash; i++) { + struct tcindex_filter *f, *next; + bool last; + + for (f = rtnl_dereference(p->h[i]); f; f = next) { + next = rtnl_dereference(f->next); + tcindex_delete(tp, &f->result, &last, rtnl_held, NULL); + } + } tcf_queue_work(&p->rwork, tcindex_destroy_work); } |