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

selftests/bpf: __not_msg() tag for test_loader framework

This patch adds tags __not_msg(<msg>) and __not_msg_unpriv(<msg>).
Test fails if <msg> is found in verifier log.

If __msg_not() is situated between __msg() tags framework matches
__msg() tags first, and then checks that <msg> is not present in a
portion of a log between bracketing __msg() tags.
__msg_not() tags bracketed by a same __msg() group are effectively
unordered.

The idea is borrowed from LLVM's CheckFile with its CHECK-NOT syntax.

Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20250918-callchain-sensitive-liveness-v3-11-c3cd27bacc60@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Eduard Zingerman and committed by
Alexei Starovoitov
34c513be 79f047c7

+301 -49
+125
tools/testing/selftests/bpf/prog_tests/prog_tests_framework.c
··· 54 54 return; 55 55 clear_test_state(state); 56 56 } 57 + 58 + static void dummy_emit(const char *buf, bool force) {} 59 + 60 + void test_prog_tests_framework_expected_msgs(void) 61 + { 62 + struct expected_msgs msgs; 63 + int i, j, error_cnt; 64 + const struct { 65 + const char *name; 66 + const char *log; 67 + const char *expected; 68 + struct expect_msg *pats; 69 + } cases[] = { 70 + { 71 + .name = "simple-ok", 72 + .log = "aaabbbccc", 73 + .pats = (struct expect_msg[]) { 74 + { .substr = "aaa" }, 75 + { .substr = "ccc" }, 76 + {} 77 + } 78 + }, 79 + { 80 + .name = "simple-fail", 81 + .log = "aaabbbddd", 82 + .expected = "MATCHED SUBSTR: 'aaa'\n" 83 + "EXPECTED SUBSTR: 'ccc'\n", 84 + .pats = (struct expect_msg[]) { 85 + { .substr = "aaa" }, 86 + { .substr = "ccc" }, 87 + {} 88 + } 89 + }, 90 + { 91 + .name = "negative-ok-mid", 92 + .log = "aaabbbccc", 93 + .pats = (struct expect_msg[]) { 94 + { .substr = "aaa" }, 95 + { .substr = "foo", .negative = true }, 96 + { .substr = "bar", .negative = true }, 97 + { .substr = "ccc" }, 98 + {} 99 + } 100 + }, 101 + { 102 + .name = "negative-ok-tail", 103 + .log = "aaabbbccc", 104 + .pats = (struct expect_msg[]) { 105 + { .substr = "aaa" }, 106 + { .substr = "foo", .negative = true }, 107 + {} 108 + } 109 + }, 110 + { 111 + .name = "negative-ok-head", 112 + .log = "aaabbbccc", 113 + .pats = (struct expect_msg[]) { 114 + { .substr = "foo", .negative = true }, 115 + { .substr = "ccc" }, 116 + {} 117 + } 118 + }, 119 + { 120 + .name = "negative-fail-head", 121 + .log = "aaabbbccc", 122 + .expected = "UNEXPECTED SUBSTR: 'aaa'\n", 123 + .pats = (struct expect_msg[]) { 124 + { .substr = "aaa", .negative = true }, 125 + { .substr = "bbb" }, 126 + {} 127 + } 128 + }, 129 + { 130 + .name = "negative-fail-tail", 131 + .log = "aaabbbccc", 132 + .expected = "UNEXPECTED SUBSTR: 'ccc'\n", 133 + .pats = (struct expect_msg[]) { 134 + { .substr = "bbb" }, 135 + { .substr = "ccc", .negative = true }, 136 + {} 137 + } 138 + }, 139 + { 140 + .name = "negative-fail-mid-1", 141 + .log = "aaabbbccc", 142 + .expected = "UNEXPECTED SUBSTR: 'bbb'\n", 143 + .pats = (struct expect_msg[]) { 144 + { .substr = "aaa" }, 145 + { .substr = "bbb", .negative = true }, 146 + { .substr = "ccc" }, 147 + {} 148 + } 149 + }, 150 + { 151 + .name = "negative-fail-mid-2", 152 + .log = "aaabbb222ccc", 153 + .expected = "UNEXPECTED SUBSTR: '222'\n", 154 + .pats = (struct expect_msg[]) { 155 + { .substr = "aaa" }, 156 + { .substr = "222", .negative = true }, 157 + { .substr = "bbb", .negative = true }, 158 + { .substr = "ccc" }, 159 + {} 160 + } 161 + } 162 + }; 163 + 164 + for (i = 0; i < ARRAY_SIZE(cases); i++) { 165 + if (test__start_subtest(cases[i].name)) { 166 + error_cnt = env.subtest_state->error_cnt; 167 + msgs.patterns = cases[i].pats; 168 + msgs.cnt = 0; 169 + for (j = 0; cases[i].pats[j].substr; j++) 170 + msgs.cnt++; 171 + validate_msgs(cases[i].log, &msgs, dummy_emit); 172 + fflush(stderr); 173 + env.subtest_state->error_cnt = error_cnt; 174 + if (cases[i].expected) 175 + ASSERT_HAS_SUBSTR(env.subtest_state->log_buf, cases[i].expected, "expected output"); 176 + else 177 + ASSERT_STREQ(env.subtest_state->log_buf, "", "expected no output"); 178 + test__end_subtest(); 179 + } 180 + } 181 + }
+9
tools/testing/selftests/bpf/progs/bpf_misc.h
··· 33 33 * e.g. "foo{{[0-9]+}}" matches strings like "foo007". 34 34 * Extended POSIX regular expression syntax is allowed 35 35 * inside the brackets. 36 + * __not_msg Message not expected to be found in verifier log. 37 + * If __msg_not is situated between __msg tags 38 + * framework matches __msg tags first, and then 39 + * checks that __msg_not is not present in a portion of 40 + * a log between bracketing __msg tags. 41 + * Same regex syntax as for __msg is supported. 36 42 * __msg_unpriv Same as __msg but for unprivileged mode. 43 + * __not_msg_unpriv Same as __not_msg but for unprivileged mode. 37 44 * 38 45 * __stderr Message expected to be found in bpf stderr stream. The 39 46 * same regex rules apply like __msg. ··· 128 121 * __caps_unpriv Specify the capabilities that should be set when running the test. 129 122 */ 130 123 #define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" XSTR(__COUNTER__) "=" msg))) 124 + #define __not_msg(msg) __attribute__((btf_decl_tag("comment:test_expect_not_msg=" XSTR(__COUNTER__) "=" msg))) 131 125 #define __xlated(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated=" XSTR(__COUNTER__) "=" msg))) 132 126 #define __jited(msg) __attribute__((btf_decl_tag("comment:test_jited=" XSTR(__COUNTER__) "=" msg))) 133 127 #define __failure __attribute__((btf_decl_tag("comment:test_expect_failure"))) 134 128 #define __success __attribute__((btf_decl_tag("comment:test_expect_success"))) 135 129 #define __description(desc) __attribute__((btf_decl_tag("comment:test_description=" desc))) 136 130 #define __msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_msg_unpriv=" XSTR(__COUNTER__) "=" msg))) 131 + #define __not_msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_not_msg_unpriv=" XSTR(__COUNTER__) "=" msg))) 137 132 #define __xlated_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated_unpriv=" XSTR(__COUNTER__) "=" msg))) 138 133 #define __jited_unpriv(msg) __attribute__((btf_decl_tag("comment:test_jited=" XSTR(__COUNTER__) "=" msg))) 139 134 #define __failure_unpriv __attribute__((btf_decl_tag("comment:test_expect_failure_unpriv")))
+150 -49
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> 6 5 #include <test_progs.h> 7 6 #include <bpf/btf.h> 8 7 ··· 19 20 #define TEST_TAG_EXPECT_FAILURE "comment:test_expect_failure" 20 21 #define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success" 21 22 #define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg=" 23 + #define TEST_TAG_EXPECT_NOT_MSG_PFX "comment:test_expect_not_msg=" 22 24 #define TEST_TAG_EXPECT_XLATED_PFX "comment:test_expect_xlated=" 23 25 #define TEST_TAG_EXPECT_FAILURE_UNPRIV "comment:test_expect_failure_unpriv" 24 26 #define TEST_TAG_EXPECT_SUCCESS_UNPRIV "comment:test_expect_success_unpriv" 25 27 #define TEST_TAG_EXPECT_MSG_PFX_UNPRIV "comment:test_expect_msg_unpriv=" 28 + #define TEST_TAG_EXPECT_NOT_MSG_PFX_UNPRIV "comment:test_expect_not_msg_unpriv=" 26 29 #define TEST_TAG_EXPECT_XLATED_PFX_UNPRIV "comment:test_expect_xlated_unpriv=" 27 30 #define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level=" 28 31 #define TEST_TAG_PROG_FLAGS_PFX "comment:test_prog_flags=" ··· 64 63 enum load_mode { 65 64 JITED = 1 << 0, 66 65 NO_JITED = 1 << 1, 67 - }; 68 - 69 - struct expect_msg { 70 - const char *substr; /* substring match */ 71 - regex_t regex; 72 - bool is_regex; 73 - bool on_next_line; 74 - }; 75 - 76 - struct expected_msgs { 77 - struct expect_msg *patterns; 78 - size_t cnt; 79 66 }; 80 67 81 68 struct test_subspec { ··· 205 216 return 0; 206 217 } 207 218 208 - static int __push_msg(const char *pattern, bool on_next_line, struct expected_msgs *msgs) 219 + static int __push_msg(const char *pattern, bool on_next_line, bool negative, 220 + struct expected_msgs *msgs) 209 221 { 210 222 struct expect_msg *msg; 211 223 void *tmp; ··· 222 232 msg = &msgs->patterns[msgs->cnt]; 223 233 msg->on_next_line = on_next_line; 224 234 msg->substr = pattern; 235 + msg->negative = negative; 225 236 msg->is_regex = false; 226 237 if (strstr(pattern, "{{")) { 227 238 err = compile_regex(pattern, &msg->regex); ··· 241 250 242 251 for (i = 0; i < from->cnt; i++) { 243 252 msg = &from->patterns[i]; 244 - err = __push_msg(msg->substr, msg->on_next_line, to); 253 + err = __push_msg(msg->substr, msg->on_next_line, msg->negative, to); 245 254 if (err) 246 255 return err; 247 256 } 248 257 return 0; 249 258 } 250 259 251 - static int push_msg(const char *substr, struct expected_msgs *msgs) 260 + static int push_msg(const char *substr, bool negative, struct expected_msgs *msgs) 252 261 { 253 - return __push_msg(substr, false, msgs); 262 + return __push_msg(substr, false, negative, msgs); 254 263 } 255 264 256 265 static int push_disasm_msg(const char *regex_str, bool *on_next_line, struct expected_msgs *msgs) ··· 261 270 *on_next_line = false; 262 271 return 0; 263 272 } 264 - err = __push_msg(regex_str, *on_next_line, msgs); 273 + err = __push_msg(regex_str, *on_next_line, false, msgs); 265 274 if (err) 266 275 return err; 267 276 *on_next_line = true; ··· 473 482 spec->auxiliary = true; 474 483 spec->mode_mask |= UNPRIV; 475 484 } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_MSG_PFX))) { 476 - err = push_msg(msg, &spec->priv.expect_msgs); 485 + err = push_msg(msg, false, &spec->priv.expect_msgs); 486 + if (err) 487 + goto cleanup; 488 + spec->mode_mask |= PRIV; 489 + } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_NOT_MSG_PFX))) { 490 + err = push_msg(msg, true, &spec->priv.expect_msgs); 477 491 if (err) 478 492 goto cleanup; 479 493 spec->mode_mask |= PRIV; 480 494 } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_MSG_PFX_UNPRIV))) { 481 - err = push_msg(msg, &spec->unpriv.expect_msgs); 495 + err = push_msg(msg, false, &spec->unpriv.expect_msgs); 496 + if (err) 497 + goto cleanup; 498 + spec->mode_mask |= UNPRIV; 499 + } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_NOT_MSG_PFX_UNPRIV))) { 500 + err = push_msg(msg, true, &spec->unpriv.expect_msgs); 482 501 if (err) 483 502 goto cleanup; 484 503 spec->mode_mask |= UNPRIV; ··· 765 764 fprintf(stdout, "STDOUT:\n=============\n%s=============\n", bpf_stdout); 766 765 } 767 766 768 - static void validate_msgs(char *log_buf, struct expected_msgs *msgs, 769 - void (*emit_fn)(const char *buf, bool force)) 767 + static const char *match_msg(struct expect_msg *msg, const char **log) 770 768 { 771 - const char *log = log_buf, *prev_match; 769 + const char *match = NULL; 772 770 regmatch_t reg_match[1]; 773 - int prev_match_line; 774 - int match_line; 775 - int i, j, err; 771 + int err; 776 772 777 - prev_match_line = -1; 778 - match_line = 0; 773 + if (!msg->is_regex) { 774 + match = strstr(*log, msg->substr); 775 + if (match) 776 + *log = match + strlen(msg->substr); 777 + } else { 778 + err = regexec(&msg->regex, *log, 1, reg_match, 0); 779 + if (err == 0) { 780 + match = *log + reg_match[0].rm_so; 781 + *log += reg_match[0].rm_eo; 782 + } 783 + } 784 + return match; 785 + } 786 + 787 + static int count_lines(const char *start, const char *end) 788 + { 789 + const char *tmp; 790 + int n = 0; 791 + 792 + for (tmp = start; tmp < end; ++tmp) 793 + if (*tmp == '\n') 794 + n++; 795 + return n; 796 + } 797 + 798 + struct match { 799 + const char *start; 800 + const char *end; 801 + int line; 802 + }; 803 + 804 + /* 805 + * Positive messages are matched sequentially, each next message 806 + * is looked for starting from the end of a previous matched one. 807 + */ 808 + static void match_positive_msgs(const char *log, struct expected_msgs *msgs, struct match *matches) 809 + { 810 + const char *prev_match; 811 + int i, line; 812 + 779 813 prev_match = log; 814 + line = 0; 780 815 for (i = 0; i < msgs->cnt; i++) { 781 816 struct expect_msg *msg = &msgs->patterns[i]; 782 - const char *match = NULL, *pat_status; 783 - bool wrong_line = false; 817 + const char *match = NULL; 784 818 785 - if (!msg->is_regex) { 786 - match = strstr(log, msg->substr); 787 - if (match) 788 - log = match + strlen(msg->substr); 789 - } else { 790 - err = regexec(&msg->regex, log, 1, reg_match, 0); 791 - if (err == 0) { 792 - match = log + reg_match[0].rm_so; 793 - log += reg_match[0].rm_eo; 819 + if (msg->negative) 820 + continue; 821 + 822 + match = match_msg(msg, &log); 823 + if (match) { 824 + line += count_lines(prev_match, match); 825 + matches[i].start = match; 826 + matches[i].end = log; 827 + matches[i].line = line; 828 + prev_match = match; 829 + } 830 + } 831 + } 832 + 833 + /* 834 + * Each negative messages N located between positive messages P1 and P2 835 + * is matched in the span P1.end .. P2.start. Consequently, negative messages 836 + * are unordered within the span. 837 + */ 838 + static void match_negative_msgs(const char *log, struct expected_msgs *msgs, struct match *matches) 839 + { 840 + const char *start = log, *end, *next, *match; 841 + const char *log_end = log + strlen(log); 842 + int i, j, next_positive; 843 + 844 + for (i = 0; i < msgs->cnt; i++) { 845 + struct expect_msg *msg = &msgs->patterns[i]; 846 + 847 + /* positive message bumps span start */ 848 + if (!msg->negative) { 849 + start = matches[i].end ?: start; 850 + continue; 851 + } 852 + 853 + /* count stride of negative patterns and adjust span end */ 854 + end = log_end; 855 + for (next_positive = i + 1; next_positive < msgs->cnt; next_positive++) { 856 + if (!msgs->patterns[next_positive].negative) { 857 + end = matches[next_positive].start; 858 + break; 794 859 } 795 860 } 796 861 797 - if (match) { 798 - for (; prev_match < match; ++prev_match) 799 - if (*prev_match == '\n') 800 - ++match_line; 801 - wrong_line = msg->on_next_line && prev_match_line >= 0 && 802 - prev_match_line + 1 != match_line; 862 + /* try matching negative messages within identified span */ 863 + for (j = i; j < next_positive; j++) { 864 + next = start; 865 + match = match_msg(msg, &next); 866 + if (match && next <= end) { 867 + matches[j].start = match; 868 + matches[j].end = next; 869 + } 803 870 } 804 871 805 - if (!match || wrong_line) { 872 + /* -1 to account for i++ */ 873 + i = next_positive - 1; 874 + } 875 + } 876 + 877 + void validate_msgs(const char *log_buf, struct expected_msgs *msgs, 878 + void (*emit_fn)(const char *buf, bool force)) 879 + { 880 + struct match matches[msgs->cnt]; 881 + struct match *prev_match = NULL; 882 + int i, j; 883 + 884 + memset(matches, 0, sizeof(*matches) * msgs->cnt); 885 + match_positive_msgs(log_buf, msgs, matches); 886 + match_negative_msgs(log_buf, msgs, matches); 887 + 888 + for (i = 0; i < msgs->cnt; i++) { 889 + struct expect_msg *msg = &msgs->patterns[i]; 890 + struct match *match = &matches[i]; 891 + const char *pat_status; 892 + bool unexpected; 893 + bool wrong_line; 894 + bool no_match; 895 + 896 + no_match = !msg->negative && !match->start; 897 + wrong_line = !msg->negative && 898 + msg->on_next_line && 899 + prev_match && prev_match->line + 1 != match->line; 900 + unexpected = msg->negative && match->start; 901 + if (no_match || wrong_line || unexpected) { 806 902 PRINT_FAIL("expect_msg\n"); 807 903 if (env.verbosity == VERBOSE_NONE) 808 904 emit_fn(log_buf, true /*force*/); ··· 909 811 pat_status = "MATCHED "; 910 812 else if (wrong_line) 911 813 pat_status = "WRONG LINE"; 912 - else 814 + else if (no_match) 913 815 pat_status = "EXPECTED "; 816 + else 817 + pat_status = "UNEXPECTED"; 914 818 msg = &msgs->patterns[j]; 915 819 fprintf(stderr, "%s %s: '%s'\n", 916 820 pat_status, ··· 922 822 if (wrong_line) { 923 823 fprintf(stderr, 924 824 "expecting match at line %d, actual match is at line %d\n", 925 - prev_match_line + 1, match_line); 825 + prev_match->line + 1, match->line); 926 826 } 927 827 break; 928 828 } 929 829 930 - prev_match_line = match_line; 830 + if (!msg->negative) 831 + prev_match = match; 931 832 } 932 833 } 933 834
+17
tools/testing/selftests/bpf/test_progs.h
··· 7 7 #include <errno.h> 8 8 #include <string.h> 9 9 #include <assert.h> 10 + #include <regex.h> 10 11 #include <stdlib.h> 11 12 #include <stdarg.h> 12 13 #include <time.h> ··· 546 545 test_loader__run_subtests(&tester, #skel, skel##__elf_bytes); \ 547 546 test_loader_fini(&tester); \ 548 547 }) 548 + 549 + struct expect_msg { 550 + const char *substr; /* substring match */ 551 + regex_t regex; 552 + bool is_regex; 553 + bool on_next_line; 554 + bool negative; 555 + }; 556 + 557 + struct expected_msgs { 558 + struct expect_msg *patterns; 559 + size_t cnt; 560 + }; 561 + 562 + void validate_msgs(const char *log_buf, struct expected_msgs *msgs, 563 + void (*emit_fn)(const char *buf, bool force)); 549 564 550 565 #endif /* __TEST_PROGS_H */