Skip to content

Commit f8d1617

Browse files
eddyz87Alexei Starovoitov
authored andcommitted
selftests/bpf: replace __regex macro with "{{...}}" patterns
Upcoming changes require a notation to specify regular expression matches for regular verifier log messages, disassembly of BPF instructions, disassembly of jited instructions. Neither basic nor extended POSIX regular expressions w/o additional escaping are good for this role because of wide use of special characters in disassembly, for example: movq -0x10(%rbp), %rax ;; () are special characters cmpq $0x21, %rax ;; $ is a special character *(u64 *)(r10 -16) = r1 ;; * and () are special characters This commit borrows syntax from LLVM's FileCheck utility. It replaces __regex macro with ability to embed regular expressions in __msg patters using "{{" "}}" pairs for escaping. Syntax for __msg patterns: pattern := (<verbatim text> | regex)* regex := "{{" <posix extended regular expression> "}}" For example, pattern "foo{{[0-9]+}}" matches strings like "foo0", "foo007", etc. Signed-off-by: Eduard Zingerman <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent f00bb75 commit f8d1617

File tree

5 files changed

+115
-70
lines changed

5 files changed

+115
-70
lines changed

tools/testing/selftests/bpf/progs/bpf_misc.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,15 @@
2525
*
2626
* __msg Message expected to be found in the verifier log.
2727
* Multiple __msg attributes could be specified.
28+
* To match a regular expression use "{{" "}}" brackets,
29+
* e.g. "foo{{[0-9]+}}" matches strings like "foo007".
30+
* Extended POSIX regular expression syntax is allowed
31+
* inside the brackets.
2832
* __msg_unpriv Same as __msg but for unprivileged mode.
2933
*
30-
* __regex Same as __msg, but using a regular expression.
31-
* __regex_unpriv Same as __msg_unpriv but using a regular expression.
3234
* __xlated Expect a line in a disassembly log after verifier applies rewrites.
3335
* Multiple __xlated attributes could be specified.
36+
* Regular expressions could be specified same way as in __msg.
3437
* __xlated_unpriv Same as __xlated but for unprivileged mode.
3538
*
3639
* __success Expect program load success in privileged mode.
@@ -72,13 +75,11 @@
7275
* When test case is not run on current arch it is marked as skipped.
7376
*/
7477
#define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" XSTR(__COUNTER__) "=" msg)))
75-
#define __regex(regex) __attribute__((btf_decl_tag("comment:test_expect_regex=" XSTR(__COUNTER__) "=" regex)))
7678
#define __xlated(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated=" XSTR(__COUNTER__) "=" msg)))
7779
#define __failure __attribute__((btf_decl_tag("comment:test_expect_failure")))
7880
#define __success __attribute__((btf_decl_tag("comment:test_expect_success")))
7981
#define __description(desc) __attribute__((btf_decl_tag("comment:test_description=" desc)))
8082
#define __msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_msg_unpriv=" XSTR(__COUNTER__) "=" msg)))
81-
#define __regex_unpriv(regex) __attribute__((btf_decl_tag("comment:test_expect_regex_unpriv=" XSTR(__COUNTER__) "=" regex)))
8283
#define __xlated_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated_unpriv=" XSTR(__COUNTER__) "=" msg)))
8384
#define __failure_unpriv __attribute__((btf_decl_tag("comment:test_expect_failure_unpriv")))
8485
#define __success_unpriv __attribute__((btf_decl_tag("comment:test_expect_success_unpriv")))

