Skip to content

Commit ee3bbfe

Browse files
dcarattidavem330
authored andcommitted
net/sched: let actions use RCU to access 'goto_chain'
use RCU when accessing the action chain, to avoid use after free in the traffic path when 'goto chain' is replaced on existing TC actions (see script below). Since the control action is read in the traffic path without holding the action spinlock, we need to explicitly ensure that a->goto_chain is not NULL before dereferencing (i.e it's not sufficient to rely on the value of TC_ACT_GOTO_CHAIN bits). Not doing so caused NULL dereferences in tcf_action_goto_chain_exec() when the following script: # tc chain add dev dd0 chain 42 ingress protocol ip flower \ > ip_proto udp action pass index 4 # tc filter add dev dd0 ingress protocol ip flower \ > ip_proto udp action csum udp goto chain 42 index 66 # tc chain del dev dd0 chain 42 ingress (start UDP traffic towards dd0) # tc action replace action csum udp pass index 66 was run repeatedly for several hours. Suggested-by: Cong Wang <[email protected]> Suggested-by: Vlad Buslov <[email protected]> Signed-off-by: Davide Caratti <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent fe384e2 commit ee3bbfe

File tree

4 files changed

+13
-10
lines changed

4 files changed

+13
-10
lines changed

include/net/act_api.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ struct tc_action {
3939
struct gnet_stats_basic_cpu __percpu *cpu_bstats_hw;
4040
struct gnet_stats_queue __percpu *cpu_qstats;
4141
struct tc_cookie __rcu *act_cookie;
42-
struct tcf_chain *goto_chain;
42+
struct tcf_chain __rcu *goto_chain;
4343
};
4444
#define tcf_index common.tcfa_index
4545
#define tcf_refcnt common.tcfa_refcnt

include/net/sch_generic.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ struct tcf_chain {
378378
bool flushing;
379379
const struct tcf_proto_ops *tmplt_ops;
380380
void *tmplt_priv;
381+
struct rcu_head rcu;
381382
};
382383

383384
struct tcf_block {

net/sched/act_api.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
static void tcf_action_goto_chain_exec(const struct tc_action *a,
3232
struct tcf_result *res)
3333
{
34-
const struct tcf_chain *chain = a->goto_chain;
34+
const struct tcf_chain *chain = rcu_dereference_bh(a->goto_chain);
3535

3636
res->goto_tp = rcu_dereference_bh(chain->filter_chain);
3737
}
@@ -91,13 +91,11 @@ int tcf_action_check_ctrlact(int action, struct tcf_proto *tp,
9191
EXPORT_SYMBOL(tcf_action_check_ctrlact);
9292

9393
struct tcf_chain *tcf_action_set_ctrlact(struct tc_action *a, int action,
94-
struct tcf_chain *newchain)
94+
struct tcf_chain *goto_chain)
9595
{
96-
struct tcf_chain *oldchain = a->goto_chain;
97-
9896
a->tcfa_action = action;
99-
a->goto_chain = newchain;
100-
return oldchain;
97+
rcu_swap_protected(a->goto_chain, goto_chain, 1);
98+
return goto_chain;
10199
}
102100
EXPORT_SYMBOL(tcf_action_set_ctrlact);
103101

@@ -108,7 +106,7 @@ EXPORT_SYMBOL(tcf_action_set_ctrlact);
108106
*/
109107
static void free_tcf(struct tc_action *p)
110108
{
111-
struct tcf_chain *chain = p->goto_chain;
109+
struct tcf_chain *chain = rcu_dereference_protected(p->goto_chain, 1);
112110

113111
free_percpu(p->cpu_bstats);
114112
free_percpu(p->cpu_bstats_hw);
@@ -686,6 +684,10 @@ int tcf_action_exec(struct sk_buff *skb, struct tc_action **actions,
686684
return TC_ACT_OK;
687685
}
688686
} else if (TC_ACT_EXT_CMP(ret, TC_ACT_GOTO_CHAIN)) {
687+
if (unlikely(!rcu_access_pointer(a->goto_chain))) {
688+
net_warn_ratelimited("can't go to NULL chain!\n");
689+
return TC_ACT_SHOT;
690+
}
689691
tcf_action_goto_chain_exec(a, res);
690692
}
691693

@@ -931,7 +933,7 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
931933
module_put(a_o->owner);
932934

933935
if (TC_ACT_EXT_CMP(a->tcfa_action, TC_ACT_GOTO_CHAIN) &&
934-
!a->goto_chain) {
936+
!rcu_access_pointer(a->goto_chain)) {
935937
tcf_action_destroy_1(a, bind);
936938
NL_SET_ERR_MSG(extack, "can't use goto chain with NULL chain");
937939
return ERR_PTR(-EINVAL);

net/sched/cls_api.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ static void tcf_chain_destroy(struct tcf_chain *chain, bool free_block)
367367
struct tcf_block *block = chain->block;
368368

369369
mutex_destroy(&chain->filter_chain_lock);
370-
kfree(chain);
370+
kfree_rcu(chain, rcu);
371371
if (free_block)
372372
tcf_block_destroy(block);
373373
}

0 commit comments

Comments
 (0)