Skip to content

Commit

Permalink
net: sched: don't release reference on action overwrite
Browse files Browse the repository at this point in the history
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: Marcelo Ricardo Leitner <[email protected]>
Signed-off-by: Vlad Buslov <[email protected]>
Signed-off-by: Jiri Pirko <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
w1ldptr authored and davem330 committed Jul 8, 2018
1 parent 16af606 commit 4e8ddd7
Show file tree
Hide file tree
Showing 17 changed files with 50 additions and 58 deletions.
2 changes: 0 additions & 2 deletions net/sched/act_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -870,8 +870,6 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
}
act->order = i;
sz += tcf_action_fill_size(act);
if (ovr)
refcount_inc(&act->tcfa_refcnt);
list_add_tail(&act->list, actions);
}

Expand Down
8 changes: 4 additions & 4 deletions net/sched/act_bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,10 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
if (bind)
return 0;

tcf_idr_release(*act, bind);
if (!replace)
if (!replace) {
tcf_idr_release(*act, bind);
return -EEXIST;
}
}

is_bpf = tb[TCA_ACT_BPF_OPS_LEN] && tb[TCA_ACT_BPF_OPS];
Expand Down Expand Up @@ -356,8 +357,7 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,

return res;
out:
if (res == ACT_P_CREATED)
tcf_idr_release(*act, bind);
tcf_idr_release(*act, bind);

return ret;
}
Expand Down
5 changes: 3 additions & 2 deletions net/sched/act_connmark.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,10 @@ static int tcf_connmark_init(struct net *net, struct nlattr *nla,
ci = to_connmark(*a);
if (bind)
return 0;
tcf_idr_release(*a, bind);
if (!ovr)
if (!ovr) {
tcf_idr_release(*a, bind);
return -EEXIST;
}
/* replacing action and zone */
ci->tcf_action = parm->action;
ci->zone = parm->zone;
Expand Down
8 changes: 4 additions & 4 deletions net/sched/act_csum.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,18 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla,
} else {
if (bind)/* dont override defaults */
return 0;
tcf_idr_release(*a, bind);
if (!ovr)
if (!ovr) {
tcf_idr_release(*a, bind);
return -EEXIST;
}
}

p = to_tcf_csum(*a);
ASSERT_RTNL();

params_new = kzalloc(sizeof(*params_new), GFP_KERNEL);
if (unlikely(!params_new)) {
if (ret == ACT_P_CREATED)
tcf_idr_release(*a, bind);
tcf_idr_release(*a, bind);
return -ENOMEM;
}
params_old = rtnl_dereference(p->params);
Expand Down
5 changes: 3 additions & 2 deletions net/sched/act_gact.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,10 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla,
} else {
if (bind)/* dont override defaults */
return 0;
tcf_idr_release(*a, bind);
if (!ovr)
if (!ovr) {
tcf_idr_release(*a, bind);
return -EEXIST;
}
}

gact = to_gact(*a);
Expand Down
10 changes: 5 additions & 5 deletions net/sched/act_ife.c
Original file line number Diff line number Diff line change
Expand Up @@ -498,12 +498,10 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
return ret;
}
ret = ACT_P_CREATED;
} else {
} else if (!ovr) {
tcf_idr_release(*a, bind);
if (!ovr) {
kfree(p);
return -EEXIST;
}
kfree(p);
return -EEXIST;
}

ife = to_ife(*a);
Expand Down Expand Up @@ -548,6 +546,8 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,

if (exists)
spin_unlock_bh(&ife->tcf_lock);
tcf_idr_release(*a, bind);

kfree(p);
return err;
}
Expand Down
5 changes: 3 additions & 2 deletions net/sched/act_ipt.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,11 @@ static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla,
} else {
if (bind)/* dont override defaults */
return 0;
tcf_idr_release(*a, bind);

if (!ovr)
if (!ovr) {
tcf_idr_release(*a, bind);
return -EEXIST;
}
}
hook = nla_get_u32(tb[TCA_IPT_HOOK]);

Expand Down
5 changes: 2 additions & 3 deletions net/sched/act_mirred.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,9 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla,
if (ret)
return ret;
ret = ACT_P_CREATED;
} else {
} else if (!ovr) {
tcf_idr_release(*a, bind);
if (!ovr)
return -EEXIST;
return -EEXIST;
}
m = to_mirred(*a);

Expand Down
5 changes: 3 additions & 2 deletions net/sched/act_nat.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est,
} else {
if (bind)
return 0;
tcf_idr_release(*a, bind);
if (!ovr)
if (!ovr) {
tcf_idr_release(*a, bind);
return -EEXIST;
}
}
p = to_tcf_nat(*a);

