Skip to content

Commit e2cedd4

Browse files
jhsmtkuba-moo
authored andcommitted
net/sched: act_ife: Fix metalist update behavior
Whenever an ife action replace changes the metalist, instead of replacing the old data on the metalist, the current ife code is appending the new metadata. Aside from being innapropriate behavior, this may lead to an unbounded addition of metadata to the metalist which might cause an out of bounds error when running the encode op: [ 138.423369][ C1] ================================================================== [ 138.424317][ C1] BUG: KASAN: slab-out-of-bounds in ife_tlv_meta_encode (net/ife/ife.c:168) [ 138.424906][ C1] Write of size 4 at addr ffff8880077f4ffe by task ife_out_out_bou/255 [ 138.425778][ C1] CPU: 1 UID: 0 PID: 255 Comm: ife_out_out_bou Not tainted 7.0.0-rc1-00169-gfbdfa8da05b6 #624 PREEMPT(full) [ 138.425795][ C1] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 [ 138.425800][ C1] Call Trace: [ 138.425804][ C1] <IRQ> [ 138.425808][ C1] dump_stack_lvl (lib/dump_stack.c:122) [ 138.425828][ C1] print_report (mm/kasan/report.c:379 mm/kasan/report.c:482) [ 138.425839][ C1] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:221) [ 138.425844][ C1] ? __virt_addr_valid (./arch/x86/include/asm/preempt.h:95 (discriminator 1) ./include/linux/rcupdate.h:975 (discriminator 1) ./include/linux/mmzone.h:2207 (discriminator 1) arch/x86/mm/physaddr.c:54 (discriminator 1)) [ 138.425853][ C1] ? ife_tlv_meta_encode (net/ife/ife.c:168) [ 138.425859][ C1] kasan_report (mm/kasan/report.c:221 mm/kasan/report.c:597) [ 138.425868][ C1] ? ife_tlv_meta_encode (net/ife/ife.c:168) [ 138.425878][ C1] kasan_check_range (mm/kasan/generic.c:186 (discriminator 1) mm/kasan/generic.c:200 (discriminator 1)) [ 138.425884][ C1] __asan_memset (mm/kasan/shadow.c:84 (discriminator 2)) [ 138.425889][ C1] ife_tlv_meta_encode (net/ife/ife.c:168) [ 138.425893][ C1] ? ife_tlv_meta_encode (net/ife/ife.c:171) [ 138.425898][ C1] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:221) [ 138.425903][ C1] ife_encode_meta_u16 (net/sched/act_ife.c:57) [ 138.425910][ C1] ? __pfx_do_raw_spin_lock (kernel/locking/spinlock_debug.c:114) [ 138.425916][ C1] ? __asan_memcpy (mm/kasan/shadow.c:105 (discriminator 3)) [ 138.425921][ C1] ? __pfx_ife_encode_meta_u16 (net/sched/act_ife.c:45) [ 138.425927][ C1] ? srso_alias_return_thunk (arch/x86/lib/retpoline.S:221) [ 138.425931][ C1] tcf_ife_act (net/sched/act_ife.c:847 net/sched/act_ife.c:879) To solve this issue, fix the replace behavior by adding the metalist to the ife rcu data structure. Fixes: aa9fd9a ("sched: act: ife: update parameters via rcu handling") Reported-by: Ruitong Liu <cnitlrt@gmail.com> Tested-by: Ruitong Liu <cnitlrt@gmail.com> Co-developed-by: Victor Nogueira <victor@mojatatu.com> Signed-off-by: Victor Nogueira <victor@mojatatu.com> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com> Link: https://patch.msgid.link/20260304140603.76500-1-jhs@mojatatu.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1 parent 4517b74 commit e2cedd4

File tree

2 files changed

+45
-52
lines changed

2 files changed

+45
-52
lines changed

include/net/tc_act/tc_ife.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,13 @@ struct tcf_ife_params {
1313
u8 eth_src[ETH_ALEN];
1414
u16 eth_type;
1515
u16 flags;
16-
16+
struct list_head metalist;
1717
struct rcu_head rcu;
1818
};
1919

2020
struct tcf_ife_info {
2121
struct tc_action common;
2222
struct tcf_ife_params __rcu *params;
23-
/* list of metaids allowed */
24-
struct list_head metalist;
2523
};
2624
#define to_ife(a) ((struct tcf_ife_info *)a)
2725

net/sched/act_ife.c

