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

selftests/bpf: Add tests for extending sleepable global subprogs

Add tests for freplace behavior with the combination of sleepable
and non-sleepable global subprogs. The changes_pkt_data selftest
did all the hardwork, so simply rename it and include new support
for more summarization tests for might_sleep bit.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20250301151846.1552362-4-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Kumar Kartikeya Dwivedi and committed by
Alexei Starovoitov
72ed076a b2bb7034

+238 -147
-107
tools/testing/selftests/bpf/prog_tests/changes_pkt_data.c
··· 1 - // SPDX-License-Identifier: GPL-2.0 2 - #include "bpf/libbpf.h" 3 - #include "changes_pkt_data_freplace.skel.h" 4 - #include "changes_pkt_data.skel.h" 5 - #include <test_progs.h> 6 - 7 - static void print_verifier_log(const char *log) 8 - { 9 - if (env.verbosity >= VERBOSE_VERY) 10 - fprintf(stdout, "VERIFIER LOG:\n=============\n%s=============\n", log); 11 - } 12 - 13 - static void test_aux(const char *main_prog_name, 14 - const char *to_be_replaced, 15 - const char *replacement, 16 - bool expect_load) 17 - { 18 - struct changes_pkt_data_freplace *freplace = NULL; 19 - struct bpf_program *freplace_prog = NULL; 20 - struct bpf_program *main_prog = NULL; 21 - LIBBPF_OPTS(bpf_object_open_opts, opts); 22 - struct changes_pkt_data *main = NULL; 23 - char log[16*1024]; 24 - int err; 25 - 26 - opts.kernel_log_buf = log; 27 - opts.kernel_log_size = sizeof(log); 28 - if (env.verbosity >= VERBOSE_SUPER) 29 - opts.kernel_log_level = 1 | 2 | 4; 30 - main = changes_pkt_data__open_opts(&opts); 31 - if (!ASSERT_OK_PTR(main, "changes_pkt_data__open")) 32 - goto out; 33 - main_prog = bpf_object__find_program_by_name(main->obj, main_prog_name); 34 - if (!ASSERT_OK_PTR(main_prog, "main_prog")) 35 - goto out; 36 - bpf_program__set_autoload(main_prog, true); 37 - err = changes_pkt_data__load(main); 38 - print_verifier_log(log); 39 - if (!ASSERT_OK(err, "changes_pkt_data__load")) 40 - goto out; 41 - freplace = changes_pkt_data_freplace__open_opts(&opts); 42 - if (!ASSERT_OK_PTR(freplace, "changes_pkt_data_freplace__open")) 43 - goto out; 44 - freplace_prog = bpf_object__find_program_by_name(freplace->obj, replacement); 45 - if (!ASSERT_OK_PTR(freplace_prog, "freplace_prog")) 46 - goto out; 47 - bpf_program__set_autoload(freplace_prog, true); 48 - bpf_program__set_autoattach(freplace_prog, true); 49 - bpf_program__set_attach_target(freplace_prog, 50 - bpf_program__fd(main_prog), 51 - to_be_replaced); 52 - err = changes_pkt_data_freplace__load(freplace); 53 - print_verifier_log(log); 54 - if (expect_load) { 55 - ASSERT_OK(err, "changes_pkt_data_freplace__load"); 56 - } else { 57 - ASSERT_ERR(err, "changes_pkt_data_freplace__load"); 58 - ASSERT_HAS_SUBSTR(log, "Extension program changes packet data", "error log"); 59 - } 60 - 61 - out: 62 - changes_pkt_data_freplace__destroy(freplace); 63 - changes_pkt_data__destroy(main); 64 - } 65 - 66 - /* There are two global subprograms in both changes_pkt_data.skel.h: 67 - * - one changes packet data; 68 - * - another does not. 69 - * It is ok to freplace subprograms that change packet data with those 70 - * that either do or do not. It is only ok to freplace subprograms 71 - * that do not change packet data with those that do not as well. 72 - * The below tests check outcomes for each combination of such freplace. 73 - * Also test a case when main subprogram itself is replaced and is a single 74 - * subprogram in a program. 75 - */ 76 - void test_changes_pkt_data_freplace(void) 77 - { 78 - struct { 79 - const char *main; 80 - const char *to_be_replaced; 81 - bool changes; 82 - } mains[] = { 83 - { "main_with_subprogs", "changes_pkt_data", true }, 84 - { "main_with_subprogs", "does_not_change_pkt_data", false }, 85 - { "main_changes", "main_changes", true }, 86 - { "main_does_not_change", "main_does_not_change", false }, 87 - }; 88 - struct { 89 - const char *func; 90 - bool changes; 91 - } replacements[] = { 92 - { "changes_pkt_data", true }, 93 - { "does_not_change_pkt_data", false } 94 - }; 95 - char buf[64]; 96 - 97 - for (int i = 0; i < ARRAY_SIZE(mains); ++i) { 98 - for (int j = 0; j < ARRAY_SIZE(replacements); ++j) { 99 - snprintf(buf, sizeof(buf), "%s_with_%s", 100 - mains[i].to_be_replaced, replacements[j].func); 101 - if (!test__start_subtest(buf)) 102 - continue; 103 - test_aux(mains[i].main, mains[i].to_be_replaced, replacements[j].func, 104 - mains[i].changes || !replacements[j].changes); 105 - } 106 - } 107 - }
+144
tools/testing/selftests/bpf/prog_tests/summarization.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include "bpf/libbpf.h" 3 + #include "summarization_freplace.skel.h" 4 + #include "summarization.skel.h" 5 + #include <test_progs.h> 6 + 7 + static void print_verifier_log(const char *log) 8 + { 9 + if (env.verbosity >= VERBOSE_VERY) 10 + fprintf(stdout, "VERIFIER LOG:\n=============\n%s=============\n", log); 11 + } 12 + 13 + static void test_aux(const char *main_prog_name, 14 + const char *to_be_replaced, 15 + const char *replacement, 16 + bool expect_load, 17 + const char *err_msg) 18 + { 19 + struct summarization_freplace *freplace = NULL; 20 + struct bpf_program *freplace_prog = NULL; 21 + struct bpf_program *main_prog = NULL; 22 + LIBBPF_OPTS(bpf_object_open_opts, opts); 23 + struct summarization *main = NULL; 24 + char log[16*1024]; 25 + int err; 26 + 27 + opts.kernel_log_buf = log; 28 + opts.kernel_log_size = sizeof(log); 29 + if (env.verbosity >= VERBOSE_SUPER) 30 + opts.kernel_log_level = 1 | 2 | 4; 31 + main = summarization__open_opts(&opts); 32 + if (!ASSERT_OK_PTR(main, "summarization__open")) 33 + goto out; 34 + main_prog = bpf_object__find_program_by_name(main->obj, main_prog_name); 35 + if (!ASSERT_OK_PTR(main_prog, "main_prog")) 36 + goto out; 37 + bpf_program__set_autoload(main_prog, true); 38 + err = summarization__load(main); 39 + print_verifier_log(log); 40 + if (!ASSERT_OK(err, "summarization__load")) 41 + goto out; 42 + freplace = summarization_freplace__open_opts(&opts); 43 + if (!ASSERT_OK_PTR(freplace, "summarization_freplace__open")) 44 + goto out; 45 + freplace_prog = bpf_object__find_program_by_name(freplace->obj, replacement); 46 + if (!ASSERT_OK_PTR(freplace_prog, "freplace_prog")) 47 + goto out; 48 + bpf_program__set_autoload(freplace_prog, true); 49 + bpf_program__set_autoattach(freplace_prog, true); 50 + bpf_program__set_attach_target(freplace_prog, 51 + bpf_program__fd(main_prog), 52 + to_be_replaced); 53 + err = summarization_freplace__load(freplace); 54 + print_verifier_log(log); 55 + 56 + /* The might_sleep extension doesn't work yet as sleepable calls are not 57 + * allowed, but preserve the check in case it's supported later and then 58 + * this particular combination can be enabled. 59 + */ 60 + if (!strcmp("might_sleep", replacement) && err) { 61 + ASSERT_HAS_SUBSTR(log, "helper call might sleep in a non-sleepable prog", "error log"); 62 + ASSERT_EQ(err, -EINVAL, "err"); 63 + test__skip(); 64 + goto out; 65 + } 66 + 67 + if (expect_load) { 68 + ASSERT_OK(err, "summarization_freplace__load"); 69 + } else { 70 + ASSERT_ERR(err, "summarization_freplace__load"); 71 + ASSERT_HAS_SUBSTR(log, err_msg, "error log"); 72 + } 73 + 74 + out: 75 + summarization_freplace__destroy(freplace); 76 + summarization__destroy(main); 77 + } 78 + 79 + /* There are two global subprograms in both summarization.skel.h: 80 + * - one changes packet data; 81 + * - another does not. 82 + * It is ok to freplace subprograms that change packet data with those 83 + * that either do or do not. It is only ok to freplace subprograms 84 + * that do not change packet data with those that do not as well. 85 + * The below tests check outcomes for each combination of such freplace. 86 + * Also test a case when main subprogram itself is replaced and is a single 87 + * subprogram in a program. 88 + * 89 + * This holds for might_sleep programs. It is ok to replace might_sleep with 90 + * might_sleep and with does_not_sleep, but does_not_sleep cannot be replaced 91 + * with might_sleep. 92 + */ 93 + void test_summarization_freplace(void) 94 + { 95 + struct { 96 + const char *main; 97 + const char *to_be_replaced; 98 + bool has_side_effect; 99 + } mains[2][4] = { 100 + { 101 + { "main_changes_with_subprogs", "changes_pkt_data", true }, 102 + { "main_changes_with_subprogs", "does_not_change_pkt_data", false }, 103 + { "main_changes", "main_changes", true }, 104 + { "main_does_not_change", "main_does_not_change", false }, 105 + }, 106 + { 107 + { "main_might_sleep_with_subprogs", "might_sleep", true }, 108 + { "main_might_sleep_with_subprogs", "does_not_sleep", false }, 109 + { "main_might_sleep", "main_might_sleep", true }, 110 + { "main_does_not_sleep", "main_does_not_sleep", false }, 111 + }, 112 + }; 113 + const char *pkt_err = "Extension program changes packet data"; 114 + const char *slp_err = "Extension program may sleep"; 115 + struct { 116 + const char *func; 117 + bool has_side_effect; 118 + const char *err_msg; 119 + } replacements[2][2] = { 120 + { 121 + { "changes_pkt_data", true, pkt_err }, 122 + { "does_not_change_pkt_data", false, pkt_err }, 123 + }, 124 + { 125 + { "might_sleep", true, slp_err }, 126 + { "does_not_sleep", false, slp_err }, 127 + }, 128 + }; 129 + char buf[64]; 130 + 131 + for (int t = 0; t < 2; t++) { 132 + for (int i = 0; i < ARRAY_SIZE(mains); ++i) { 133 + for (int j = 0; j < ARRAY_SIZE(replacements); ++j) { 134 + snprintf(buf, sizeof(buf), "%s_with_%s", 135 + mains[t][i].to_be_replaced, replacements[t][j].func); 136 + if (!test__start_subtest(buf)) 137 + continue; 138 + test_aux(mains[t][i].main, mains[t][i].to_be_replaced, replacements[t][j].func, 139 + mains[t][i].has_side_effect || !replacements[t][j].has_side_effect, 140 + replacements[t][j].err_msg); 141 + } 142 + } 143 + } 144 + }
-39
tools/testing/selftests/bpf/progs/changes_pkt_data.c
··· 1 - // SPDX-License-Identifier: GPL-2.0 2 - 3 - #include <linux/bpf.h> 4 - #include <bpf/bpf_helpers.h> 5 - 6 - __noinline 7 - long changes_pkt_data(struct __sk_buff *sk) 8 - { 9 - return bpf_skb_pull_data(sk, 0); 10 - } 11 - 12 - __noinline __weak 13 - long does_not_change_pkt_data(struct __sk_buff *sk) 14 - { 15 - return 0; 16 - } 17 - 18 - SEC("?tc") 19 - int main_with_subprogs(struct __sk_buff *sk) 20 - { 21 - changes_pkt_data(sk); 22 - does_not_change_pkt_data(sk); 23 - return 0; 24 - } 25 - 26 - SEC("?tc") 27 - int main_changes(struct __sk_buff *sk) 28 - { 29 - bpf_skb_pull_data(sk, 0); 30 - return 0; 31 - } 32 - 33 - SEC("?tc") 34 - int main_does_not_change(struct __sk_buff *sk) 35 - { 36 - return 0; 37 - } 38 - 39 - char _license[] SEC("license") = "GPL";
+16 -1
tools/testing/selftests/bpf/progs/changes_pkt_data_freplace.c tools/testing/selftests/bpf/progs/summarization_freplace.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 - #include <linux/bpf.h> 3 + #include <vmlinux.h> 4 4 #include <bpf/bpf_helpers.h> 5 5 6 6 SEC("?freplace") ··· 11 11 12 12 SEC("?freplace") 13 13 long does_not_change_pkt_data(struct __sk_buff *sk) 14 + { 15 + return 0; 16 + } 17 + 18 + SEC("?freplace") 19 + long might_sleep(struct pt_regs *ctx) 20 + { 21 + int i; 22 + 23 + bpf_copy_from_user(&i, sizeof(i), NULL); 24 + return i; 25 + } 26 + 27 + SEC("?freplace") 28 + long does_not_sleep(struct pt_regs *ctx) 14 29 { 15 30 return 0; 16 31 }
+78
tools/testing/selftests/bpf/progs/summarization.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <vmlinux.h> 4 + #include <bpf/bpf_helpers.h> 5 + #include "bpf_misc.h" 6 + 7 + __noinline 8 + long changes_pkt_data(struct __sk_buff *sk) 9 + { 10 + return bpf_skb_pull_data(sk, 0); 11 + } 12 + 13 + __noinline __weak 14 + long does_not_change_pkt_data(struct __sk_buff *sk) 15 + { 16 + return 0; 17 + } 18 + 19 + SEC("?tc") 20 + int main_changes_with_subprogs(struct __sk_buff *sk) 21 + { 22 + changes_pkt_data(sk); 23 + does_not_change_pkt_data(sk); 24 + return 0; 25 + } 26 + 27 + SEC("?tc") 28 + int main_changes(struct __sk_buff *sk) 29 + { 30 + bpf_skb_pull_data(sk, 0); 31 + return 0; 32 + } 33 + 34 + SEC("?tc") 35 + int main_does_not_change(struct __sk_buff *sk) 36 + { 37 + return 0; 38 + } 39 + 40 + __noinline 41 + long might_sleep(struct pt_regs *ctx __arg_ctx) 42 + { 43 + int i; 44 + 45 + bpf_copy_from_user(&i, sizeof(i), NULL); 46 + return i; 47 + } 48 + 49 + __noinline __weak 50 + long does_not_sleep(struct pt_regs *ctx __arg_ctx) 51 + { 52 + return 0; 53 + } 54 + 55 + SEC("?uprobe.s") 56 + int main_might_sleep_with_subprogs(struct pt_regs *ctx) 57 + { 58 + might_sleep(ctx); 59 + does_not_sleep(ctx); 60 + return 0; 61 + } 62 + 63 + SEC("?uprobe.s") 64 + int main_might_sleep(struct pt_regs *ctx) 65 + { 66 + int i; 67 + 68 + bpf_copy_from_user(&i, sizeof(i), NULL); 69 + return i; 70 + } 71 + 72 + SEC("?uprobe.s") 73 + int main_does_not_sleep(struct pt_regs *ctx) 74 + { 75 + return 0; 76 + } 77 + 78 + char _license[] SEC("license") = "GPL";