diff options
author | Jiri Pirko <jiri@mellanox.com> | 2017-09-13 18:32:37 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-09-13 19:34:08 +0300 |
commit | 255cd50f207ae8ec7b22663246c833407744e634 (patch) | |
tree | 8c98c50e192e780bfa9d845b4c7e416cb3ae9d3a /net/sched/act_api.c | |
parent | 822f8565c93949fb2d31502d595c8bc45629c9b7 (diff) | |
download | linux-255cd50f207ae8ec7b22663246c833407744e634.tar.xz |
net: sched: fix use-after-free in tcf_action_destroy and tcf_del_walker
Recent commit d7fb60b9cafb ("net_sched: get rid of tcfa_rcu") removed
freeing in call_rcu, which changed already existing hard-to-hit
race condition into 100% hit:
[ 598.599825] BUG: unable to handle kernel NULL pointer dereference at 0000000000000030
[ 598.607782] IP: tcf_action_destroy+0xc0/0x140
Or:
[ 40.858924] BUG: unable to handle kernel NULL pointer dereference at 0000000000000030
[ 40.862840] IP: tcf_generic_walker+0x534/0x820
Fix this by storing the ops and use them directly for module_put call.
Fixes: a85a970af265 ("net_sched: move tc_action into tcf_common")
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sched/act_api.c')
-rw-r--r-- | net/sched/act_api.c | 6 |
1 files changed, 4 insertions, 2 deletions
diff --git a/net/sched/act_api.c b/net/sched/act_api.c index fcd7dc7b807a..da6fa82c98a8 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -180,7 +180,7 @@ static int tcf_del_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb, idr_for_each_entry_ext(idr, p, id) { ret = __tcf_idr_release(p, false, true); if (ret == ACT_P_DELETED) { - module_put(p->ops->owner); + module_put(ops->owner); n_i++; } else if (ret < 0) { goto nla_put_failure; @@ -514,13 +514,15 @@ EXPORT_SYMBOL(tcf_action_exec); int tcf_action_destroy(struct list_head *actions, int bind) { + const struct tc_action_ops *ops; struct tc_action *a, *tmp; int ret = 0; list_for_each_entry_safe(a, tmp, actions, list) { + ops = a->ops; ret = __tcf_idr_release(a, bind, true); if (ret == ACT_P_DELETED) - module_put(a->ops->owner); + module_put(ops->owner); else if (ret < 0) return ret; } |