From cdd54fe98c00549264a92613af6bb0e9a5fd0d1c Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Sat, 11 Apr 2026 00:33:44 -0700 Subject: [PATCH 1/4] selftests/bpf: fix __jited_unpriv tag name __jited_unpriv was using "test_jited=" as its tag name, same as the priv variant __jited. Fix by using "test_jited_unpriv=". Fixes: 7d743e4c759c ("selftests/bpf: __jited test tag to check disassembly after jit") Acked-by: Ihor Solodrai Reviewed-by: Puranjay Mohan Signed-off-by: Eduard Zingerman Link: https://lore.kernel.org/r/20260410-selftests-global-tags-ordering-v2-1-c566ec9781bf@gmail.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/progs/bpf_misc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h index 4b313374c5f1..7e99fe8555e0 100644 --- a/tools/testing/selftests/bpf/progs/bpf_misc.h +++ b/tools/testing/selftests/bpf/progs/bpf_misc.h @@ -140,7 +140,7 @@ #define __msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_msg_unpriv=" XSTR(__COUNTER__) "=" msg))) #define __not_msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_not_msg_unpriv=" XSTR(__COUNTER__) "=" msg))) #define __xlated_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated_unpriv=" XSTR(__COUNTER__) "=" msg))) -#define __jited_unpriv(msg) __attribute__((btf_decl_tag("comment:test_jited=" XSTR(__COUNTER__) "=" msg))) +#define __jited_unpriv(msg) __attribute__((btf_decl_tag("comment:test_jited_unpriv=" XSTR(__COUNTER__) "=" msg))) #define __failure_unpriv __attribute__((btf_decl_tag("comment:test_expect_failure_unpriv"))) #define __success_unpriv __attribute__((btf_decl_tag("comment:test_expect_success_unpriv"))) #define __log_level(lvl) __attribute__((btf_decl_tag("comment:test_log_level="#lvl))) From 5160e584c3616e744252d0a571264f9745bc6e11 Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Sat, 11 Apr 2026 00:33:45 -0700 Subject: [PATCH 2/4] selftests/bpf: make str_has_pfx return pointer past the prefix Change str_has_pfx() to return a pointer to the first character after the prefix, thus eliminating the repetitive (s + sizeof(PFX) - 1) patterns. Acked-by: Ihor Solodrai Acked-by: Mykyta Yatsenko Reviewed-by: Puranjay Mohan Signed-off-by: Eduard Zingerman Link: https://lore.kernel.org/r/20260410-selftests-global-tags-ordering-v2-2-c566ec9781bf@gmail.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/test_loader.c | 51 ++++++++++------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c index 96ed70e01fe5..fa6147607c4d 100644 --- a/tools/testing/selftests/bpf/test_loader.c +++ b/tools/testing/selftests/bpf/test_loader.c @@ -11,8 +11,12 @@ #include "cap_helpers.h" #include "jit_disasm_helpers.h" -#define str_has_pfx(str, pfx) \ - (strncmp(str, pfx, __builtin_constant_p(pfx) ? sizeof(pfx) - 1 : strlen(pfx)) == 0) +static inline const char *str_has_pfx(const char *str, const char *pfx) +{ + size_t len = strlen(pfx); + + return strncmp(str, pfx, len) == 0 ? str + len : NULL; +} #define TEST_LOADER_LOG_BUF_SZ 2097152 @@ -166,21 +170,21 @@ static void free_test_spec(struct test_spec *spec) static int compile_regex(const char *pattern, regex_t *regex) { char err_buf[256], buf[256] = {}, *ptr, *buf_end; - const char *original_pattern = pattern; + const char *original_pattern = pattern, *next; bool in_regex = false; int err; buf_end = buf + sizeof(buf); ptr = buf; while (*pattern && ptr < buf_end - 2) { - if (!in_regex && str_has_pfx(pattern, "{{")) { + if (!in_regex && (next = str_has_pfx(pattern, "{{"))) { in_regex = true; - pattern += 2; + pattern = next; continue; } - if (in_regex && str_has_pfx(pattern, "}}")) { + if (in_regex && (next = str_has_pfx(pattern, "}}"))) { in_regex = false; - pattern += 2; + pattern = next; continue; } if (in_regex) { @@ -457,8 +461,8 @@ static int parse_test_spec(struct test_loader *tester, continue; s = btf__str_by_offset(btf, t->name_off); - if (str_has_pfx(s, TEST_TAG_DESCRIPTION_PFX)) { - description = s + sizeof(TEST_TAG_DESCRIPTION_PFX) - 1; + if ((val = str_has_pfx(s, TEST_TAG_DESCRIPTION_PFX))) { + description = val; } else if (strcmp(s, TEST_TAG_EXPECT_FAILURE) == 0) { spec->priv.expect_failure = true; spec->mode_mask |= PRIV; @@ -535,29 +539,24 @@ static int parse_test_spec(struct test_loader *tester, if (err) goto cleanup; spec->mode_mask |= UNPRIV; - } else if (str_has_pfx(s, TEST_TAG_RETVAL_PFX)) { - val = s + sizeof(TEST_TAG_RETVAL_PFX) - 1; + } else if ((val = str_has_pfx(s, TEST_TAG_RETVAL_PFX))) { err = parse_retval(val, &spec->priv.retval, "__retval"); if (err) goto cleanup; spec->priv.execute = true; spec->mode_mask |= PRIV; - } else if (str_has_pfx(s, TEST_TAG_RETVAL_PFX_UNPRIV)) { - val = s + sizeof(TEST_TAG_RETVAL_PFX_UNPRIV) - 1; + } else if ((val = str_has_pfx(s, TEST_TAG_RETVAL_PFX_UNPRIV))) { err = parse_retval(val, &spec->unpriv.retval, "__retval_unpriv"); if (err) goto cleanup; spec->mode_mask |= UNPRIV; spec->unpriv.execute = true; has_unpriv_retval = true; - } else if (str_has_pfx(s, TEST_TAG_LOG_LEVEL_PFX)) { - val = s + sizeof(TEST_TAG_LOG_LEVEL_PFX) - 1; + } else if ((val = str_has_pfx(s, TEST_TAG_LOG_LEVEL_PFX))) { err = parse_int(val, &spec->log_level, "test log level"); if (err) goto cleanup; - } else if (str_has_pfx(s, TEST_TAG_PROG_FLAGS_PFX)) { - val = s + sizeof(TEST_TAG_PROG_FLAGS_PFX) - 1; - + } else if ((val = str_has_pfx(s, TEST_TAG_PROG_FLAGS_PFX))) { clear = val[0] == '!'; if (clear) val++; @@ -582,8 +581,7 @@ static int parse_test_spec(struct test_loader *tester, goto cleanup; update_flags(&spec->prog_flags, flags, clear); } - } else if (str_has_pfx(s, TEST_TAG_ARCH)) { - val = s + sizeof(TEST_TAG_ARCH) - 1; + } else if ((val = str_has_pfx(s, TEST_TAG_ARCH))) { if (strcmp(val, "X86_64") == 0) { arch = ARCH_X86_64; } else if (strcmp(val, "ARM64") == 0) { @@ -601,16 +599,14 @@ static int parse_test_spec(struct test_loader *tester, collect_jit = get_current_arch() == arch; unpriv_jit_on_next_line = true; jit_on_next_line = true; - } else if (str_has_pfx(s, TEST_BTF_PATH)) { - spec->btf_custom_path = s + sizeof(TEST_BTF_PATH) - 1; - } else if (str_has_pfx(s, TEST_TAG_CAPS_UNPRIV)) { - val = s + sizeof(TEST_TAG_CAPS_UNPRIV) - 1; + } else if ((val = str_has_pfx(s, TEST_BTF_PATH))) { + spec->btf_custom_path = val; + } else if ((val = str_has_pfx(s, TEST_TAG_CAPS_UNPRIV))) { err = parse_caps(val, &spec->unpriv.caps, "test caps"); if (err) goto cleanup; spec->mode_mask |= UNPRIV; - } else if (str_has_pfx(s, TEST_TAG_LOAD_MODE_PFX)) { - val = s + sizeof(TEST_TAG_LOAD_MODE_PFX) - 1; + } else if ((val = str_has_pfx(s, TEST_TAG_LOAD_MODE_PFX))) { if (strcmp(val, "jited") == 0) { load_mask = JITED; } else if (strcmp(val, "no_jited") == 0) { @@ -640,12 +636,11 @@ static int parse_test_spec(struct test_loader *tester, &spec->unpriv.stdout); if (err) goto cleanup; - } else if (str_has_pfx(s, TEST_TAG_LINEAR_SIZE)) { + } else if ((val = str_has_pfx(s, TEST_TAG_LINEAR_SIZE))) { switch (bpf_program__type(prog)) { case BPF_PROG_TYPE_SCHED_ACT: case BPF_PROG_TYPE_SCHED_CLS: case BPF_PROG_TYPE_CGROUP_SKB: - val = s + sizeof(TEST_TAG_LINEAR_SIZE) - 1; err = parse_int(val, &spec->linear_sz, "test linear size"); if (err) goto cleanup; From 713db9fd0336e8fade7e776ab807c21fd8b7a0f1 Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Sat, 11 Apr 2026 00:33:46 -0700 Subject: [PATCH 3/4] selftests/bpf: impose global ordering for test decl_tags Impose global ordering for all decl tags used by test_loader.c based tests (__success, __failure, __msg, etc): - change every tag to expand as __attribute__((btf_decl_tag("comment:" XSTR(__COUNTER__) ...))) - change parse_test_spec() to collect all decl tags before processing and sort them using strverscmp(). The ordering is necessary for gcc-bpf. Neither GCC nor the C standard defines the order in which function attributes are consumed. While Clang tends to preserve definition order, GCC may process them out of sequence. This inconsistency causes BPF tests with multiple __msg entries to fail when compiled with GCC. Signed-off-by: Cupertino Miranda Reviewed-by: Puranjay Mohan Signed-off-by: Eduard Zingerman Link: https://lore.kernel.org/r/20260410-selftests-global-tags-ordering-v2-3-c566ec9781bf@gmail.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/progs/bpf_misc.h | 60 +++---- tools/testing/selftests/bpf/test_loader.c | 167 +++++++++++-------- 2 files changed, 124 insertions(+), 103 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h index 7e99fe8555e0..dcd78a3a9052 100644 --- a/tools/testing/selftests/bpf/progs/bpf_misc.h +++ b/tools/testing/selftests/bpf/progs/bpf_misc.h @@ -130,39 +130,41 @@ * __linear_size Specify the size of the linear area of non-linear skbs, or * 0 for linear skbs. */ -#define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" XSTR(__COUNTER__) "=" msg))) -#define __not_msg(msg) __attribute__((btf_decl_tag("comment:test_expect_not_msg=" XSTR(__COUNTER__) "=" msg))) -#define __xlated(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated=" XSTR(__COUNTER__) "=" msg))) -#define __jited(msg) __attribute__((btf_decl_tag("comment:test_jited=" XSTR(__COUNTER__) "=" msg))) -#define __failure __attribute__((btf_decl_tag("comment:test_expect_failure"))) -#define __success __attribute__((btf_decl_tag("comment:test_expect_success"))) -#define __description(desc) __attribute__((btf_decl_tag("comment:test_description=" desc))) -#define __msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_msg_unpriv=" XSTR(__COUNTER__) "=" msg))) -#define __not_msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_not_msg_unpriv=" XSTR(__COUNTER__) "=" msg))) -#define __xlated_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated_unpriv=" XSTR(__COUNTER__) "=" msg))) -#define __jited_unpriv(msg) __attribute__((btf_decl_tag("comment:test_jited_unpriv=" XSTR(__COUNTER__) "=" msg))) -#define __failure_unpriv __attribute__((btf_decl_tag("comment:test_expect_failure_unpriv"))) -#define __success_unpriv __attribute__((btf_decl_tag("comment:test_expect_success_unpriv"))) -#define __log_level(lvl) __attribute__((btf_decl_tag("comment:test_log_level="#lvl))) -#define __flag(flag) __attribute__((btf_decl_tag("comment:test_prog_flags="#flag))) -#define __retval(val) __attribute__((btf_decl_tag("comment:test_retval="XSTR(val)))) -#define __retval_unpriv(val) __attribute__((btf_decl_tag("comment:test_retval_unpriv="XSTR(val)))) -#define __auxiliary __attribute__((btf_decl_tag("comment:test_auxiliary"))) -#define __auxiliary_unpriv __attribute__((btf_decl_tag("comment:test_auxiliary_unpriv"))) -#define __btf_path(path) __attribute__((btf_decl_tag("comment:test_btf_path=" path))) -#define __arch(arch) __attribute__((btf_decl_tag("comment:test_arch=" arch))) +#define __test_tag(tag) __attribute__((btf_decl_tag("comment:" XSTR(__COUNTER__) ":" tag))) + +#define __msg(msg) __test_tag("test_expect_msg=" msg) +#define __not_msg(msg) __test_tag("test_expect_not_msg=" msg) +#define __xlated(msg) __test_tag("test_expect_xlated=" msg) +#define __jited(msg) __test_tag("test_jited=" msg) +#define __failure __test_tag("test_expect_failure") +#define __success __test_tag("test_expect_success") +#define __description(desc) __test_tag("test_description=" desc) +#define __msg_unpriv(msg) __test_tag("test_expect_msg_unpriv=" msg) +#define __not_msg_unpriv(msg) __test_tag("test_expect_not_msg_unpriv=" msg) +#define __xlated_unpriv(msg) __test_tag("test_expect_xlated_unpriv=" msg) +#define __jited_unpriv(msg) __test_tag("test_jited_unpriv=" msg) +#define __failure_unpriv __test_tag("test_expect_failure_unpriv") +#define __success_unpriv __test_tag("test_expect_success_unpriv") +#define __log_level(lvl) __test_tag("test_log_level=" #lvl) +#define __flag(flag) __test_tag("test_prog_flags=" #flag) +#define __retval(val) __test_tag("test_retval=" XSTR(val)) +#define __retval_unpriv(val) __test_tag("test_retval_unpriv=" XSTR(val)) +#define __auxiliary __test_tag("test_auxiliary") +#define __auxiliary_unpriv __test_tag("test_auxiliary_unpriv") +#define __btf_path(path) __test_tag("test_btf_path=" path) +#define __arch(arch) __test_tag("test_arch=" arch) #define __arch_x86_64 __arch("X86_64") #define __arch_arm64 __arch("ARM64") #define __arch_riscv64 __arch("RISCV64") #define __arch_s390x __arch("s390x") -#define __caps_unpriv(caps) __attribute__((btf_decl_tag("comment:test_caps_unpriv=" EXPAND_QUOTE(caps)))) -#define __load_if_JITed() __attribute__((btf_decl_tag("comment:load_mode=jited"))) -#define __load_if_no_JITed() __attribute__((btf_decl_tag("comment:load_mode=no_jited"))) -#define __stderr(msg) __attribute__((btf_decl_tag("comment:test_expect_stderr=" XSTR(__COUNTER__) "=" msg))) -#define __stderr_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_stderr_unpriv=" XSTR(__COUNTER__) "=" msg))) -#define __stdout(msg) __attribute__((btf_decl_tag("comment:test_expect_stdout=" XSTR(__COUNTER__) "=" msg))) -#define __stdout_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_stdout_unpriv=" XSTR(__COUNTER__) "=" msg))) -#define __linear_size(sz) __attribute__((btf_decl_tag("comment:test_linear_size=" XSTR(sz)))) +#define __caps_unpriv(caps) __test_tag("test_caps_unpriv=" EXPAND_QUOTE(caps)) +#define __load_if_JITed() __test_tag("load_mode=jited") +#define __load_if_no_JITed() __test_tag("load_mode=no_jited") +#define __stderr(msg) __test_tag("test_expect_stderr=" msg) +#define __stderr_unpriv(msg) __test_tag("test_expect_stderr_unpriv=" msg) +#define __stdout(msg) __test_tag("test_expect_stdout=" msg) +#define __stdout_unpriv(msg) __test_tag("test_expect_stdout_unpriv=" msg) +#define __linear_size(sz) __test_tag("test_linear_size=" XSTR(sz)) /* Define common capabilities tested using __caps_unpriv */ #define CAP_NET_ADMIN 12 diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c index fa6147607c4d..066898d0c123 100644 --- a/tools/testing/selftests/bpf/test_loader.c +++ b/tools/testing/selftests/bpf/test_loader.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ #include +#include #include #include #include @@ -20,34 +21,34 @@ static inline const char *str_has_pfx(const char *str, const char *pfx) #define TEST_LOADER_LOG_BUF_SZ 2097152 -#define TEST_TAG_EXPECT_FAILURE "comment:test_expect_failure" -#define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success" -#define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg=" -#define TEST_TAG_EXPECT_NOT_MSG_PFX "comment:test_expect_not_msg=" -#define TEST_TAG_EXPECT_XLATED_PFX "comment:test_expect_xlated=" -#define TEST_TAG_EXPECT_FAILURE_UNPRIV "comment:test_expect_failure_unpriv" -#define TEST_TAG_EXPECT_SUCCESS_UNPRIV "comment:test_expect_success_unpriv" -#define TEST_TAG_EXPECT_MSG_PFX_UNPRIV "comment:test_expect_msg_unpriv=" -#define TEST_TAG_EXPECT_NOT_MSG_PFX_UNPRIV "comment:test_expect_not_msg_unpriv=" -#define TEST_TAG_EXPECT_XLATED_PFX_UNPRIV "comment:test_expect_xlated_unpriv=" -#define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level=" -#define TEST_TAG_PROG_FLAGS_PFX "comment:test_prog_flags=" -#define TEST_TAG_DESCRIPTION_PFX "comment:test_description=" -#define TEST_TAG_RETVAL_PFX "comment:test_retval=" -#define TEST_TAG_RETVAL_PFX_UNPRIV "comment:test_retval_unpriv=" -#define TEST_TAG_AUXILIARY "comment:test_auxiliary" -#define TEST_TAG_AUXILIARY_UNPRIV "comment:test_auxiliary_unpriv" -#define TEST_BTF_PATH "comment:test_btf_path=" -#define TEST_TAG_ARCH "comment:test_arch=" -#define TEST_TAG_JITED_PFX "comment:test_jited=" -#define TEST_TAG_JITED_PFX_UNPRIV "comment:test_jited_unpriv=" -#define TEST_TAG_CAPS_UNPRIV "comment:test_caps_unpriv=" -#define TEST_TAG_LOAD_MODE_PFX "comment:load_mode=" -#define TEST_TAG_EXPECT_STDERR_PFX "comment:test_expect_stderr=" -#define TEST_TAG_EXPECT_STDERR_PFX_UNPRIV "comment:test_expect_stderr_unpriv=" -#define TEST_TAG_EXPECT_STDOUT_PFX "comment:test_expect_stdout=" -#define TEST_TAG_EXPECT_STDOUT_PFX_UNPRIV "comment:test_expect_stdout_unpriv=" -#define TEST_TAG_LINEAR_SIZE "comment:test_linear_size=" +#define TEST_TAG_EXPECT_FAILURE "test_expect_failure" +#define TEST_TAG_EXPECT_SUCCESS "test_expect_success" +#define TEST_TAG_EXPECT_MSG_PFX "test_expect_msg=" +#define TEST_TAG_EXPECT_NOT_MSG_PFX "test_expect_not_msg=" +#define TEST_TAG_EXPECT_XLATED_PFX "test_expect_xlated=" +#define TEST_TAG_EXPECT_FAILURE_UNPRIV "test_expect_failure_unpriv" +#define TEST_TAG_EXPECT_SUCCESS_UNPRIV "test_expect_success_unpriv" +#define TEST_TAG_EXPECT_MSG_PFX_UNPRIV "test_expect_msg_unpriv=" +#define TEST_TAG_EXPECT_NOT_MSG_PFX_UNPRIV "test_expect_not_msg_unpriv=" +#define TEST_TAG_EXPECT_XLATED_PFX_UNPRIV "test_expect_xlated_unpriv=" +#define TEST_TAG_LOG_LEVEL_PFX "test_log_level=" +#define TEST_TAG_PROG_FLAGS_PFX "test_prog_flags=" +#define TEST_TAG_DESCRIPTION_PFX "test_description=" +#define TEST_TAG_RETVAL_PFX "test_retval=" +#define TEST_TAG_RETVAL_PFX_UNPRIV "test_retval_unpriv=" +#define TEST_TAG_AUXILIARY "test_auxiliary" +#define TEST_TAG_AUXILIARY_UNPRIV "test_auxiliary_unpriv" +#define TEST_BTF_PATH "test_btf_path=" +#define TEST_TAG_ARCH "test_arch=" +#define TEST_TAG_JITED_PFX "test_jited=" +#define TEST_TAG_JITED_PFX_UNPRIV "test_jited_unpriv=" +#define TEST_TAG_CAPS_UNPRIV "test_caps_unpriv=" +#define TEST_TAG_LOAD_MODE_PFX "load_mode=" +#define TEST_TAG_EXPECT_STDERR_PFX "test_expect_stderr=" +#define TEST_TAG_EXPECT_STDERR_PFX_UNPRIV "test_expect_stderr_unpriv=" +#define TEST_TAG_EXPECT_STDOUT_PFX "test_expect_stdout=" +#define TEST_TAG_EXPECT_STDOUT_PFX_UNPRIV "test_expect_stdout_unpriv=" +#define TEST_TAG_LINEAR_SIZE "test_linear_size=" /* Warning: duplicated in bpf_misc.h */ #define POINTER_VALUE 0xbadcafe @@ -352,33 +353,49 @@ static void update_flags(int *flags, int flag, bool clear) *flags |= flag; } -/* Matches a string of form '[^=]=.*' and returns it's suffix. - * Used to parse btf_decl_tag values. - * Such values require unique prefix because compiler does not add - * same __attribute__((btf_decl_tag(...))) twice. - * Test suite uses two-component tags for such cases: - * - * __COUNTER__ '=' - * - * For example, two consecutive __msg tags '__msg("foo") __msg("foo")' - * would be encoded as: - * - * [18] DECL_TAG 'comment:test_expect_msg=0=foo' type_id=15 component_idx=-1 - * [19] DECL_TAG 'comment:test_expect_msg=1=foo' type_id=15 component_idx=-1 - * - * And the purpose of this function is to extract 'foo' from the above. - */ -static const char *skip_dynamic_pfx(const char *s, const char *pfx) +static const char *skip_decl_tag_pfx(const char *s) { - const char *msg; + int n = 0; - if (strncmp(s, pfx, strlen(pfx)) != 0) + if (sscanf(s, "comment:%*d:%n", &n) < 0 || !n) return NULL; - msg = s + strlen(pfx); - msg = strchr(msg, '='); - if (!msg) - return NULL; - return msg + 1; + return s + n; +} + +static int compare_decl_tags(const void *a, const void *b) +{ + return strverscmp(*(const char **)a, *(const char **)b); +} + +/* + * Compilers don't guarantee order in which BTF attributes would be generated, + * while order is important for test tags like __msg. + * Each test tag has the following prefix: "comment:" __COUNTER__, + * when sorted using strverscmp this gives same order as in the original C code. + */ +static const char **collect_decl_tags(struct btf *btf, int id, int *cnt) +{ + const char **tmp, **tags = NULL; + const struct btf_type *t; + int i; + + *cnt = 0; + for (i = 1; i < btf__type_cnt(btf); i++) { + t = btf__type_by_id(btf, i); + if (!btf_is_decl_tag(t) || t->type != id || btf_decl_tag(t)->component_idx != -1) + continue; + tmp = realloc(tags, (*cnt + 1) * sizeof(*tags)); + if (!tmp) { + free(tags); + return ERR_PTR(-ENOMEM); + } + tags = tmp; + tags[(*cnt)++] = btf__str_by_offset(btf, t->name_off); + } + + if (*cnt) + qsort(tags, *cnt, sizeof(*tags), compare_decl_tags); + return tags; } enum arch { @@ -424,7 +441,9 @@ static int parse_test_spec(struct test_loader *tester, bool stdout_on_next_line = true; bool unpriv_stdout_on_next_line = true; bool collect_jit = false; - int func_id, i, err = 0; + const char **tags = NULL; + int func_id, i, nr_tags; + int err = 0; u32 arch_mask = 0; u32 load_mask = 0; struct btf *btf; @@ -447,20 +466,18 @@ static int parse_test_spec(struct test_loader *tester, return -EINVAL; } - for (i = 1; i < btf__type_cnt(btf); i++) { + tags = collect_decl_tags(btf, func_id, &nr_tags); + if (IS_ERR(tags)) + return PTR_ERR(tags); + + for (i = 0; i < nr_tags; i++) { const char *s, *val, *msg; - const struct btf_type *t; bool clear; int flags; - t = btf__type_by_id(btf, i); - if (!btf_is_decl_tag(t)) + s = skip_decl_tag_pfx(tags[i]); + if (!s) continue; - - if (t->type != func_id || btf_decl_tag(t)->component_idx != -1) - continue; - - s = btf__str_by_offset(btf, t->name_off); if ((val = str_has_pfx(s, TEST_TAG_DESCRIPTION_PFX))) { description = val; } else if (strcmp(s, TEST_TAG_EXPECT_FAILURE) == 0) { @@ -483,27 +500,27 @@ static int parse_test_spec(struct test_loader *tester, } else if (strcmp(s, TEST_TAG_AUXILIARY_UNPRIV) == 0) { spec->auxiliary = true; spec->mode_mask |= UNPRIV; - } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_MSG_PFX))) { + } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX))) { err = push_msg(msg, false, &spec->priv.expect_msgs); if (err) goto cleanup; spec->mode_mask |= PRIV; - } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_NOT_MSG_PFX))) { + } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_NOT_MSG_PFX))) { err = push_msg(msg, true, &spec->priv.expect_msgs); if (err) goto cleanup; spec->mode_mask |= PRIV; - } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_MSG_PFX_UNPRIV))) { + } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX_UNPRIV))) { err = push_msg(msg, false, &spec->unpriv.expect_msgs); if (err) goto cleanup; spec->mode_mask |= UNPRIV; - } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_NOT_MSG_PFX_UNPRIV))) { + } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_NOT_MSG_PFX_UNPRIV))) { err = push_msg(msg, true, &spec->unpriv.expect_msgs); if (err) goto cleanup; spec->mode_mask |= UNPRIV; - } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_JITED_PFX))) { + } else if ((msg = str_has_pfx(s, TEST_TAG_JITED_PFX))) { if (arch_mask == 0) { PRINT_FAIL("__jited used before __arch_*"); goto cleanup; @@ -515,7 +532,7 @@ static int parse_test_spec(struct test_loader *tester, goto cleanup; spec->mode_mask |= PRIV; } - } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_JITED_PFX_UNPRIV))) { + } else if ((msg = str_has_pfx(s, TEST_TAG_JITED_PFX_UNPRIV))) { if (arch_mask == 0) { PRINT_FAIL("__unpriv_jited used before __arch_*"); goto cleanup; @@ -527,13 +544,13 @@ static int parse_test_spec(struct test_loader *tester, goto cleanup; spec->mode_mask |= UNPRIV; } - } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_XLATED_PFX))) { + } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_XLATED_PFX))) { err = push_disasm_msg(msg, &xlated_on_next_line, &spec->priv.expect_xlated); if (err) goto cleanup; spec->mode_mask |= PRIV; - } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_XLATED_PFX_UNPRIV))) { + } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_XLATED_PFX_UNPRIV))) { err = push_disasm_msg(msg, &unpriv_xlated_on_next_line, &spec->unpriv.expect_xlated); if (err) @@ -616,22 +633,22 @@ static int parse_test_spec(struct test_loader *tester, err = -EINVAL; goto cleanup; } - } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_STDERR_PFX))) { + } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_STDERR_PFX))) { err = push_disasm_msg(msg, &stderr_on_next_line, &spec->priv.stderr); if (err) goto cleanup; - } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_STDERR_PFX_UNPRIV))) { + } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_STDERR_PFX_UNPRIV))) { err = push_disasm_msg(msg, &unpriv_stderr_on_next_line, &spec->unpriv.stderr); if (err) goto cleanup; - } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_STDOUT_PFX))) { + } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_STDOUT_PFX))) { err = push_disasm_msg(msg, &stdout_on_next_line, &spec->priv.stdout); if (err) goto cleanup; - } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_STDOUT_PFX_UNPRIV))) { + } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_STDOUT_PFX_UNPRIV))) { err = push_disasm_msg(msg, &unpriv_stdout_on_next_line, &spec->unpriv.stdout); if (err) @@ -734,9 +751,11 @@ static int parse_test_spec(struct test_loader *tester, spec->valid = true; + free(tags); return 0; cleanup: + free(tags); free_test_spec(spec); return err; } From 335a6ca04158505b94afb7271488f5052c46f5e5 Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Sat, 11 Apr 2026 00:33:47 -0700 Subject: [PATCH 4/4] selftests/bpf: inline TEST_TAG constants in test_loader.c After str_has_pfx() refactoring each TEST_TAG_* / TEST_BTF_PATH constant is used exactly once. Since constant definitions are not shared between BPF-side bpf_misc.h and userspace side test_loader.c, there is no need in the additional redirection layer. Acked-by: Ihor Solodrai Reviewed-by: Puranjay Mohan Signed-off-by: Eduard Zingerman Link: https://lore.kernel.org/r/20260410-selftests-global-tags-ordering-v2-4-c566ec9781bf@gmail.com Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/test_loader.c | 84 ++++++++--------------- 1 file changed, 28 insertions(+), 56 deletions(-) diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c index 066898d0c123..c4c34cae6102 100644 --- a/tools/testing/selftests/bpf/test_loader.c +++ b/tools/testing/selftests/bpf/test_loader.c @@ -21,34 +21,6 @@ static inline const char *str_has_pfx(const char *str, const char *pfx) #define TEST_LOADER_LOG_BUF_SZ 2097152 -#define TEST_TAG_EXPECT_FAILURE "test_expect_failure" -#define TEST_TAG_EXPECT_SUCCESS "test_expect_success" -#define TEST_TAG_EXPECT_MSG_PFX "test_expect_msg=" -#define TEST_TAG_EXPECT_NOT_MSG_PFX "test_expect_not_msg=" -#define TEST_TAG_EXPECT_XLATED_PFX "test_expect_xlated=" -#define TEST_TAG_EXPECT_FAILURE_UNPRIV "test_expect_failure_unpriv" -#define TEST_TAG_EXPECT_SUCCESS_UNPRIV "test_expect_success_unpriv" -#define TEST_TAG_EXPECT_MSG_PFX_UNPRIV "test_expect_msg_unpriv=" -#define TEST_TAG_EXPECT_NOT_MSG_PFX_UNPRIV "test_expect_not_msg_unpriv=" -#define TEST_TAG_EXPECT_XLATED_PFX_UNPRIV "test_expect_xlated_unpriv=" -#define TEST_TAG_LOG_LEVEL_PFX "test_log_level=" -#define TEST_TAG_PROG_FLAGS_PFX "test_prog_flags=" -#define TEST_TAG_DESCRIPTION_PFX "test_description=" -#define TEST_TAG_RETVAL_PFX "test_retval=" -#define TEST_TAG_RETVAL_PFX_UNPRIV "test_retval_unpriv=" -#define TEST_TAG_AUXILIARY "test_auxiliary" -#define TEST_TAG_AUXILIARY_UNPRIV "test_auxiliary_unpriv" -#define TEST_BTF_PATH "test_btf_path=" -#define TEST_TAG_ARCH "test_arch=" -#define TEST_TAG_JITED_PFX "test_jited=" -#define TEST_TAG_JITED_PFX_UNPRIV "test_jited_unpriv=" -#define TEST_TAG_CAPS_UNPRIV "test_caps_unpriv=" -#define TEST_TAG_LOAD_MODE_PFX "load_mode=" -#define TEST_TAG_EXPECT_STDERR_PFX "test_expect_stderr=" -#define TEST_TAG_EXPECT_STDERR_PFX_UNPRIV "test_expect_stderr_unpriv=" -#define TEST_TAG_EXPECT_STDOUT_PFX "test_expect_stdout=" -#define TEST_TAG_EXPECT_STDOUT_PFX_UNPRIV "test_expect_stdout_unpriv=" -#define TEST_TAG_LINEAR_SIZE "test_linear_size=" /* Warning: duplicated in bpf_misc.h */ #define POINTER_VALUE 0xbadcafe @@ -478,49 +450,49 @@ static int parse_test_spec(struct test_loader *tester, s = skip_decl_tag_pfx(tags[i]); if (!s) continue; - if ((val = str_has_pfx(s, TEST_TAG_DESCRIPTION_PFX))) { + if ((val = str_has_pfx(s, "test_description="))) { description = val; - } else if (strcmp(s, TEST_TAG_EXPECT_FAILURE) == 0) { + } else if (strcmp(s, "test_expect_failure") == 0) { spec->priv.expect_failure = true; spec->mode_mask |= PRIV; - } else if (strcmp(s, TEST_TAG_EXPECT_SUCCESS) == 0) { + } else if (strcmp(s, "test_expect_success") == 0) { spec->priv.expect_failure = false; spec->mode_mask |= PRIV; - } else if (strcmp(s, TEST_TAG_EXPECT_FAILURE_UNPRIV) == 0) { + } else if (strcmp(s, "test_expect_failure_unpriv") == 0) { spec->unpriv.expect_failure = true; spec->mode_mask |= UNPRIV; has_unpriv_result = true; - } else if (strcmp(s, TEST_TAG_EXPECT_SUCCESS_UNPRIV) == 0) { + } else if (strcmp(s, "test_expect_success_unpriv") == 0) { spec->unpriv.expect_failure = false; spec->mode_mask |= UNPRIV; has_unpriv_result = true; - } else if (strcmp(s, TEST_TAG_AUXILIARY) == 0) { + } else if (strcmp(s, "test_auxiliary") == 0) { spec->auxiliary = true; spec->mode_mask |= PRIV; - } else if (strcmp(s, TEST_TAG_AUXILIARY_UNPRIV) == 0) { + } else if (strcmp(s, "test_auxiliary_unpriv") == 0) { spec->auxiliary = true; spec->mode_mask |= UNPRIV; - } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX))) { + } else if ((msg = str_has_pfx(s, "test_expect_msg="))) { err = push_msg(msg, false, &spec->priv.expect_msgs); if (err) goto cleanup; spec->mode_mask |= PRIV; - } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_NOT_MSG_PFX))) { + } else if ((msg = str_has_pfx(s, "test_expect_not_msg="))) { err = push_msg(msg, true, &spec->priv.expect_msgs); if (err) goto cleanup; spec->mode_mask |= PRIV; - } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX_UNPRIV))) { + } else if ((msg = str_has_pfx(s, "test_expect_msg_unpriv="))) { err = push_msg(msg, false, &spec->unpriv.expect_msgs); if (err) goto cleanup; spec->mode_mask |= UNPRIV; - } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_NOT_MSG_PFX_UNPRIV))) { + } else if ((msg = str_has_pfx(s, "test_expect_not_msg_unpriv="))) { err = push_msg(msg, true, &spec->unpriv.expect_msgs); if (err) goto cleanup; spec->mode_mask |= UNPRIV; - } else if ((msg = str_has_pfx(s, TEST_TAG_JITED_PFX))) { + } else if ((msg = str_has_pfx(s, "test_jited="))) { if (arch_mask == 0) { PRINT_FAIL("__jited used before __arch_*"); goto cleanup; @@ -532,7 +504,7 @@ static int parse_test_spec(struct test_loader *tester, goto cleanup; spec->mode_mask |= PRIV; } - } else if ((msg = str_has_pfx(s, TEST_TAG_JITED_PFX_UNPRIV))) { + } else if ((msg = str_has_pfx(s, "test_jited_unpriv="))) { if (arch_mask == 0) { PRINT_FAIL("__unpriv_jited used before __arch_*"); goto cleanup; @@ -544,36 +516,36 @@ static int parse_test_spec(struct test_loader *tester, goto cleanup; spec->mode_mask |= UNPRIV; } - } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_XLATED_PFX))) { + } else if ((msg = str_has_pfx(s, "test_expect_xlated="))) { err = push_disasm_msg(msg, &xlated_on_next_line, &spec->priv.expect_xlated); if (err) goto cleanup; spec->mode_mask |= PRIV; - } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_XLATED_PFX_UNPRIV))) { + } else if ((msg = str_has_pfx(s, "test_expect_xlated_unpriv="))) { err = push_disasm_msg(msg, &unpriv_xlated_on_next_line, &spec->unpriv.expect_xlated); if (err) goto cleanup; spec->mode_mask |= UNPRIV; - } else if ((val = str_has_pfx(s, TEST_TAG_RETVAL_PFX))) { + } else if ((val = str_has_pfx(s, "test_retval="))) { err = parse_retval(val, &spec->priv.retval, "__retval"); if (err) goto cleanup; spec->priv.execute = true; spec->mode_mask |= PRIV; - } else if ((val = str_has_pfx(s, TEST_TAG_RETVAL_PFX_UNPRIV))) { + } else if ((val = str_has_pfx(s, "test_retval_unpriv="))) { err = parse_retval(val, &spec->unpriv.retval, "__retval_unpriv"); if (err) goto cleanup; spec->mode_mask |= UNPRIV; spec->unpriv.execute = true; has_unpriv_retval = true; - } else if ((val = str_has_pfx(s, TEST_TAG_LOG_LEVEL_PFX))) { + } else if ((val = str_has_pfx(s, "test_log_level="))) { err = parse_int(val, &spec->log_level, "test log level"); if (err) goto cleanup; - } else if ((val = str_has_pfx(s, TEST_TAG_PROG_FLAGS_PFX))) { + } else if ((val = str_has_pfx(s, "test_prog_flags="))) { clear = val[0] == '!'; if (clear) val++; @@ -598,7 +570,7 @@ static int parse_test_spec(struct test_loader *tester, goto cleanup; update_flags(&spec->prog_flags, flags, clear); } - } else if ((val = str_has_pfx(s, TEST_TAG_ARCH))) { + } else if ((val = str_has_pfx(s, "test_arch="))) { if (strcmp(val, "X86_64") == 0) { arch = ARCH_X86_64; } else if (strcmp(val, "ARM64") == 0) { @@ -616,14 +588,14 @@ static int parse_test_spec(struct test_loader *tester, collect_jit = get_current_arch() == arch; unpriv_jit_on_next_line = true; jit_on_next_line = true; - } else if ((val = str_has_pfx(s, TEST_BTF_PATH))) { + } else if ((val = str_has_pfx(s, "test_btf_path="))) { spec->btf_custom_path = val; - } else if ((val = str_has_pfx(s, TEST_TAG_CAPS_UNPRIV))) { + } else if ((val = str_has_pfx(s, "test_caps_unpriv="))) { err = parse_caps(val, &spec->unpriv.caps, "test caps"); if (err) goto cleanup; spec->mode_mask |= UNPRIV; - } else if ((val = str_has_pfx(s, TEST_TAG_LOAD_MODE_PFX))) { + } else if ((val = str_has_pfx(s, "load_mode="))) { if (strcmp(val, "jited") == 0) { load_mask = JITED; } else if (strcmp(val, "no_jited") == 0) { @@ -633,27 +605,27 @@ static int parse_test_spec(struct test_loader *tester, err = -EINVAL; goto cleanup; } - } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_STDERR_PFX))) { + } else if ((msg = str_has_pfx(s, "test_expect_stderr="))) { err = push_disasm_msg(msg, &stderr_on_next_line, &spec->priv.stderr); if (err) goto cleanup; - } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_STDERR_PFX_UNPRIV))) { + } else if ((msg = str_has_pfx(s, "test_expect_stderr_unpriv="))) { err = push_disasm_msg(msg, &unpriv_stderr_on_next_line, &spec->unpriv.stderr); if (err) goto cleanup; - } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_STDOUT_PFX))) { + } else if ((msg = str_has_pfx(s, "test_expect_stdout="))) { err = push_disasm_msg(msg, &stdout_on_next_line, &spec->priv.stdout); if (err) goto cleanup; - } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_STDOUT_PFX_UNPRIV))) { + } else if ((msg = str_has_pfx(s, "test_expect_stdout_unpriv="))) { err = push_disasm_msg(msg, &unpriv_stdout_on_next_line, &spec->unpriv.stdout); if (err) goto cleanup; - } else if ((val = str_has_pfx(s, TEST_TAG_LINEAR_SIZE))) { + } else if ((val = str_has_pfx(s, "test_linear_size="))) { switch (bpf_program__type(prog)) { case BPF_PROG_TYPE_SCHED_ACT: case BPF_PROG_TYPE_SCHED_CLS: