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

Merge branch 'regular-expression-support-for-test-output-matching'

Cupertino Miranda says:

====================
Regular expression support for test output matching

Hi everyone,

This version removes regexp from inline assembly examples that did not
require the regular expressions to match.

Thanks,
Cupertino
====================

Link: https://lore.kernel.org/r/20240617141458.471620-1-cupertino.miranda@oracle.com
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

+105 -39
+8 -3
tools/testing/selftests/bpf/progs/bpf_misc.h
··· 7 7 * 8 8 * The test_loader sequentially loads each program in a skeleton. 9 9 * Programs could be loaded in privileged and unprivileged modes. 10 - * - __success, __failure, __msg imply privileged mode; 11 - * - __success_unpriv, __failure_unpriv, __msg_unpriv imply 12 - * unprivileged mode. 10 + * - __success, __failure, __msg, __regex imply privileged mode; 11 + * - __success_unpriv, __failure_unpriv, __msg_unpriv, __regex_unpriv 12 + * imply unprivileged mode. 13 13 * If combination of privileged and unprivileged attributes is present 14 14 * both modes are used. If none are present privileged mode is implied. 15 15 * ··· 23 23 * __msg Message expected to be found in the verifier log. 24 24 * Multiple __msg attributes could be specified. 25 25 * __msg_unpriv Same as __msg but for unprivileged mode. 26 + * 27 + * __regex Same as __msg, but using a regular expression. 28 + * __regex_unpriv Same as __msg_unpriv but using a regular expression. 26 29 * 27 30 * __success Expect program load success in privileged mode. 28 31 * __success_unpriv Expect program load success in unprivileged mode. ··· 62 59 * __auxiliary_unpriv Same, but load program in unprivileged mode. 63 60 */ 64 61 #define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" msg))) 62 + #define __regex(regex) __attribute__((btf_decl_tag("comment:test_expect_regex=" regex))) 65 63 #define __failure __attribute__((btf_decl_tag("comment:test_expect_failure"))) 66 64 #define __success __attribute__((btf_decl_tag("comment:test_expect_success"))) 67 65 #define __description(desc) __attribute__((btf_decl_tag("comment:test_description=" desc))) 68 66 #define __msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_msg_unpriv=" msg))) 67 + #define __regex_unpriv(regex) __attribute__((btf_decl_tag("comment:test_expect_regex_unpriv=" regex))) 69 68 #define __failure_unpriv __attribute__((btf_decl_tag("comment:test_expect_failure_unpriv"))) 70 69 #define __success_unpriv __attribute__((btf_decl_tag("comment:test_expect_success_unpriv"))) 71 70 #define __log_level(lvl) __attribute__((btf_decl_tag("comment:test_log_level="#lvl)))
+3 -3
tools/testing/selftests/bpf/progs/dynptr_fail.c
··· 964 964 * mem_or_null pointers. 965 965 */ 966 966 SEC("?raw_tp") 967 - __failure __msg("R1 type=scalar expected=percpu_ptr_") 967 + __failure __regex("R[0-9]+ type=scalar expected=percpu_ptr_") 968 968 int dynptr_invalidate_slice_or_null(void *ctx) 969 969 { 970 970 struct bpf_dynptr ptr; ··· 982 982 983 983 /* Destruction of dynptr should also any slices obtained from it */ 984 984 SEC("?raw_tp") 985 - __failure __msg("R7 invalid mem access 'scalar'") 985 + __failure __regex("R[0-9]+ invalid mem access 'scalar'") 986 986 int dynptr_invalidate_slice_failure(void *ctx) 987 987 { 988 988 struct bpf_dynptr ptr1; ··· 1069 1069 1070 1070 /* bpf_dynptr_slice()s are read-only and cannot be written to */ 1071 1071 SEC("?tc") 1072 - __failure __msg("R0 cannot write into rdonly_mem") 1072 + __failure __regex("R[0-9]+ cannot write into rdonly_mem") 1073 1073 int skb_invalid_slice_write(struct __sk_buff *skb) 1074 1074 { 1075 1075 struct bpf_dynptr ptr;
+1 -1
tools/testing/selftests/bpf/progs/rbtree_fail.c
··· 105 105 } 106 106 107 107 SEC("?tc") 108 - __failure __msg("Unreleased reference id=3 alloc_insn=10") 108 + __failure __regex("Unreleased reference id=3 alloc_insn=[0-9]+") 109 109 long rbtree_api_remove_no_drop(void *ctx) 110 110 { 111 111 struct bpf_rb_node *res;
+2 -2
tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c
··· 32 32 } 33 33 34 34 SEC("?tc") 35 - __failure __msg("Unreleased reference id=4 alloc_insn=21") 35 + __failure __regex("Unreleased reference id=4 alloc_insn=[0-9]+") 36 36 long rbtree_refcounted_node_ref_escapes(void *ctx) 37 37 { 38 38 struct node_acquire *n, *m; ··· 73 73 } 74 74 75 75 SEC("?tc") 76 - __failure __msg("Unreleased reference id=3 alloc_insn=9") 76 + __failure __regex("Unreleased reference id=3 alloc_insn=[0-9]+") 77 77 long rbtree_refcounted_node_ref_escapes_owning_input(void *ctx) 78 78 { 79 79 struct node_acquire *n, *m;
+91 -30
tools/testing/selftests/bpf/test_loader.c
··· 2 2 /* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ 3 3 #include <linux/capability.h> 4 4 #include <stdlib.h> 5 + #include <regex.h> 5 6 #include <test_progs.h> 6 7 #include <bpf/btf.h> 7 8 ··· 18 17 #define TEST_TAG_EXPECT_FAILURE "comment:test_expect_failure" 19 18 #define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success" 20 19 #define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg=" 20 + #define TEST_TAG_EXPECT_REGEX_PFX "comment:test_expect_regex=" 21 21 #define TEST_TAG_EXPECT_FAILURE_UNPRIV "comment:test_expect_failure_unpriv" 22 22 #define TEST_TAG_EXPECT_SUCCESS_UNPRIV "comment:test_expect_success_unpriv" 23 23 #define TEST_TAG_EXPECT_MSG_PFX_UNPRIV "comment:test_expect_msg_unpriv=" 24 + #define TEST_TAG_EXPECT_REGEX_PFX_UNPRIV "comment:test_expect_regex_unpriv=" 24 25 #define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level=" 25 26 #define TEST_TAG_PROG_FLAGS_PFX "comment:test_prog_flags=" 26 27 #define TEST_TAG_DESCRIPTION_PFX "comment:test_description=" ··· 49 46 UNPRIV = 2 50 47 }; 51 48 49 + struct expect_msg { 50 + const char *substr; /* substring match */ 51 + const char *regex_str; /* regex-based match */ 52 + regex_t regex; 53 + }; 54 + 52 55 struct test_subspec { 53 56 char *name; 54 57 bool expect_failure; 55 - const char **expect_msgs; 58 + struct expect_msg *expect_msgs; 56 59 size_t expect_msg_cnt; 57 60 int retval; 58 61 bool execute; ··· 98 89 99 90 static void free_test_spec(struct test_spec *spec) 100 91 { 92 + int i; 93 + 94 + /* Deallocate expect_msgs arrays. */ 95 + for (i = 0; i < spec->priv.expect_msg_cnt; i++) 96 + if (spec->priv.expect_msgs[i].regex_str) 97 + regfree(&spec->priv.expect_msgs[i].regex); 98 + for (i = 0; i < spec->unpriv.expect_msg_cnt; i++) 99 + if (spec->unpriv.expect_msgs[i].regex_str) 100 + regfree(&spec->unpriv.expect_msgs[i].regex); 101 + 101 102 free(spec->priv.name); 102 103 free(spec->unpriv.name); 103 104 free(spec->priv.expect_msgs); ··· 119 100 spec->unpriv.expect_msgs = NULL; 120 101 } 121 102 122 - static int push_msg(const char *msg, struct test_subspec *subspec) 103 + static int push_msg(const char *substr, const char *regex_str, struct test_subspec *subspec) 123 104 { 124 105 void *tmp; 106 + int regcomp_res; 107 + char error_msg[100]; 108 + struct expect_msg *msg; 125 109 126 - tmp = realloc(subspec->expect_msgs, (1 + subspec->expect_msg_cnt) * sizeof(void *)); 110 + tmp = realloc(subspec->expect_msgs, 111 + (1 + subspec->expect_msg_cnt) * sizeof(struct expect_msg)); 127 112 if (!tmp) { 128 113 ASSERT_FAIL("failed to realloc memory for messages\n"); 129 114 return -ENOMEM; 130 115 } 131 116 subspec->expect_msgs = tmp; 132 - subspec->expect_msgs[subspec->expect_msg_cnt++] = msg; 117 + msg = &subspec->expect_msgs[subspec->expect_msg_cnt]; 133 118 119 + if (substr) { 120 + msg->substr = substr; 121 + msg->regex_str = NULL; 122 + } else { 123 + msg->regex_str = regex_str; 124 + msg->substr = NULL; 125 + regcomp_res = regcomp(&msg->regex, regex_str, REG_EXTENDED|REG_NEWLINE); 126 + if (regcomp_res != 0) { 127 + regerror(regcomp_res, &msg->regex, error_msg, sizeof(error_msg)); 128 + PRINT_FAIL("Regexp compilation error in '%s': '%s'\n", 129 + regex_str, error_msg); 130 + return -EINVAL; 131 + } 132 + } 133 + 134 + subspec->expect_msg_cnt += 1; 134 135 return 0; 135 136 } 136 137 ··· 272 233 spec->mode_mask |= UNPRIV; 273 234 } else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX)) { 274 235 msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1; 275 - err = push_msg(msg, &spec->priv); 236 + err = push_msg(msg, NULL, &spec->priv); 276 237 if (err) 277 238 goto cleanup; 278 239 spec->mode_mask |= PRIV; 279 240 } else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX_UNPRIV)) { 280 241 msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX_UNPRIV) - 1; 281 - err = push_msg(msg, &spec->unpriv); 242 + err = push_msg(msg, NULL, &spec->unpriv); 243 + if (err) 244 + goto cleanup; 245 + spec->mode_mask |= UNPRIV; 246 + } else if (str_has_pfx(s, TEST_TAG_EXPECT_REGEX_PFX)) { 247 + msg = s + sizeof(TEST_TAG_EXPECT_REGEX_PFX) - 1; 248 + err = push_msg(NULL, msg, &spec->priv); 249 + if (err) 250 + goto cleanup; 251 + spec->mode_mask |= PRIV; 252 + } else if (str_has_pfx(s, TEST_TAG_EXPECT_REGEX_PFX_UNPRIV)) { 253 + msg = s + sizeof(TEST_TAG_EXPECT_REGEX_PFX_UNPRIV) - 1; 254 + err = push_msg(NULL, msg, &spec->unpriv); 282 255 if (err) 283 256 goto cleanup; 284 257 spec->mode_mask |= UNPRIV; ··· 388 337 } 389 338 390 339 if (!spec->unpriv.expect_msgs) { 391 - size_t sz = spec->priv.expect_msg_cnt * sizeof(void *); 340 + for (i = 0; i < spec->priv.expect_msg_cnt; i++) { 341 + struct expect_msg *msg = &spec->priv.expect_msgs[i]; 392 342 393 - spec->unpriv.expect_msgs = malloc(sz); 394 - if (!spec->unpriv.expect_msgs) { 395 - PRINT_FAIL("failed to allocate memory for unpriv.expect_msgs\n"); 396 - err = -ENOMEM; 397 - goto cleanup; 343 + err = push_msg(msg->substr, msg->regex_str, &spec->unpriv); 344 + if (err) 345 + goto cleanup; 398 346 } 399 - memcpy(spec->unpriv.expect_msgs, spec->priv.expect_msgs, sz); 400 - spec->unpriv.expect_msg_cnt = spec->priv.expect_msg_cnt; 401 347 } 402 348 } 403 349 ··· 450 402 struct bpf_program *prog, 451 403 int load_err) 452 404 { 453 - int i, j; 405 + int i, j, err; 406 + char *match; 407 + regmatch_t reg_match[1]; 454 408 455 409 for (i = 0; i < subspec->expect_msg_cnt; i++) { 456 - char *match; 457 - const char *expect_msg; 410 + struct expect_msg *msg = &subspec->expect_msgs[i]; 458 411 459 - expect_msg = subspec->expect_msgs[i]; 460 - 461 - match = strstr(tester->log_buf + tester->next_match_pos, expect_msg); 462 - if (!ASSERT_OK_PTR(match, "expect_msg")) { 463 - /* if we are in verbose mode, we've already emitted log */ 464 - if (env.verbosity == VERBOSE_NONE) 465 - emit_verifier_log(tester->log_buf, true /*force*/); 466 - for (j = 0; j < i; j++) 467 - fprintf(stderr, 468 - "MATCHED MSG: '%s'\n", subspec->expect_msgs[j]); 469 - fprintf(stderr, "EXPECTED MSG: '%s'\n", expect_msg); 470 - return; 412 + if (msg->substr) { 413 + match = strstr(tester->log_buf + tester->next_match_pos, msg->substr); 414 + if (match) 415 + tester->next_match_pos = match - tester->log_buf + strlen(msg->substr); 416 + } else { 417 + err = regexec(&msg->regex, 418 + tester->log_buf + tester->next_match_pos, 1, reg_match, 0); 419 + if (err == 0) { 420 + match = tester->log_buf + tester->next_match_pos + reg_match[0].rm_so; 421 + tester->next_match_pos += reg_match[0].rm_eo; 422 + } else { 423 + match = NULL; 424 + } 471 425 } 472 426 473 - tester->next_match_pos = match - tester->log_buf + strlen(expect_msg); 427 + if (!ASSERT_OK_PTR(match, "expect_msg")) { 428 + if (env.verbosity == VERBOSE_NONE) 429 + emit_verifier_log(tester->log_buf, true /*force*/); 430 + for (j = 0; j <= i; j++) { 431 + msg = &subspec->expect_msgs[j]; 432 + fprintf(stderr, "%s %s: '%s'\n", 433 + j < i ? "MATCHED " : "EXPECTED", 434 + msg->substr ? "SUBSTR" : " REGEX", 435 + msg->substr ?: msg->regex_str); 436 + } 437 + return; 438 + } 474 439 } 475 440 } 476 441