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

selftests: openvswitch: Add validation for the recursion test

Add a test case into the netlink checks that will show the number of
nested action recursions won't exceed 16. Going to 17 on a small
clone call isn't enough to exhaust the stack on (most) systems, so
it should be safe to run even on systems that don't have the fix
applied.

Signed-off-by: Aaron Conole <aconole@redhat.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://lore.kernel.org/r/20240207132416.1488485-3-aconole@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Aaron Conole and committed by
Jakub Kicinski
bd128f62 6e2f90d3

+69 -15
+13
tools/testing/selftests/net/openvswitch/openvswitch.sh
··· 502 502 wc -l) == 2 ] || \ 503 503 return 1 504 504 505 + info "Checking clone depth" 505 506 ERR_MSG="Flow actions may not be safe on all matching packets" 507 + PRE_TEST=$(dmesg | grep -c "${ERR_MSG}") 508 + ovs_add_flow "test_netlink_checks" nv0 \ 509 + 'in_port(1),eth(),eth_type(0x800),ipv4()' \ 510 + 'clone(clone(clone(clone(clone(clone(clone(clone(clone(clone(clone(clone(clone(clone(clone(clone(clone(drop)))))))))))))))))' \ 511 + >/dev/null 2>&1 && return 1 512 + POST_TEST=$(dmesg | grep -c "${ERR_MSG}") 513 + 514 + if [ "$PRE_TEST" == "$POST_TEST" ]; then 515 + info "failed - clone depth too large" 516 + return 1 517 + fi 518 + 506 519 PRE_TEST=$(dmesg | grep -c "${ERR_MSG}") 507 520 ovs_add_flow "test_netlink_checks" nv0 \ 508 521 'in_port(1),eth(),eth_type(0x0806),arp()' 'drop(0),2' \
+56 -15
tools/testing/selftests/net/openvswitch/ovs-dpctl.py
··· 299 299 ("OVS_ACTION_ATTR_PUSH_NSH", "none"), 300 300 ("OVS_ACTION_ATTR_POP_NSH", "flag"), 301 301 ("OVS_ACTION_ATTR_METER", "none"), 302 - ("OVS_ACTION_ATTR_CLONE", "none"), 302 + ("OVS_ACTION_ATTR_CLONE", "recursive"), 303 303 ("OVS_ACTION_ATTR_CHECK_PKT_LEN", "none"), 304 304 ("OVS_ACTION_ATTR_ADD_MPLS", "none"), 305 305 ("OVS_ACTION_ATTR_DEC_TTL", "none"), ··· 465 465 print_str += "pop_mpls" 466 466 else: 467 467 datum = self.get_attr(field[0]) 468 - print_str += datum.dpstr(more) 468 + if field[0] == "OVS_ACTION_ATTR_CLONE": 469 + print_str += "clone(" 470 + print_str += datum.dpstr(more) 471 + print_str += ")" 472 + else: 473 + print_str += datum.dpstr(more) 469 474 470 475 return print_str 471 476 472 477 def parse(self, actstr): 478 + totallen = len(actstr) 473 479 while len(actstr) != 0: 474 480 parsed = False 481 + parencount = 0 475 482 if actstr.startswith("drop"): 476 483 # If no reason is provided, the implicit drop is used (i.e no 477 484 # action). If some reason is given, an explicit action is used. 478 - actstr, reason = parse_extract_field( 479 - actstr, 480 - "drop(", 481 - "([0-9]+)", 482 - lambda x: int(x, 0), 483 - False, 484 - None, 485 - ) 485 + reason = None 486 + if actstr.startswith("drop("): 487 + parencount += 1 488 + 489 + actstr, reason = parse_extract_field( 490 + actstr, 491 + "drop(", 492 + "([0-9]+)", 493 + lambda x: int(x, 0), 494 + False, 495 + None, 496 + ) 497 + 486 498 if reason is not None: 487 499 self["attrs"].append(["OVS_ACTION_ATTR_DROP", reason]) 488 500 parsed = True 489 501 else: 490 - return 502 + actstr = actstr[len("drop"): ] 503 + return (totallen - len(actstr)) 491 504 492 505 elif parse_starts_block(actstr, "^(\d+)", False, True): 493 506 actstr, output = parse_extract_field( ··· 517 504 False, 518 505 0, 519 506 ) 507 + parencount += 1 520 508 self["attrs"].append(["OVS_ACTION_ATTR_RECIRC", recircid]) 521 509 parsed = True 522 510 ··· 530 516 531 517 for flat_act in parse_flat_map: 532 518 if parse_starts_block(actstr, flat_act[0], False): 533 - actstr += len(flat_act[0]) 519 + actstr = actstr[len(flat_act[0]):] 534 520 self["attrs"].append([flat_act[1]]) 535 521 actstr = actstr[strspn(actstr, ", ") :] 536 522 parsed = True 537 523 538 - if parse_starts_block(actstr, "ct(", False): 524 + if parse_starts_block(actstr, "clone(", False): 525 + parencount += 1 526 + subacts = ovsactions() 527 + actstr = actstr[len("clone("):] 528 + parsedLen = subacts.parse(actstr) 529 + lst = [] 530 + self["attrs"].append(("OVS_ACTION_ATTR_CLONE", subacts)) 531 + actstr = actstr[parsedLen:] 532 + parsed = True 533 + elif parse_starts_block(actstr, "ct(", False): 534 + parencount += 1 539 535 actstr = actstr[len("ct(") :] 540 536 ctact = ovsactions.ctact() 541 537 ··· 577 553 natact = ovsactions.ctact.natattr() 578 554 579 555 if actstr.startswith("("): 556 + parencount += 1 580 557 t = None 581 558 actstr = actstr[1:] 582 559 if actstr.startswith("src"): ··· 632 607 actstr = actstr[strspn(actstr, ", ") :] 633 608 634 609 ctact["attrs"].append(["OVS_CT_ATTR_NAT", natact]) 635 - actstr = actstr[strspn(actstr, ",) ") :] 610 + actstr = actstr[strspn(actstr, ", ") :] 636 611 637 612 self["attrs"].append(["OVS_ACTION_ATTR_CT", ctact]) 638 613 parsed = True 639 614 640 - actstr = actstr[strspn(actstr, "), ") :] 615 + actstr = actstr[strspn(actstr, ", ") :] 616 + while parencount > 0: 617 + parencount -= 1 618 + actstr = actstr[strspn(actstr, " "):] 619 + if len(actstr) and actstr[0] != ")": 620 + raise ValueError("Action str: '%s' unbalanced" % actstr) 621 + actstr = actstr[1:] 622 + 623 + if len(actstr) and actstr[0] == ")": 624 + return (totallen - len(actstr)) 625 + 626 + actstr = actstr[strspn(actstr, ", ") :] 627 + 641 628 if not parsed: 642 629 raise ValueError("Action str: '%s' not supported" % actstr) 630 + 631 + return (totallen - len(actstr)) 643 632 644 633 645 634 class ovskey(nla): ··· 2149 2110 ovsvp = OvsVport(ovspk) 2150 2111 ovsflow = OvsFlow() 2151 2112 ndb = NDB() 2113 + 2114 + sys.setrecursionlimit(100000) 2152 2115 2153 2116 if hasattr(args, "showdp"): 2154 2117 found = False