|
| 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 | +} |
0 commit comments