tools/testing/selftests/bpf/progs/dynptr_fail.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -964,7 +964,7 @@ int dynptr_invalidate_slice_reinit(void *ctx)
964964
* mem_or_null pointers.
965965
*/
966966
SEC("?raw_tp")
967-
__failure __regex("R[0-9]+ type=scalar expected=percpu_ptr_")
967+
__failure __msg("R{{[0-9]+}} type=scalar expected=percpu_ptr_")
968968
int dynptr_invalidate_slice_or_null(void *ctx)
969969
{
970970
struct bpf_dynptr ptr;
@@ -982,7 +982,7 @@ int dynptr_invalidate_slice_or_null(void *ctx)
982982

983983
/* Destruction of dynptr should also any slices obtained from it */
984984
SEC("?raw_tp")
985-
__failure __regex("R[0-9]+ invalid mem access 'scalar'")
985+
__failure __msg("R{{[0-9]+}} invalid mem access 'scalar'")
986986
int dynptr_invalidate_slice_failure(void *ctx)
987987
{
988988
struct bpf_dynptr ptr1;
@@ -1069,7 +1069,7 @@ int dynptr_read_into_slot(void *ctx)
10691069

10701070
/* bpf_dynptr_slice()s are read-only and cannot be written to */
10711071
SEC("?tc")
1072-
__failure __regex("R[0-9]+ cannot write into rdonly_mem")
1072+
__failure __msg("R{{[0-9]+}} cannot write into rdonly_mem")
10731073
int skb_invalid_slice_write(struct __sk_buff *skb)
10741074
{
10751075
struct bpf_dynptr ptr;

tools/testing/selftests/bpf/progs/rbtree_fail.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ long rbtree_api_remove_unadded_node(void *ctx)
105105
}
106106

107107
SEC("?tc")
108-
__failure __regex("Unreleased reference id=3 alloc_insn=[0-9]+")
108+
__failure __msg("Unreleased reference id=3 alloc_insn={{[0-9]+}}")
109109
long rbtree_api_remove_no_drop(void *ctx)
110110
{
111111
struct bpf_rb_node *res;

tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ static bool less(struct bpf_rb_node *a, const struct bpf_rb_node *b)
3232
}
3333

3434
SEC("?tc")
35-
__failure __regex("Unreleased reference id=4 alloc_insn=[0-9]+")
35+
__failure __msg("Unreleased reference id=4 alloc_insn={{[0-9]+}}")
3636
long rbtree_refcounted_node_ref_escapes(void *ctx)
3737
{
3838
struct node_acquire *n, *m;
@@ -73,7 +73,7 @@ long refcount_acquire_maybe_null(void *ctx)
7373
}
7474

7575
SEC("?tc")
76-
__failure __regex("Unreleased reference id=3 alloc_insn=[0-9]+")
76+
__failure __msg("Unreleased reference id=3 alloc_insn={{[0-9]+}}")
7777
long rbtree_refcounted_node_ref_escapes_owning_input(void *ctx)
7878
{
7979
struct node_acquire *n, *m;

tools/testing/selftests/bpf/test_loader.c

Lines changed: 104 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,10 @@
1919
#define TEST_TAG_EXPECT_FAILURE "comment:test_expect_failure"
2020
#define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success"
2121
#define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg="
22-
#define TEST_TAG_EXPECT_REGEX_PFX "comment:test_expect_regex="
2322
#define TEST_TAG_EXPECT_XLATED_PFX "comment:test_expect_xlated="
2423
#define TEST_TAG_EXPECT_FAILURE_UNPRIV "comment:test_expect_failure_unpriv"
2524
#define TEST_TAG_EXPECT_SUCCESS_UNPRIV "comment:test_expect_success_unpriv"
2625
#define TEST_TAG_EXPECT_MSG_PFX_UNPRIV "comment:test_expect_msg_unpriv="
27-
#define TEST_TAG_EXPECT_REGEX_PFX_UNPRIV "comment:test_expect_regex_unpriv="
2826
#define TEST_TAG_EXPECT_XLATED_PFX_UNPRIV "comment:test_expect_xlated_unpriv="
2927
#define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level="
3028
#define TEST_TAG_PROG_FLAGS_PFX "comment:test_prog_flags="
@@ -55,8 +53,9 @@ enum mode {
5553

5654
struct expect_msg {
5755
const char *substr; /* substring match */
58-
const char *regex_str; /* regex-based match */
5956
regex_t regex;
57+
bool is_regex;
58+
bool on_next_line;
6059
};
6160

6261
struct expected_msgs {
@@ -111,7 +110,7 @@ static void free_msgs(struct expected_msgs *msgs)
111110
int i;
112111

113112
for (i = 0; i < msgs->cnt; i++)
114-
if (msgs->patterns[i].regex_str)
113+
if (msgs->patterns[i].is_regex)
115114
regfree(&msgs->patterns[i].regex);
116115
free(msgs->patterns);
117116
msgs->patterns = NULL;
@@ -132,12 +131,71 @@ static void free_test_spec(struct test_spec *spec)
132131
spec->unpriv.name = NULL;
133132
}
134133

135-
static int push_msg(const char *substr, const char *regex_str, struct expected_msgs *msgs)
134+
/* Compiles regular expression matching pattern.
135+
* Pattern has a special syntax:
136+
*
137+
* pattern := (<verbatim text> | regex)*
138+
* regex := "{{" <posix extended regular expression> "}}"
139+
*
140+
* In other words, pattern is a verbatim text with inclusion
141+
* of regular expressions enclosed in "{{" "}}" pairs.
142+
* For example, pattern "foo{{[0-9]+}}" matches strings like
143+
* "foo0", "foo007", etc.
144+
*/
145+
static int compile_regex(const char *pattern, regex_t *regex)
146+
{
147+
char err_buf[256], buf[256] = {}, *ptr, *buf_end;
148+
const char *original_pattern = pattern;
149+
bool in_regex = false;
150+
int err;
151+
152+
buf_end = buf + sizeof(buf);
153+
ptr = buf;
154+
while (*pattern && ptr < buf_end - 2) {
155+
if (!in_regex && str_has_pfx(pattern, "{{")) {
156+
in_regex = true;
157+
pattern += 2;
158+
continue;
159+
}
160+
if (in_regex && str_has_pfx(pattern, "}}")) {
161+
in_regex = false;
162+
pattern += 2;
163+
continue;
164+
}
165+
if (in_regex) {
166+
*ptr++ = *pattern++;
167+
continue;
168+
}
169+
/* list of characters that need escaping for extended posix regex */
170+
if (strchr(".[]\\()*+?{}|^$", *pattern)) {
171+
*ptr++ = '\\';
172+
*ptr++ = *pattern++;
173+
continue;
174+
}
175+
*ptr++ = *pattern++;
176+
}
177+
if (*pattern) {
178+
PRINT_FAIL("Regexp too long: '%s'\n", original_pattern);
179+
return -EINVAL;
180+
}
181+
if (in_regex) {
182+
PRINT_FAIL("Regexp has open '{{' but no closing '}}': '%s'\n", original_pattern);
183+
return -EINVAL;
184+
}
185+
err = regcomp(regex, buf, REG_EXTENDED | REG_NEWLINE);
186+
if (err != 0) {
187+
regerror(err, regex, err_buf, sizeof(err_buf));
188+
PRINT_FAIL("Regexp compilation error in '%s': '%s'\n", buf, err_buf);
189+
return -EINVAL;
190+
}
191+
return 0;
192+
}
193+
194+
static int __push_msg(const char *pattern, bool on_next_line, struct expected_msgs *msgs)
136195
{
137-
void *tmp;
138-
int regcomp_res;
139-
char error_msg[100];
140196
struct expect_msg *msg;
197+
void *tmp;
198+
int err;
141199

142200
tmp = realloc(msgs->patterns,
143201
(1 + msgs->cnt) * sizeof(struct expect_msg));
@@ -147,26 +205,38 @@ static int push_msg(const char *substr, const char *regex_str, struct expected_m
147205
}
148206
msgs->patterns = tmp;
149207
msg = &msgs->patterns[msgs->cnt];
150-
151-
if (substr) {
152-
msg->substr = substr;
153-
msg->regex_str = NULL;
154-
} else {
155-
msg->regex_str = regex_str;
156-
msg->substr = NULL;
157-
regcomp_res = regcomp(&msg->regex, regex_str, REG_EXTENDED|REG_NEWLINE);
158-
if (regcomp_res != 0) {
159-
regerror(regcomp_res, &msg->regex, error_msg, sizeof(error_msg));
160-
PRINT_FAIL("Regexp compilation error in '%s': '%s'\n",
161-
regex_str, error_msg);
162-
return -EINVAL;
163-
}
208+
msg->on_next_line = on_next_line;
209+
msg->substr = pattern;
210+
msg->is_regex = false;
211+
if (strstr(pattern, "{{")) {
212+
err = compile_regex(pattern, &msg->regex);
213+
if (err)
214+
return err;
215+
msg->is_regex = true;
164216
}
165-
166217
msgs->cnt += 1;
167218
return 0;
168219
}
169220

221+
static int clone_msgs(struct expected_msgs *from, struct expected_msgs *to)
222+
{
223+
struct expect_msg *msg;
224+
int i, err;
225+
226+
for (i = 0; i < from->cnt; i++) {
227+
msg = &from->patterns[i];
228+
err = __push_msg(msg->substr, msg->on_next_line, to);
229+
if (err)
230+
return err;
231+
}
232+
return 0;
233+
}
234+
235+
static int push_msg(const char *substr, struct expected_msgs *msgs)
236+
{
237+
return __push_msg(substr, false, msgs);
238+
}
239+
170240
static int parse_int(const char *str, int *val, const char *name)
171241
{
172242
char *end;
@@ -320,32 +390,22 @@ static int parse_test_spec(struct test_loader *tester,
320390
spec->auxiliary = true;
321391
spec->mode_mask |= UNPRIV;
322392
} else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_MSG_PFX))) {
323-
err = push_msg(msg, NULL, &spec->priv.expect_msgs);
393+
err = push_msg(msg, &spec->priv.expect_msgs);
324394
if (err)
325395
goto cleanup;
326396
spec->mode_mask |= PRIV;
327397
} else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_MSG_PFX_UNPRIV))) {
328-
err = push_msg(msg, NULL, &spec->unpriv.expect_msgs);
329-
if (err)
330-
goto cleanup;
331-
spec->mode_mask |= UNPRIV;
332-
} else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_REGEX_PFX))) {
333-
err = push_msg(NULL, msg, &spec->priv.expect_msgs);
334-
if (err)
335-
goto cleanup;
336-
spec->mode_mask |= PRIV;
337-
} else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_REGEX_PFX_UNPRIV))) {
338-
err = push_msg(NULL, msg, &spec->unpriv.expect_msgs);
398+
err = push_msg(msg, &spec->unpriv.expect_msgs);
339399
if (err)
340400
goto cleanup;
341401
spec->mode_mask |= UNPRIV;
342402
} else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_XLATED_PFX))) {
343-
err = push_msg(msg, NULL, &spec->priv.expect_xlated);
403+
err = push_msg(msg, &spec->priv.expect_xlated);
344404
if (err)
345405
goto cleanup;
346406
spec->mode_mask |= PRIV;
347407
} else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_XLATED_PFX_UNPRIV))) {
348-
err = push_msg(msg, NULL, &spec->unpriv.expect_xlated);
408+
err = push_msg(msg, &spec->unpriv.expect_xlated);
349409
if (err)
350410
goto cleanup;
351411
spec->mode_mask |= UNPRIV;
@@ -457,26 +517,10 @@ static int parse_test_spec(struct test_loader *tester,
457517
spec->unpriv.execute = spec->priv.execute;
458518
}
459519

460-
if (spec->unpriv.expect_msgs.cnt == 0) {
461-
for (i = 0; i < spec->priv.expect_msgs.cnt; i++) {
462-
struct expect_msg *msg = &spec->priv.expect_msgs.patterns[i];
463-
464-
err = push_msg(msg->substr, msg->regex_str,
465-
&spec->unpriv.expect_msgs);
466-
if (err)
467-
goto cleanup;
468-
}
469-
}
470-
if (spec->unpriv.expect_xlated.cnt == 0) {
471-
for (i = 0; i < spec->priv.expect_xlated.cnt; i++) {
472-
struct expect_msg *msg = &spec->priv.expect_xlated.patterns[i];
473-
474-
err = push_msg(msg->substr, msg->regex_str,
475-
&spec->unpriv.expect_xlated);
476-
if (err)
477-
goto cleanup;
478-
}
479-
}
520+
if (spec->unpriv.expect_msgs.cnt == 0)
521+
clone_msgs(&spec->priv.expect_msgs, &spec->unpriv.expect_msgs);
522+
if (spec->unpriv.expect_xlated.cnt == 0)
523+
clone_msgs(&spec->priv.expect_xlated, &spec->unpriv.expect_xlated);
480524
}
481525

482526
spec->valid = true;
@@ -542,7 +586,7 @@ static void validate_msgs(char *log_buf, struct expected_msgs *msgs,
542586
struct expect_msg *msg = &msgs->patterns[i];
543587
const char *match = NULL;
544588

545-
if (msg->substr) {
589+
if (!msg->is_regex) {
546590
match = strstr(log, msg->substr);
547591
if (match)
548592
log = match + strlen(msg->substr);
@@ -562,8 +606,8 @@ static void validate_msgs(char *log_buf, struct expected_msgs *msgs,
562606
msg = &msgs->patterns[j];
563607
fprintf(stderr, "%s %s: '%s'\n",
564608
j < i ? "MATCHED " : "EXPECTED",
565-
msg->substr ? "SUBSTR" : " REGEX",
566-
msg->substr ?: msg->regex_str);
609+
msg->is_regex ? " REGEX" : "SUBSTR",
610+
msg->substr);
567611
}
568612
return;
569613
}

0 commit comments

Comments
 (0)