Lines changed: 44 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -293,8 +293,8 @@ static int load_metaops_and_vet(u32 metaid, void *val, int len, bool rtnl_held)
293293
/* called when adding new meta information
294294
*/
295295
static int __add_metainfo(const struct tcf_meta_ops *ops,
296-
struct tcf_ife_info *ife, u32 metaid, void *metaval,
297-
int len, bool atomic, bool exists)
296+
struct tcf_ife_params *p, u32 metaid, void *metaval,
297+
int len, bool atomic)
298298
{
299299
struct tcf_meta_info *mi = NULL;
300300
int ret = 0;
@@ -313,53 +313,48 @@ static int __add_metainfo(const struct tcf_meta_ops *ops,
313313
}
314314
}
315315

316-
if (exists)
317-
spin_lock_bh(&ife->tcf_lock);
318-
list_add_tail(&mi->metalist, &ife->metalist);
319-
if (exists)
320-
spin_unlock_bh(&ife->tcf_lock);
316+
list_add_tail(&mi->metalist, &p->metalist);
321317

322318
return ret;
323319
}
324320

325321
static int add_metainfo_and_get_ops(const struct tcf_meta_ops *ops,
326-
struct tcf_ife_info *ife, u32 metaid,
327-
bool exists)
322+
struct tcf_ife_params *p, u32 metaid)
328323
{
329324
int ret;
330325

331326
if (!try_module_get(ops->owner))
332327
return -ENOENT;
333-
ret = __add_metainfo(ops, ife, metaid, NULL, 0, true, exists);
328+
ret = __add_metainfo(ops, p, metaid, NULL, 0, true);
334329
if (ret)
335330
module_put(ops->owner);
336331
return ret;
337332
}
338333

339-
static int add_metainfo(struct tcf_ife_info *ife, u32 metaid, void *metaval,
340-
int len, bool exists)
334+
static int add_metainfo(struct tcf_ife_params *p, u32 metaid, void *metaval,
335+
int len)
341336
{
342337
const struct tcf_meta_ops *ops = find_ife_oplist(metaid);
343338
int ret;
344339

345340
if (!ops)
346341
return -ENOENT;
347-
ret = __add_metainfo(ops, ife, metaid, metaval, len, false, exists);
342+
ret = __add_metainfo(ops, p, metaid, metaval, len, false);
348343
if (ret)
349344
/*put back what find_ife_oplist took */
350345
module_put(ops->owner);
351346
return ret;
352347
}
353348

354-
static int use_all_metadata(struct tcf_ife_info *ife, bool exists)
349+
static int use_all_metadata(struct tcf_ife_params *p)
355350
{
356351
struct tcf_meta_ops *o;
357352
int rc = 0;
358353
int installed = 0;
359354

360355
read_lock(&ife_mod_lock);
361356
list_for_each_entry(o, &ifeoplist, list) {
362-
rc = add_metainfo_and_get_ops(o, ife, o->metaid, exists);
357+
rc = add_metainfo_and_get_ops(o, p, o->metaid);
363358
if (rc == 0)
364359
installed += 1;
365360
}
@@ -371,22 +366,22 @@ static int use_all_metadata(struct tcf_ife_info *ife, bool exists)
371366
return -EINVAL;
372367
}
373368

374-
static int dump_metalist(struct sk_buff *skb, struct tcf_ife_info *ife)
369+
static int dump_metalist(struct sk_buff *skb, struct tcf_ife_params *p)
375370
{
376371
struct tcf_meta_info *e;
377372
struct nlattr *nest;
378373
unsigned char *b = skb_tail_pointer(skb);
379374
int total_encoded = 0;
380375

381376
/*can only happen on decode */
382-
if (list_empty(&ife->metalist))
377+
if (list_empty(&p->metalist))
383378
return 0;
384379

385380
nest = nla_nest_start_noflag(skb, TCA_IFE_METALST);
386381
if (!nest)
387382
goto out_nlmsg_trim;
388383

389-
list_for_each_entry(e, &ife->metalist, metalist) {
384+
list_for_each_entry(e, &p->metalist, metalist) {
390385
if (!e->ops->get(skb, e))
391386
total_encoded += 1;
392387
}
@@ -403,13 +398,11 @@ static int dump_metalist(struct sk_buff *skb, struct tcf_ife_info *ife)
403398
return -1;
404399
}
405400

406-
/* under ife->tcf_lock */
407-
static void _tcf_ife_cleanup(struct tc_action *a)
401+
static void __tcf_ife_cleanup(struct tcf_ife_params *p)
408402
{
409-
struct tcf_ife_info *ife = to_ife(a);
410403
struct tcf_meta_info *e, *n;
411404

412-
list_for_each_entry_safe(e, n, &ife->metalist, metalist) {
405+
list_for_each_entry_safe(e, n, &p->metalist, metalist) {
413406
list_del(&e->metalist);
414407
if (e->metaval) {
415408
if (e->ops->release)
@@ -422,18 +415,23 @@ static void _tcf_ife_cleanup(struct tc_action *a)
422415
}
423416
}
424417

418+
static void tcf_ife_cleanup_params(struct rcu_head *head)
419+
{
420+
struct tcf_ife_params *p = container_of(head, struct tcf_ife_params,
421+
rcu);
422+
423+
__tcf_ife_cleanup(p);
424+
kfree(p);
425+
}
426+
425427
static void tcf_ife_cleanup(struct tc_action *a)
426428
{
427429
struct tcf_ife_info *ife = to_ife(a);
428430
struct tcf_ife_params *p;
429431

430-
spin_lock_bh(&ife->tcf_lock);
431-
_tcf_ife_cleanup(a);
432-
spin_unlock_bh(&ife->tcf_lock);
433-
434432
p = rcu_dereference_protected(ife->params, 1);
435433
if (p)
436-
kfree_rcu(p, rcu);
434+
call_rcu(&p->rcu, tcf_ife_cleanup_params);
437435
}
438436

439437
static int load_metalist(struct nlattr **tb, bool rtnl_held)
@@ -455,8 +453,7 @@ static int load_metalist(struct nlattr **tb, bool rtnl_held)
455453
return 0;
456454
}
457455

458-
static int populate_metalist(struct tcf_ife_info *ife, struct nlattr **tb,
459-
bool exists, bool rtnl_held)
456+
static int populate_metalist(struct tcf_ife_params *p, struct nlattr **tb)
460457
{
461458
int len = 0;
462459
int rc = 0;
@@ -468,7 +465,7 @@ static int populate_metalist(struct tcf_ife_info *ife, struct nlattr **tb,
468465
val = nla_data(tb[i]);
469466
len = nla_len(tb[i]);
470467

471-
rc = add_metainfo(ife, i, val, len, exists);
468+
rc = add_metainfo(p, i, val, len);
472469
if (rc)
473470
return rc;
474471
}
@@ -523,6 +520,7 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
523520
p = kzalloc_obj(*p);
524521
if (!p)
525522
return -ENOMEM;
523+
INIT_LIST_HEAD(&p->metalist);
526524

527525
if (tb[TCA_IFE_METALST]) {
528526
err = nla_parse_nested_deprecated(tb2, IFE_META_MAX,
@@ -567,8 +565,6 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
567565
}
568566

569567
ife = to_ife(*a);
570-
if (ret == ACT_P_CREATED)
571-
INIT_LIST_HEAD(&ife->metalist);
572568

573569
err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
574570
if (err < 0)
@@ -600,8 +596,7 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
600596
}
601597

602598
if (tb[TCA_IFE_METALST]) {
603-
err = populate_metalist(ife, tb2, exists,
604-
!(flags & TCA_ACT_FLAGS_NO_RTNL));
599+
err = populate_metalist(p, tb2);
605600
if (err)
606601
goto metadata_parse_err;
607602
} else {
@@ -610,7 +605,7 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
610605
* as we can. You better have at least one else we are
611606
* going to bail out
612607
*/
613-
err = use_all_metadata(ife, exists);
608+
err = use_all_metadata(p);
614609
if (err)
615610
goto metadata_parse_err;
616611
}
@@ -626,13 +621,14 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
626621
if (goto_ch)
627622
tcf_chain_put_by_act(goto_ch);
628623
if (p)
629-
kfree_rcu(p, rcu);
624+
call_rcu(&p->rcu, tcf_ife_cleanup_params);
630625

631626
return ret;
632627
metadata_parse_err:
633628
if (goto_ch)
634629
tcf_chain_put_by_act(goto_ch);
635630
release_idr:
631+
__tcf_ife_cleanup(p);
636632
kfree(p);
637633
tcf_idr_release(*a, bind);
638634
return err;
@@ -679,7 +675,7 @@ static int tcf_ife_dump(struct sk_buff *skb, struct tc_action *a, int bind,
679675
if (nla_put(skb, TCA_IFE_TYPE, 2, &p->eth_type))
680676
goto nla_put_failure;
681677

682-
if (dump_metalist(skb, ife)) {
678+
if (dump_metalist(skb, p)) {
683679
/*ignore failure to dump metalist */
684680
pr_info("Failed to dump metalist\n");
685681
}
@@ -693,13 +689,13 @@ static int tcf_ife_dump(struct sk_buff *skb, struct tc_action *a, int bind,
693689
return -1;
694690
}
695691

696-
static int find_decode_metaid(struct sk_buff *skb, struct tcf_ife_info *ife,
692+
static int find_decode_metaid(struct sk_buff *skb, struct tcf_ife_params *p,
697693
u16 metaid, u16 mlen, void *mdata)
698694
{
699695
struct tcf_meta_info *e;
700696

701697
/* XXX: use hash to speed up */
702-
list_for_each_entry(e, &ife->metalist, metalist) {
698+
list_for_each_entry_rcu(e, &p->metalist, metalist) {
703699
if (metaid == e->metaid) {
704700
if (e->ops) {
705701
/* We check for decode presence already */
@@ -716,10 +712,13 @@ static int tcf_ife_decode(struct sk_buff *skb, const struct tc_action *a,
716712
{
717713
struct tcf_ife_info *ife = to_ife(a);
718714
int action = ife->tcf_action;
715+
struct tcf_ife_params *p;
719716
u8 *ifehdr_end;
720717
u8 *tlv_data;
721718
u16 metalen;
722719

720+
p = rcu_dereference_bh(ife->params);
721+
723722
bstats_update(this_cpu_ptr(ife->common.cpu_bstats), skb);
724723
tcf_lastuse_update(&ife->tcf_tm);
725724

@@ -745,7 +744,7 @@ static int tcf_ife_decode(struct sk_buff *skb, const struct tc_action *a,
745744
return TC_ACT_SHOT;
746745
}
747746

748-
if (find_decode_metaid(skb, ife, mtype, dlen, curr_data)) {
747+
if (find_decode_metaid(skb, p, mtype, dlen, curr_data)) {
749748
/* abuse overlimits to count when we receive metadata
750749
* but dont have an ops for it
751750
*/
@@ -769,12 +768,12 @@ static int tcf_ife_decode(struct sk_buff *skb, const struct tc_action *a,
769768
/*XXX: check if we can do this at install time instead of current
770769
* send data path
771770
**/
772-
static int ife_get_sz(struct sk_buff *skb, struct tcf_ife_info *ife)
771+
static int ife_get_sz(struct sk_buff *skb, struct tcf_ife_params *p)
773772
{
774-
struct tcf_meta_info *e, *n;
773+
struct tcf_meta_info *e;
775774
int tot_run_sz = 0, run_sz = 0;
776775

777-
list_for_each_entry_safe(e, n, &ife->metalist, metalist) {
776+
list_for_each_entry_rcu(e, &p->metalist, metalist) {
778777
if (e->ops->check_presence) {
779778
run_sz = e->ops->check_presence(skb, e);
780779
tot_run_sz += run_sz;
@@ -795,7 +794,7 @@ static int tcf_ife_encode(struct sk_buff *skb, const struct tc_action *a,
795794
OUTERHDR:TOTMETALEN:{TLVHDR:Metadatum:TLVHDR..}:ORIGDATA
796795
where ORIGDATA = original ethernet header ...
797796
*/
798-
u16 metalen = ife_get_sz(skb, ife);
797+
u16 metalen = ife_get_sz(skb, p);
799798
int hdrm = metalen + skb->dev->hard_header_len + IFE_METAHDRLEN;
800799
unsigned int skboff = 0;
801800
int new_len = skb->len + hdrm;
@@ -833,25 +832,21 @@ static int tcf_ife_encode(struct sk_buff *skb, const struct tc_action *a,
833832
if (!ife_meta)
834833
goto drop;
835834

836-
spin_lock(&ife->tcf_lock);
837-
838835
/* XXX: we dont have a clever way of telling encode to
839836
* not repeat some of the computations that are done by
840837
* ops->presence_check...
841838
*/
842-
list_for_each_entry(e, &ife->metalist, metalist) {
839+
list_for_each_entry_rcu(e, &p->metalist, metalist) {
843840
if (e->ops->encode) {
844841
err = e->ops->encode(skb, (void *)(ife_meta + skboff),
845842
e);
846843
}
847844
if (err < 0) {
848845
/* too corrupt to keep around if overwritten */
849-
spin_unlock(&ife->tcf_lock);
850846
goto drop;
851847
}
852848
skboff += err;
853849
}
854-
spin_unlock(&ife->tcf_lock);
855850
oethh = (struct ethhdr *)skb->data;
856851

857852
if (!is_zero_ether_addr(p->eth_src))

0 commit comments

Comments
 (0)