Skip to content

Commit 87c750e

Browse files
w1ldptrdavem330
authored andcommitted
net: sched: fix action overwrite reference counting
Action init code increments reference counter when it changes an action. This is the desired behavior for cls API which needs to obtain action reference for every classifier that points to action. However, act API just needs to change the action and releases the reference before returning. This sequence breaks when the requested action doesn't exist, which causes act API init code to create new action with specified index, but action is still released before returning and is deleted (unless it was referenced concurrently by cls API). Reproduction: $ sudo tc actions ls action gact $ sudo tc actions change action gact drop index 1 $ sudo tc actions ls action gact Extend tcf_action_init() to accept 'init_res' array and initialize it with action->ops->init() result. In tcf_action_add() remove pointers to created actions from actions array before passing it to tcf_action_put_many(). Fixes: cae422f ("net: sched: use reference counting action init") Reported-by: Kumar Kartikeya Dwivedi <[email protected]> Signed-off-by: Vlad Buslov <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 4ba8612 commit 87c750e

File tree

3 files changed

+23
-13
lines changed

3 files changed

+23
-13
lines changed

include/net/act_api.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,15 +185,16 @@ int tcf_action_exec(struct sk_buff *skb, struct tc_action **actions,
185185
int nr_actions, struct tcf_result *res);
186186
int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
187187
struct nlattr *est, char *name, int ovr, int bind,
188-
struct tc_action *actions[], size_t *attr_size,
188+
struct tc_action *actions[], int init_res[], size_t *attr_size,
189189
bool rtnl_held, struct netlink_ext_ack *extack);
190190
struct tc_action_ops *tc_action_load_ops(char *name, struct nlattr *nla,
191191
bool rtnl_held,
192192
struct netlink_ext_ack *extack);
193193
struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
194194
struct nlattr *nla, struct nlattr *est,
195195
char *name, int ovr, int bind,
196-
struct tc_action_ops *ops, bool rtnl_held,
196+
struct tc_action_ops *a_o, int *init_res,
197+
bool rtnl_held,
197198
struct netlink_ext_ack *extack);
198199
int tcf_action_dump(struct sk_buff *skb, struct tc_action *actions[], int bind,
199200
int ref, bool terse);

net/sched/act_api.c

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,8 @@ struct tc_action_ops *tc_action_load_ops(char *name, struct nlattr *nla,
992992
struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
993993
struct nlattr *nla, struct nlattr *est,
994994
char *name, int ovr, int bind,
995-
struct tc_action_ops *a_o, bool rtnl_held,
995+
struct tc_action_ops *a_o, int *init_res,
996+
bool rtnl_held,
996997
struct netlink_ext_ack *extack)
997998
{
998999
struct nla_bitfield32 flags = { 0, 0 };
@@ -1028,6 +1029,7 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
10281029
}
10291030
if (err < 0)
10301031
goto err_out;
1032+
*init_res = err;
10311033

10321034
if (!name && tb[TCA_ACT_COOKIE])
10331035
tcf_set_action_cookie(&a->act_cookie, cookie);
@@ -1056,7 +1058,7 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
10561058

10571059
int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
10581060
struct nlattr *est, char *name, int ovr, int bind,
1059-
struct tc_action *actions[], size_t *attr_size,
1061+
struct tc_action *actions[], int init_res[], size_t *attr_size,
10601062
bool rtnl_held, struct netlink_ext_ack *extack)
10611063
{
10621064
struct tc_action_ops *ops[TCA_ACT_MAX_PRIO] = {};
@@ -1084,7 +1086,8 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
10841086

10851087
for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
10861088
act = tcf_action_init_1(net, tp, tb[i], est, name, ovr, bind,
1087-
ops[i - 1], rtnl_held, extack);
1089+
ops[i - 1], &init_res[i - 1], rtnl_held,
1090+
extack);
10881091
if (IS_ERR(act)) {
10891092
err = PTR_ERR(act);
10901093
goto err;
@@ -1497,21 +1500,26 @@ static int tcf_action_add(struct net *net, struct nlattr *nla,
14971500
struct netlink_ext_ack *extack)
14981501
{
14991502
size_t attr_size = 0;
1500-
int loop, ret;
1503+
int loop, ret, i;
15011504
struct tc_action *actions[TCA_ACT_MAX_PRIO] = {};
1505+
int init_res[TCA_ACT_MAX_PRIO] = {};
15021506

15031507
for (loop = 0; loop < 10; loop++) {
15041508
ret = tcf_action_init(net, NULL, nla, NULL, NULL, ovr, 0,
1505-
actions, &attr_size, true, extack);
1509+
actions, init_res, &attr_size, true, extack);
15061510
if (ret != -EAGAIN)
15071511
break;
15081512
}
15091513

15101514
if (ret < 0)
15111515
return ret;
15121516
ret = tcf_add_notify(net, n, actions, portid, attr_size, extack);
1513-
if (ovr)
1514-
tcf_action_put_many(actions);
1517+
1518+
/* only put existing actions */
1519+
for (i = 0; i < TCA_ACT_MAX_PRIO; i++)
1520+
if (init_res[i] == ACT_P_CREATED)
1521+
actions[i] = NULL;
1522+
tcf_action_put_many(actions);
15151523

15161524
return ret;
15171525
}

net/sched/cls_api.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3040,6 +3040,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
30403040
{
30413041
#ifdef CONFIG_NET_CLS_ACT
30423042
{
3043+
int init_res[TCA_ACT_MAX_PRIO] = {};
30433044
struct tc_action *act;
30443045
size_t attr_size = 0;
30453046

@@ -3051,8 +3052,8 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
30513052
return PTR_ERR(a_o);
30523053
act = tcf_action_init_1(net, tp, tb[exts->police],
30533054
rate_tlv, "police", ovr,
3054-
TCA_ACT_BIND, a_o, rtnl_held,
3055-
extack);
3055+
TCA_ACT_BIND, a_o, init_res,
3056+
rtnl_held, extack);
30563057
if (IS_ERR(act)) {
30573058
module_put(a_o->owner);
30583059
return PTR_ERR(act);
@@ -3067,8 +3068,8 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
30673068

30683069
err = tcf_action_init(net, tp, tb[exts->action],
30693070
rate_tlv, NULL, ovr, TCA_ACT_BIND,
3070-
exts->actions, &attr_size,
3071-
rtnl_held, extack);
3071+
exts->actions, init_res,
3072+
&attr_size, rtnl_held, extack);
30723073
if (err < 0)
30733074
return err;
30743075
exts->nr_actions = err;

0 commit comments

Comments
 (0)