Expand Down
2 changes: 1 addition & 1 deletion net/sched/act_pedit.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,8 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
} else {
if (bind)
goto out_free;
tcf_idr_release(*a, bind);
if (!ovr) {
tcf_idr_release(*a, bind);
ret = -EEXIST;
goto out_free;
}
Expand Down
8 changes: 3 additions & 5 deletions net/sched/act_police.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,9 @@ static int tcf_act_police_init(struct net *net, struct nlattr *nla,
if (ret)
return ret;
ret = ACT_P_CREATED;
} else {
} else if (!ovr) {
tcf_idr_release(*a, bind);
if (!ovr)
return -EEXIST;
return -EEXIST;
}

police = to_police(*a);
Expand Down Expand Up @@ -195,8 +194,7 @@ static int tcf_act_police_init(struct net *net, struct nlattr *nla,
failure:
qdisc_put_rtab(P_tab);
qdisc_put_rtab(R_tab);
if (ret == ACT_P_CREATED)
tcf_idr_release(*a, bind);
tcf_idr_release(*a, bind);
return err;
}

Expand Down
8 changes: 3 additions & 5 deletions net/sched/act_sample.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,9 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla,
if (ret)
return ret;
ret = ACT_P_CREATED;
} else {
} else if (!ovr) {
tcf_idr_release(*a, bind);
if (!ovr)
return -EEXIST;
return -EEXIST;
}
s = to_sample(*a);

Expand All @@ -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]);
psample_group = psample_group_get(net, s->psample_group_num);
if (!psample_group) {
if (ret == ACT_P_CREATED)
tcf_idr_release(*a, bind);
tcf_idr_release(*a, bind);
return -ENOMEM;
}
RCU_INIT_POINTER(s->psample_group, psample_group);
Expand Down
5 changes: 3 additions & 2 deletions net/sched/act_simple.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,10 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla,
} else {
d = to_defact(*a);

tcf_idr_release(*a, bind);
if (!ovr)
if (!ovr) {
tcf_idr_release(*a, bind);
return -EEXIST;
}

reset_policy(d, tb[TCA_DEF_DATA], parm);
}
Expand Down
5 changes: 3 additions & 2 deletions net/sched/act_skbedit.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,10 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
ret = ACT_P_CREATED;
} else {
d = to_skbedit(*a);
tcf_idr_release(*a, bind);
if (!ovr)
if (!ovr) {
tcf_idr_release(*a, bind);
return -EEXIST;
}
}

spin_lock_bh(&d->tcf_lock);
Expand Down
8 changes: 3 additions & 5 deletions net/sched/act_skbmod.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,19 +145,17 @@ static int tcf_skbmod_init(struct net *net, struct nlattr *nla,
return ret;

ret = ACT_P_CREATED;
} else {
} else if (!ovr) {
tcf_idr_release(*a, bind);
if (!ovr)
return -EEXIST;
return -EEXIST;
}

d = to_skbmod(*a);

ASSERT_RTNL();
p = kzalloc(sizeof(struct tcf_skbmod_params), GFP_KERNEL);
if (unlikely(!p)) {
if (ret == ACT_P_CREATED)
tcf_idr_release(*a, bind);
tcf_idr_release(*a, bind);
return -ENOMEM;
}

Expand Down
11 changes: 4 additions & 7 deletions net/sched/act_tunnel_key.c
Original file line number Diff line number Diff line change
Expand Up @@ -329,21 +329,18 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla,
}

ret = ACT_P_CREATED;
} else {
} else if (!ovr) {
tcf_idr_release(*a, bind);
if (!ovr) {
NL_SET_ERR_MSG(extack, "TC IDR already exists");
return -EEXIST;
}
NL_SET_ERR_MSG(extack, "TC IDR already exists");
return -EEXIST;
}

t = to_tunnel_key(*a);

ASSERT_RTNL();
params_new = kzalloc(sizeof(*params_new), GFP_KERNEL);
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");
return -ENOMEM;
}
Expand Down
8 changes: 3 additions & 5 deletions net/sched/act_vlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,19 +187,17 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla,
return ret;

ret = ACT_P_CREATED;
} else {
} else if (!ovr) {
tcf_idr_release(*a, bind);
if (!ovr)
return -EEXIST;
return -EEXIST;
}

v = to_vlan(*a);

ASSERT_RTNL();
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (!p) {
if (ret == ACT_P_CREATED)
tcf_idr_release(*a, bind);
tcf_idr_release(*a, bind);
return -ENOMEM;
}

Expand Down

0 comments on commit 4e8ddd7

Please sign in to comment.