Skip to content

Commit a565482

Browse files
w1ldptrdavem330
authored andcommitted
net: sched: protect chain template accesses with block lock
When cls API is called without protection of rtnl lock, parallel modification of chain is possible, which means that chain template can be changed concurrently in certain circumstances. For example, when chain is 'deleted' by new user-space chain API, the chain might continue to be used if it is referenced by actions, and can be 're-created' again by user. In such case same chain structure is reused and its template is changed. To protect from described scenario, cache chain template while holding block lock. Introduce standalone tc_chain_notify_delete() function that works with cached template values, instead of chains themselves. Signed-off-by: Vlad Buslov <vladbu@mellanox.com> Acked-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent bbf7383 commit a565482

File tree

1 file changed

+57
-16
lines changed

1 file changed

+57
-16
lines changed

net/sched/cls_api.c

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -371,14 +371,22 @@ struct tcf_chain *tcf_chain_get_by_act(struct tcf_block *block, u32 chain_index)
371371
}
372372
EXPORT_SYMBOL(tcf_chain_get_by_act);
373373

374-
static void tc_chain_tmplt_del(struct tcf_chain *chain);
374+
static void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops,
375+
void *tmplt_priv);
376+
static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops,
377+
void *tmplt_priv, u32 chain_index,
378+
struct tcf_block *block, struct sk_buff *oskb,
379+
u32 seq, u16 flags, bool unicast);
375380

376381
static void __tcf_chain_put(struct tcf_chain *chain, bool by_act,
377382
bool explicitly_created)
378383
{
379384
struct tcf_block *block = chain->block;
385+
const struct tcf_proto_ops *tmplt_ops;
380386
bool is_last, free_block = false;
381387
unsigned int refcnt;
388+
void *tmplt_priv;
389+
u32 chain_index;
382390

383391
mutex_lock(&block->lock);
384392
if (explicitly_created) {
@@ -398,16 +406,21 @@ static void __tcf_chain_put(struct tcf_chain *chain, bool by_act,
398406
*/
399407
refcnt = --chain->refcnt;
400408
is_last = refcnt - chain->action_refcnt == 0;
409+
tmplt_ops = chain->tmplt_ops;
410+
tmplt_priv = chain->tmplt_priv;
411+
chain_index = chain->index;
412+
401413
if (refcnt == 0)
402414
free_block = tcf_chain_detach(chain);
403415
mutex_unlock(&block->lock);
404416

405417
/* The last dropped non-action reference will trigger notification. */
406418
if (is_last && !by_act)
407-
tc_chain_notify(chain, NULL, 0, 0, RTM_DELCHAIN, false);
419+
tc_chain_notify_delete(tmplt_ops, tmplt_priv, chain_index,
420+
block, NULL, 0, 0, false);
408421

409422
if (refcnt == 0) {
410-
tc_chain_tmplt_del(chain);
423+
tc_chain_tmplt_del(tmplt_ops, tmplt_priv);
411424
tcf_chain_destroy(chain, free_block);
412425
}
413426
}
@@ -2155,8 +2168,10 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
21552168
return skb->len;
21562169
}
21572170

2158-
static int tc_chain_fill_node(struct tcf_chain *chain, struct net *net,
2159-
struct sk_buff *skb, struct tcf_block *block,
2171+
static int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops,
2172+
void *tmplt_priv, u32 chain_index,
2173+
struct net *net, struct sk_buff *skb,
2174+
struct tcf_block *block,
21602175
u32 portid, u32 seq, u16 flags, int event)
21612176
{
21622177
unsigned char *b = skb_tail_pointer(skb);
@@ -2165,8 +2180,8 @@ static int tc_chain_fill_node(struct tcf_chain *chain, struct net *net,
21652180
struct tcmsg *tcm;
21662181
void *priv;
21672182

2168-
ops = chain->tmplt_ops;
2169-
priv = chain->tmplt_priv;
2183+
ops = tmplt_ops;
2184+
priv = tmplt_priv;
21702185

21712186
nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
21722187
if (!nlh)
@@ -2184,7 +2199,7 @@ static int tc_chain_fill_node(struct tcf_chain *chain, struct net *net,
21842199
tcm->tcm_block_index = block->index;
21852200
}
21862201

2187-
if (nla_put_u32(skb, TCA_CHAIN, chain->index))
2202+
if (nla_put_u32(skb, TCA_CHAIN, chain_index))
21882203
goto nla_put_failure;
21892204

21902205
if (ops) {
@@ -2215,7 +2230,8 @@ static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
22152230
if (!skb)
22162231
return -ENOBUFS;
22172232

2218-
if (tc_chain_fill_node(chain, net, skb, block, portid,
2233+
if (tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv,
2234+
chain->index, net, skb, block, portid,
22192235
seq, flags, event) <= 0) {
22202236
kfree_skb(skb);
22212237
return -EINVAL;
@@ -2227,6 +2243,31 @@ static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
22272243
return rtnetlink_send(skb, net, portid, RTNLGRP_TC, flags & NLM_F_ECHO);
22282244
}
22292245

2246+
static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops,
2247+
void *tmplt_priv, u32 chain_index,
2248+
struct tcf_block *block, struct sk_buff *oskb,
2249+
u32 seq, u16 flags, bool unicast)
2250+
{
2251+
u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
2252+
struct net *net = block->net;
2253+
struct sk_buff *skb;
2254+
2255+
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
2256+
if (!skb)
2257+
return -ENOBUFS;
2258+
2259+
if (tc_chain_fill_node(tmplt_ops, tmplt_priv, chain_index, net, skb,
2260+
block, portid, seq, flags, RTM_DELCHAIN) <= 0) {
2261+
kfree_skb(skb);
2262+
return -EINVAL;
2263+
}
2264+
2265+
if (unicast)
2266+
return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT);
2267+
2268+
return rtnetlink_send(skb, net, portid, RTNLGRP_TC, flags & NLM_F_ECHO);
2269+
}
2270+
22302271
static int tc_chain_tmplt_add(struct tcf_chain *chain, struct net *net,
22312272
struct nlattr **tca,
22322273
struct netlink_ext_ack *extack)
@@ -2256,16 +2297,15 @@ static int tc_chain_tmplt_add(struct tcf_chain *chain, struct net *net,
22562297
return 0;
22572298
}
22582299

2259-
static void tc_chain_tmplt_del(struct tcf_chain *chain)
2300+
static void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops,
2301+
void *tmplt_priv)
22602302
{
2261-
const struct tcf_proto_ops *ops = chain->tmplt_ops;
2262-
22632303
/* If template ops are set, no work to do for us. */
2264-
if (!ops)
2304+
if (!tmplt_ops)
22652305
return;
22662306

2267-
ops->tmplt_destroy(chain->tmplt_priv);
2268-
module_put(ops->owner);
2307+
tmplt_ops->tmplt_destroy(tmplt_priv);
2308+
module_put(tmplt_ops->owner);
22692309
}
22702310

22712311
/* Add/delete/get a chain */
@@ -2486,7 +2526,8 @@ static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb)
24862526
index++;
24872527
continue;
24882528
}
2489-
err = tc_chain_fill_node(chain, net, skb, block,
2529+
err = tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv,
2530+
chain->index, net, skb, block,
24902531
NETLINK_CB(cb->skb).portid,
24912532
cb->nlh->nlmsg_seq, NLM_F_MULTI,
24922533
RTM_NEWCHAIN);

0 commit comments

Comments
 (0)