Commit 4e8ddd7f authored by Vlad Buslov's avatar Vlad Buslov Committed by David S. Miller

net: sched: don't release reference on action overwrite

Return from action init function with reference to action taken,
even when overwriting existing action.

Action init API initializes its fourth argument (pointer to pointer to tc
action) to either existing action with same index or newly created action.
In case of existing index(and bind argument is zero), init function returns
without incrementing action reference counter. Caller of action init then
proceeds working with action, without actually holding reference to it.
This means that action could be deleted concurrently.

Change action init behavior to always take reference to action before
returning successfully, in order to protect from concurrent deletion.
Reviewed-by: default avatarMarcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: default avatarVlad Buslov <vladbu@mellanox.com>
Signed-off-by: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 16af6067
...@@ -870,8 +870,6 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla, ...@@ -870,8 +870,6 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
} }
act->order = i; act->order = i;
sz += tcf_action_fill_size(act); sz += tcf_action_fill_size(act);
if (ovr)
refcount_inc(&act->tcfa_refcnt);
list_add_tail(&act->list, actions); list_add_tail(&act->list, actions);
} }
......
...@@ -311,9 +311,10 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla, ...@@ -311,9 +311,10 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
if (bind) if (bind)
return 0; return 0;
tcf_idr_release(*act, bind); if (!replace) {
if (!replace) tcf_idr_release(*act, bind);
return -EEXIST; return -EEXIST;
}
} }
is_bpf = tb[TCA_ACT_BPF_OPS_LEN] && tb[TCA_ACT_BPF_OPS]; is_bpf = tb[TCA_ACT_BPF_OPS_LEN] && tb[TCA_ACT_BPF_OPS];
...@@ -356,8 +357,7 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla, ...@@ -356,8 +357,7 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
return res; return res;
out: out:
if (res == ACT_P_CREATED) tcf_idr_release(*act, bind);
tcf_idr_release(*act, bind);
return ret; return ret;
} }
......
...@@ -135,9 +135,10 @@ static int tcf_connmark_init(struct net *net, struct nlattr *nla, ...@@ -135,9 +135,10 @@ static int tcf_connmark_init(struct net *net, struct nlattr *nla,
ci = to_connmark(*a); ci = to_connmark(*a);
if (bind) if (bind)
return 0; return 0;
tcf_idr_release(*a, bind); if (!ovr) {
if (!ovr) tcf_idr_release(*a, bind);
return -EEXIST; return -EEXIST;
}
/* replacing action and zone */ /* replacing action and zone */
ci->tcf_action = parm->action; ci->tcf_action = parm->action;
ci->zone = parm->zone; ci->zone = parm->zone;
......
...@@ -76,9 +76,10 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla, ...@@ -76,9 +76,10 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla,
} else { } else {
if (bind)/* dont override defaults */ if (bind)/* dont override defaults */
return 0; return 0;
tcf_idr_release(*a, bind); if (!ovr) {
if (!ovr) tcf_idr_release(*a, bind);
return -EEXIST; return -EEXIST;
}
} }
p = to_tcf_csum(*a); p = to_tcf_csum(*a);
...@@ -86,8 +87,7 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla, ...@@ -86,8 +87,7 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla,
params_new = kzalloc(sizeof(*params_new), GFP_KERNEL); params_new = kzalloc(sizeof(*params_new), GFP_KERNEL);
if (unlikely(!params_new)) { if (unlikely(!params_new)) {
if (ret == ACT_P_CREATED) tcf_idr_release(*a, bind);
tcf_idr_release(*a, bind);
return -ENOMEM; return -ENOMEM;
} }
params_old = rtnl_dereference(p->params); params_old = rtnl_dereference(p->params);
......
...@@ -100,9 +100,10 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla, ...@@ -100,9 +100,10 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla,
} else { } else {
if (bind)/* dont override defaults */ if (bind)/* dont override defaults */
return 0; return 0;
tcf_idr_release(*a, bind); if (!ovr) {
if (!ovr) tcf_idr_release(*a, bind);
return -EEXIST; return -EEXIST;
}
} }
gact = to_gact(*a); gact = to_gact(*a);
......
...@@ -498,12 +498,10 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla, ...@@ -498,12 +498,10 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
return ret; return ret;
} }
ret = ACT_P_CREATED; ret = ACT_P_CREATED;
} else { } else if (!ovr) {
tcf_idr_release(*a, bind); tcf_idr_release(*a, bind);
if (!ovr) { kfree(p);
kfree(p); return -EEXIST;
return -EEXIST;
}
} }
ife = to_ife(*a); ife = to_ife(*a);
...@@ -548,6 +546,8 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla, ...@@ -548,6 +546,8 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
if (exists) if (exists)
spin_unlock_bh(&ife->tcf_lock); spin_unlock_bh(&ife->tcf_lock);
tcf_idr_release(*a, bind);
kfree(p); kfree(p);
return err; return err;
} }
......
...@@ -145,10 +145,11 @@ static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla, ...@@ -145,10 +145,11 @@ static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla,
} else { } else {
if (bind)/* dont override defaults */ if (bind)/* dont override defaults */
return 0; return 0;
tcf_idr_release(*a, bind);
if (!ovr) if (!ovr) {
tcf_idr_release(*a, bind);
return -EEXIST; return -EEXIST;
}
} }
hook = nla_get_u32(tb[TCA_IPT_HOOK]); hook = nla_get_u32(tb[TCA_IPT_HOOK]);
......
...@@ -132,10 +132,9 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, ...@@ -132,10 +132,9 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
if (ret) if (ret)
return ret; return ret;
ret = ACT_P_CREATED; ret = ACT_P_CREATED;
} else { } else if (!ovr) {
tcf_idr_release(*a, bind); tcf_idr_release(*a, bind);
if (!ovr) return -EEXIST;
return -EEXIST;
} }
m = to_mirred(*a); m = to_mirred(*a);
......
...@@ -66,9 +66,10 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est, ...@@ -66,9 +66,10 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est,
} else { } else {
if (bind) if (bind)
return 0; return 0;
tcf_idr_release(*a, bind); if (!ovr) {
if (!ovr) tcf_idr_release(*a, bind);
return -EEXIST; return -EEXIST;
}
} }
p = to_tcf_nat(*a); p = to_tcf_nat(*a);
......
...@@ -194,8 +194,8 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, ...@@ -194,8 +194,8 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
} else { } else {
if (bind) if (bind)
goto out_free; goto out_free;
tcf_idr_release(*a, bind);
if (!ovr) { if (!ovr) {
tcf_idr_release(*a, bind);
ret = -EEXIST; ret = -EEXIST;
goto out_free; goto out_free;
} }
......
...@@ -111,10 +111,9 @@ static int tcf_act_police_init(struct net *net, struct nlattr *nla, ...@@ -111,10 +111,9 @@ static int tcf_act_police_init(struct net *net, struct nlattr *nla,
if (ret) if (ret)
return ret; return ret;
ret = ACT_P_CREATED; ret = ACT_P_CREATED;
} else { } else if (!ovr) {
tcf_idr_release(*a, bind); tcf_idr_release(*a, bind);
if (!ovr) return -EEXIST;
return -EEXIST;
} }
police = to_police(*a); police = to_police(*a);
...@@ -195,8 +194,7 @@ static int tcf_act_police_init(struct net *net, struct nlattr *nla, ...@@ -195,8 +194,7 @@ static int tcf_act_police_init(struct net *net, struct nlattr *nla,
failure: failure:
qdisc_put_rtab(P_tab); qdisc_put_rtab(P_tab);
qdisc_put_rtab(R_tab); qdisc_put_rtab(R_tab);
if (ret == ACT_P_CREATED) tcf_idr_release(*a, bind);
tcf_idr_release(*a, bind);
return err; return err;
} }
......
...@@ -69,10 +69,9 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla, ...@@ -69,10 +69,9 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla,
if (ret) if (ret)
return ret; return ret;
ret = ACT_P_CREATED; ret = ACT_P_CREATED;
} else { } else if (!ovr) {
tcf_idr_release(*a, bind); tcf_idr_release(*a, bind);
if (!ovr) return -EEXIST;
return -EEXIST;
} }
s = to_sample(*a); s = to_sample(*a);
...@@ -81,8 +80,7 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla, ...@@ -81,8 +80,7 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla,
s->psample_group_num = nla_get_u32(tb[TCA_SAMPLE_PSAMPLE_GROUP]); s->psample_group_num = nla_get_u32(tb[TCA_SAMPLE_PSAMPLE_GROUP]);
psample_group = psample_group_get(net, s->psample_group_num); psample_group = psample_group_get(net, s->psample_group_num);
if (!psample_group) { if (!psample_group) {
if (ret == ACT_P_CREATED) tcf_idr_release(*a, bind);
tcf_idr_release(*a, bind);
return -ENOMEM; return -ENOMEM;
} }
RCU_INIT_POINTER(s->psample_group, psample_group); RCU_INIT_POINTER(s->psample_group, psample_group);
......
...@@ -127,9 +127,10 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla, ...@@ -127,9 +127,10 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla,
} else { } else {
d = to_defact(*a); d = to_defact(*a);
tcf_idr_release(*a, bind); if (!ovr) {
if (!ovr) tcf_idr_release(*a, bind);
return -EEXIST; return -EEXIST;
}
reset_policy(d, tb[TCA_DEF_DATA], parm); reset_policy(d, tb[TCA_DEF_DATA], parm);
} }
......
...@@ -172,9 +172,10 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla, ...@@ -172,9 +172,10 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
ret = ACT_P_CREATED; ret = ACT_P_CREATED;
} else { } else {
d = to_skbedit(*a); d = to_skbedit(*a);
tcf_idr_release(*a, bind); if (!ovr) {
if (!ovr) tcf_idr_release(*a, bind);
return -EEXIST; return -EEXIST;
}
} }
spin_lock_bh(&d->tcf_lock); spin_lock_bh(&d->tcf_lock);
......
...@@ -145,10 +145,9 @@ static int tcf_skbmod_init(struct net *net, struct nlattr *nla, ...@@ -145,10 +145,9 @@ static int tcf_skbmod_init(struct net *net, struct nlattr *nla,
return ret; return ret;
ret = ACT_P_CREATED; ret = ACT_P_CREATED;
} else { } else if (!ovr) {
tcf_idr_release(*a, bind); tcf_idr_release(*a, bind);
if (!ovr) return -EEXIST;
return -EEXIST;
} }
d = to_skbmod(*a); d = to_skbmod(*a);
...@@ -156,8 +155,7 @@ static int tcf_skbmod_init(struct net *net, struct nlattr *nla, ...@@ -156,8 +155,7 @@ static int tcf_skbmod_init(struct net *net, struct nlattr *nla,
ASSERT_RTNL(); ASSERT_RTNL();
p = kzalloc(sizeof(struct tcf_skbmod_params), GFP_KERNEL); p = kzalloc(sizeof(struct tcf_skbmod_params), GFP_KERNEL);
if (unlikely(!p)) { if (unlikely(!p)) {
if (ret == ACT_P_CREATED) tcf_idr_release(*a, bind);
tcf_idr_release(*a, bind);
return -ENOMEM; return -ENOMEM;
} }
......
...@@ -329,12 +329,10 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, ...@@ -329,12 +329,10 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla,
} }
ret = ACT_P_CREATED; ret = ACT_P_CREATED;
} else { } else if (!ovr) {
tcf_idr_release(*a, bind); tcf_idr_release(*a, bind);
if (!ovr) { NL_SET_ERR_MSG(extack, "TC IDR already exists");
NL_SET_ERR_MSG(extack, "TC IDR already exists"); return -EEXIST;
return -EEXIST;
}
} }
t = to_tunnel_key(*a); t = to_tunnel_key(*a);
...@@ -342,8 +340,7 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, ...@@ -342,8 +340,7 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla,
ASSERT_RTNL(); ASSERT_RTNL();
params_new = kzalloc(sizeof(*params_new), GFP_KERNEL); params_new = kzalloc(sizeof(*params_new), GFP_KERNEL);
if (unlikely(!params_new)) { if (unlikely(!params_new)) {
if (ret == ACT_P_CREATED) tcf_idr_release(*a, bind);
tcf_idr_release(*a, bind);
NL_SET_ERR_MSG(extack, "Cannot allocate tunnel key parameters"); NL_SET_ERR_MSG(extack, "Cannot allocate tunnel key parameters");
return -ENOMEM; return -ENOMEM;
} }
......
...@@ -187,10 +187,9 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla, ...@@ -187,10 +187,9 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla,
return ret; return ret;
ret = ACT_P_CREATED; ret = ACT_P_CREATED;
} else { } else if (!ovr) {
tcf_idr_release(*a, bind); tcf_idr_release(*a, bind);
if (!ovr) return -EEXIST;
return -EEXIST;
} }
v = to_vlan(*a); v = to_vlan(*a);
...@@ -198,8 +197,7 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla, ...@@ -198,8 +197,7 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla,
ASSERT_RTNL(); ASSERT_RTNL();
p = kzalloc(sizeof(*p), GFP_KERNEL); p = kzalloc(sizeof(*p), GFP_KERNEL);
if (!p) { if (!p) {
if (ret == ACT_P_CREATED) tcf_idr_release(*a, bind);
tcf_idr_release(*a, bind);
return -ENOMEM; return -ENOMEM;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment