Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: aa9fd9a325d51 ("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>

authored by

Jamal Hadi Salim and committed by
Jakub Kicinski
e2cedd40 4517b74c

+45 -52
+1 -3
include/net/tc_act/tc_ife.h
··· 13 u8 eth_src[ETH_ALEN]; 14 u16 eth_type; 15 u16 flags; 16 - 17 struct rcu_head rcu; 18 }; 19 20 struct tcf_ife_info { 21 struct tc_action common; 22 struct tcf_ife_params __rcu *params; 23 - /* list of metaids allowed */ 24 - struct list_head metalist; 25 }; 26 #define to_ife(a) ((struct tcf_ife_info *)a) 27
··· 13 u8 eth_src[ETH_ALEN]; 14 u16 eth_type; 15 u16 flags; 16 + struct list_head metalist; 17 struct rcu_head rcu; 18 }; 19 20 struct tcf_ife_info { 21 struct tc_action common; 22 struct tcf_ife_params __rcu *params; 23 }; 24 #define to_ife(a) ((struct tcf_ife_info *)a) 25
+44 -49
net/sched/act_ife.c
··· 293 /* called when adding new meta information 294 */ 295 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) 298 { 299 struct tcf_meta_info *mi = NULL; 300 int ret = 0; ··· 313 } 314 } 315 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); 321 322 return ret; 323 } 324 325 static int add_metainfo_and_get_ops(const struct tcf_meta_ops *ops, 326 - struct tcf_ife_info *ife, u32 metaid, 327 - bool exists) 328 { 329 int ret; 330 331 if (!try_module_get(ops->owner)) 332 return -ENOENT; 333 - ret = __add_metainfo(ops, ife, metaid, NULL, 0, true, exists); 334 if (ret) 335 module_put(ops->owner); 336 return ret; 337 } 338 339 - static int add_metainfo(struct tcf_ife_info *ife, u32 metaid, void *metaval, 340 - int len, bool exists) 341 { 342 const struct tcf_meta_ops *ops = find_ife_oplist(metaid); 343 int ret; 344 345 if (!ops) 346 return -ENOENT; 347 - ret = __add_metainfo(ops, ife, metaid, metaval, len, false, exists); 348 if (ret) 349 /*put back what find_ife_oplist took */ 350 module_put(ops->owner); 351 return ret; 352 } 353 354 - static int use_all_metadata(struct tcf_ife_info *ife, bool exists) 355 { 356 struct tcf_meta_ops *o; 357 int rc = 0; ··· 354 355 read_lock(&ife_mod_lock); 356 list_for_each_entry(o, &ifeoplist, list) { 357 - rc = add_metainfo_and_get_ops(o, ife, o->metaid, exists); 358 if (rc == 0) 359 installed += 1; 360 } ··· 366 return -EINVAL; 367 } 368 369 - static int dump_metalist(struct sk_buff *skb, struct tcf_ife_info *ife) 370 { 371 struct tcf_meta_info *e; 372 struct nlattr *nest; ··· 374 int total_encoded = 0; 375 376 /*can only happen on decode */ 377 - if (list_empty(&ife->metalist)) 378 return 0; 379 380 nest = nla_nest_start_noflag(skb, TCA_IFE_METALST); 381 if (!nest) 382 goto out_nlmsg_trim; 383 384 - list_for_each_entry(e, &ife->metalist, metalist) { 385 if (!e->ops->get(skb, e)) 386 total_encoded += 1; 387 } ··· 398 return -1; 399 } 400 401 - /* under ife->tcf_lock */ 402 - static void _tcf_ife_cleanup(struct tc_action *a) 403 { 404 - struct tcf_ife_info *ife = to_ife(a); 405 struct tcf_meta_info *e, *n; 406 407 - list_for_each_entry_safe(e, n, &ife->metalist, metalist) { 408 list_del(&e->metalist); 409 if (e->metaval) { 410 if (e->ops->release) ··· 415 } 416 } 417 418 static void tcf_ife_cleanup(struct tc_action *a) 419 { 420 struct tcf_ife_info *ife = to_ife(a); 421 struct tcf_ife_params *p; 422 423 - spin_lock_bh(&ife->tcf_lock); 424 - _tcf_ife_cleanup(a); 425 - spin_unlock_bh(&ife->tcf_lock); 426 - 427 p = rcu_dereference_protected(ife->params, 1); 428 if (p) 429 - kfree_rcu(p, rcu); 430 } 431 432 static int load_metalist(struct nlattr **tb, bool rtnl_held) ··· 453 return 0; 454 } 455 456 - static int populate_metalist(struct tcf_ife_info *ife, struct nlattr **tb, 457 - bool exists, bool rtnl_held) 458 { 459 int len = 0; 460 int rc = 0; ··· 465 val = nla_data(tb[i]); 466 len = nla_len(tb[i]); 467 468 - rc = add_metainfo(ife, i, val, len, exists); 469 if (rc) 470 return rc; 471 } ··· 520 p = kzalloc_obj(*p); 521 if (!p) 522 return -ENOMEM; 523 524 if (tb[TCA_IFE_METALST]) { 525 err = nla_parse_nested_deprecated(tb2, IFE_META_MAX, ··· 565 } 566 567 ife = to_ife(*a); 568 - if (ret == ACT_P_CREATED) 569 - INIT_LIST_HEAD(&ife->metalist); 570 571 err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); 572 if (err < 0) ··· 596 } 597 598 if (tb[TCA_IFE_METALST]) { 599 - err = populate_metalist(ife, tb2, exists, 600 - !(flags & TCA_ACT_FLAGS_NO_RTNL)); 601 if (err) 602 goto metadata_parse_err; 603 } else { ··· 605 * as we can. You better have at least one else we are 606 * going to bail out 607 */ 608 - err = use_all_metadata(ife, exists); 609 if (err) 610 goto metadata_parse_err; 611 } ··· 621 if (goto_ch) 622 tcf_chain_put_by_act(goto_ch); 623 if (p) 624 - kfree_rcu(p, rcu); 625 626 return ret; 627 metadata_parse_err: 628 if (goto_ch) 629 tcf_chain_put_by_act(goto_ch); 630 release_idr: 631 kfree(p); 632 tcf_idr_release(*a, bind); 633 return err; ··· 675 if (nla_put(skb, TCA_IFE_TYPE, 2, &p->eth_type)) 676 goto nla_put_failure; 677 678 - if (dump_metalist(skb, ife)) { 679 /*ignore failure to dump metalist */ 680 pr_info("Failed to dump metalist\n"); 681 } ··· 689 return -1; 690 } 691 692 - static int find_decode_metaid(struct sk_buff *skb, struct tcf_ife_info *ife, 693 u16 metaid, u16 mlen, void *mdata) 694 { 695 struct tcf_meta_info *e; 696 697 /* XXX: use hash to speed up */ 698 - list_for_each_entry(e, &ife->metalist, metalist) { 699 if (metaid == e->metaid) { 700 if (e->ops) { 701 /* We check for decode presence already */ ··· 712 { 713 struct tcf_ife_info *ife = to_ife(a); 714 int action = ife->tcf_action; 715 u8 *ifehdr_end; 716 u8 *tlv_data; 717 u16 metalen; 718 719 bstats_update(this_cpu_ptr(ife->common.cpu_bstats), skb); 720 tcf_lastuse_update(&ife->tcf_tm); ··· 744 return TC_ACT_SHOT; 745 } 746 747 - if (find_decode_metaid(skb, ife, mtype, dlen, curr_data)) { 748 /* abuse overlimits to count when we receive metadata 749 * but dont have an ops for it 750 */ ··· 768 /*XXX: check if we can do this at install time instead of current 769 * send data path 770 **/ 771 - static int ife_get_sz(struct sk_buff *skb, struct tcf_ife_info *ife) 772 { 773 - struct tcf_meta_info *e, *n; 774 int tot_run_sz = 0, run_sz = 0; 775 776 - list_for_each_entry_safe(e, n, &ife->metalist, metalist) { 777 if (e->ops->check_presence) { 778 run_sz = e->ops->check_presence(skb, e); 779 tot_run_sz += run_sz; ··· 794 OUTERHDR:TOTMETALEN:{TLVHDR:Metadatum:TLVHDR..}:ORIGDATA 795 where ORIGDATA = original ethernet header ... 796 */ 797 - u16 metalen = ife_get_sz(skb, ife); 798 int hdrm = metalen + skb->dev->hard_header_len + IFE_METAHDRLEN; 799 unsigned int skboff = 0; 800 int new_len = skb->len + hdrm; ··· 832 if (!ife_meta) 833 goto drop; 834 835 - spin_lock(&ife->tcf_lock); 836 - 837 /* XXX: we dont have a clever way of telling encode to 838 * not repeat some of the computations that are done by 839 * ops->presence_check... 840 */ 841 - list_for_each_entry(e, &ife->metalist, metalist) { 842 if (e->ops->encode) { 843 err = e->ops->encode(skb, (void *)(ife_meta + skboff), 844 e); 845 } 846 if (err < 0) { 847 /* too corrupt to keep around if overwritten */ 848 - spin_unlock(&ife->tcf_lock); 849 goto drop; 850 } 851 skboff += err; 852 } 853 - spin_unlock(&ife->tcf_lock); 854 oethh = (struct ethhdr *)skb->data; 855 856 if (!is_zero_ether_addr(p->eth_src))
··· 293 /* called when adding new meta information 294 */ 295 static int __add_metainfo(const struct tcf_meta_ops *ops, 296 + struct tcf_ife_params *p, u32 metaid, void *metaval, 297 + int len, bool atomic) 298 { 299 struct tcf_meta_info *mi = NULL; 300 int ret = 0; ··· 313 } 314 } 315 316 + list_add_tail(&mi->metalist, &p->metalist); 317 318 return ret; 319 } 320 321 static int add_metainfo_and_get_ops(const struct tcf_meta_ops *ops, 322 + struct tcf_ife_params *p, u32 metaid) 323 { 324 int ret; 325 326 if (!try_module_get(ops->owner)) 327 return -ENOENT; 328 + ret = __add_metainfo(ops, p, metaid, NULL, 0, true); 329 if (ret) 330 module_put(ops->owner); 331 return ret; 332 } 333 334 + static int add_metainfo(struct tcf_ife_params *p, u32 metaid, void *metaval, 335 + int len) 336 { 337 const struct tcf_meta_ops *ops = find_ife_oplist(metaid); 338 int ret; 339 340 if (!ops) 341 return -ENOENT; 342 + ret = __add_metainfo(ops, p, metaid, metaval, len, false); 343 if (ret) 344 /*put back what find_ife_oplist took */ 345 module_put(ops->owner); 346 return ret; 347 } 348 349 + static int use_all_metadata(struct tcf_ife_params *p) 350 { 351 struct tcf_meta_ops *o; 352 int rc = 0; ··· 359 360 read_lock(&ife_mod_lock); 361 list_for_each_entry(o, &ifeoplist, list) { 362 + rc = add_metainfo_and_get_ops(o, p, o->metaid); 363 if (rc == 0) 364 installed += 1; 365 } ··· 371 return -EINVAL; 372 } 373 374 + static int dump_metalist(struct sk_buff *skb, struct tcf_ife_params *p) 375 { 376 struct tcf_meta_info *e; 377 struct nlattr *nest; ··· 379 int total_encoded = 0; 380 381 /*can only happen on decode */ 382 + if (list_empty(&p->metalist)) 383 return 0; 384 385 nest = nla_nest_start_noflag(skb, TCA_IFE_METALST); 386 if (!nest) 387 goto out_nlmsg_trim; 388 389 + list_for_each_entry(e, &p->metalist, metalist) { 390 if (!e->ops->get(skb, e)) 391 total_encoded += 1; 392 } ··· 403 return -1; 404 } 405 406 + static void __tcf_ife_cleanup(struct tcf_ife_params *p) 407 { 408 struct tcf_meta_info *e, *n; 409 410 + list_for_each_entry_safe(e, n, &p->metalist, metalist) { 411 list_del(&e->metalist); 412 if (e->metaval) { 413 if (e->ops->release) ··· 422 } 423 } 424 425 + static void tcf_ife_cleanup_params(struct rcu_head *head) 426 + { 427 + struct tcf_ife_params *p = container_of(head, struct tcf_ife_params, 428 + rcu); 429 + 430 + __tcf_ife_cleanup(p); 431 + kfree(p); 432 + } 433 + 434 static void tcf_ife_cleanup(struct tc_action *a) 435 { 436 struct tcf_ife_info *ife = to_ife(a); 437 struct tcf_ife_params *p; 438 439 p = rcu_dereference_protected(ife->params, 1); 440 if (p) 441 + call_rcu(&p->rcu, tcf_ife_cleanup_params); 442 } 443 444 static int load_metalist(struct nlattr **tb, bool rtnl_held) ··· 455 return 0; 456 } 457 458 + static int populate_metalist(struct tcf_ife_params *p, struct nlattr **tb) 459 { 460 int len = 0; 461 int rc = 0; ··· 468 val = nla_data(tb[i]); 469 len = nla_len(tb[i]); 470 471 + rc = add_metainfo(p, i, val, len); 472 if (rc) 473 return rc; 474 } ··· 523 p = kzalloc_obj(*p); 524 if (!p) 525 return -ENOMEM; 526 + INIT_LIST_HEAD(&p->metalist); 527 528 if (tb[TCA_IFE_METALST]) { 529 err = nla_parse_nested_deprecated(tb2, IFE_META_MAX, ··· 567 } 568 569 ife = to_ife(*a); 570 571 err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); 572 if (err < 0) ··· 600 } 601 602 if (tb[TCA_IFE_METALST]) { 603 + err = populate_metalist(p, tb2); 604 if (err) 605 goto metadata_parse_err; 606 } else { ··· 610 * as we can. You better have at least one else we are 611 * going to bail out 612 */ 613 + err = use_all_metadata(p); 614 if (err) 615 goto metadata_parse_err; 616 } ··· 626 if (goto_ch) 627 tcf_chain_put_by_act(goto_ch); 628 if (p) 629 + call_rcu(&p->rcu, tcf_ife_cleanup_params); 630 631 return ret; 632 metadata_parse_err: 633 if (goto_ch) 634 tcf_chain_put_by_act(goto_ch); 635 release_idr: 636 + __tcf_ife_cleanup(p); 637 kfree(p); 638 tcf_idr_release(*a, bind); 639 return err; ··· 679 if (nla_put(skb, TCA_IFE_TYPE, 2, &p->eth_type)) 680 goto nla_put_failure; 681 682 + if (dump_metalist(skb, p)) { 683 /*ignore failure to dump metalist */ 684 pr_info("Failed to dump metalist\n"); 685 } ··· 693 return -1; 694 } 695 696 + static int find_decode_metaid(struct sk_buff *skb, struct tcf_ife_params *p, 697 u16 metaid, u16 mlen, void *mdata) 698 { 699 struct tcf_meta_info *e; 700 701 /* XXX: use hash to speed up */ 702 + list_for_each_entry_rcu(e, &p->metalist, metalist) { 703 if (metaid == e->metaid) { 704 if (e->ops) { 705 /* We check for decode presence already */ ··· 716 { 717 struct tcf_ife_info *ife = to_ife(a); 718 int action = ife->tcf_action; 719 + struct tcf_ife_params *p; 720 u8 *ifehdr_end; 721 u8 *tlv_data; 722 u16 metalen; 723 + 724 + p = rcu_dereference_bh(ife->params); 725 726 bstats_update(this_cpu_ptr(ife->common.cpu_bstats), skb); 727 tcf_lastuse_update(&ife->tcf_tm); ··· 745 return TC_ACT_SHOT; 746 } 747 748 + if (find_decode_metaid(skb, p, mtype, dlen, curr_data)) { 749 /* abuse overlimits to count when we receive metadata 750 * but dont have an ops for it 751 */ ··· 769 /*XXX: check if we can do this at install time instead of current 770 * send data path 771 **/ 772 + static int ife_get_sz(struct sk_buff *skb, struct tcf_ife_params *p) 773 { 774 + struct tcf_meta_info *e; 775 int tot_run_sz = 0, run_sz = 0; 776 777 + list_for_each_entry_rcu(e, &p->metalist, metalist) { 778 if (e->ops->check_presence) { 779 run_sz = e->ops->check_presence(skb, e); 780 tot_run_sz += run_sz; ··· 795 OUTERHDR:TOTMETALEN:{TLVHDR:Metadatum:TLVHDR..}:ORIGDATA 796 where ORIGDATA = original ethernet header ... 797 */ 798 + u16 metalen = ife_get_sz(skb, p); 799 int hdrm = metalen + skb->dev->hard_header_len + IFE_METAHDRLEN; 800 unsigned int skboff = 0; 801 int new_len = skb->len + hdrm; ··· 833 if (!ife_meta) 834 goto drop; 835 836 /* XXX: we dont have a clever way of telling encode to 837 * not repeat some of the computations that are done by 838 * ops->presence_check... 839 */ 840 + list_for_each_entry_rcu(e, &p->metalist, metalist) { 841 if (e->ops->encode) { 842 err = e->ops->encode(skb, (void *)(ife_meta + skboff), 843 e); 844 } 845 if (err < 0) { 846 /* too corrupt to keep around if overwritten */ 847 goto drop; 848 } 849 skboff += err; 850 } 851 oethh = (struct ethhdr *)skb->data; 852 853 if (!is_zero_ether_addr(p->eth_src))