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

bpf: Add selftests for BPF_MODIFY_RETURN

Test for two scenarios:

* When the fmod_ret program returns 0, the original function should
be called along with fentry and fexit programs.
* When the fmod_ret program returns a non-zero value, the original
function should not be called, no side effect should be observed and
fentry and fexit programs should be called.

The result from the kernel function call and whether a side-effect is
observed is returned via the retval attr of the BPF_PROG_TEST_RUN (bpf)
syscall.

Signed-off-by: KP Singh <kpsingh@google.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Andrii Nakryiko <andriin@fb.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/20200304191853.1529-8-kpsingh@chromium.org

authored by

KP Singh and committed by
Alexei Starovoitov
3d08b6f2 da00d2f1

+135 -1
+21 -1
net/bpf/test_run.c
··· 10 10 #include <net/bpf_sk_storage.h> 11 11 #include <net/sock.h> 12 12 #include <net/tcp.h> 13 + #include <linux/error-injection.h> 13 14 14 15 #define CREATE_TRACE_POINTS 15 16 #include <trace/events/bpf_test_run.h> ··· 144 143 return a + (long)b + c + d + (long)e + f; 145 144 } 146 145 146 + int noinline bpf_modify_return_test(int a, int *b) 147 + { 148 + *b += 1; 149 + return a + *b; 150 + } 151 + 152 + ALLOW_ERROR_INJECTION(bpf_modify_return_test, ERRNO); 153 + 147 154 static void *bpf_test_init(const union bpf_attr *kattr, u32 size, 148 155 u32 headroom, u32 tailroom) 149 156 { ··· 177 168 const union bpf_attr *kattr, 178 169 union bpf_attr __user *uattr) 179 170 { 180 - int err = -EFAULT; 171 + u16 side_effect = 0, ret = 0; 172 + int b = 2, err = -EFAULT; 173 + u32 retval = 0; 181 174 182 175 switch (prog->expected_attach_type) { 183 176 case BPF_TRACE_FENTRY: ··· 192 181 bpf_fentry_test6(16, (void *)17, 18, 19, (void *)20, 21) != 111) 193 182 goto out; 194 183 break; 184 + case BPF_MODIFY_RETURN: 185 + ret = bpf_modify_return_test(1, &b); 186 + if (b != 2) 187 + side_effect = 1; 188 + break; 195 189 default: 196 190 goto out; 197 191 } 192 + 193 + retval = ((u32)side_effect << 16) | ret; 194 + if (copy_to_user(&uattr->test.retval, &retval, sizeof(retval))) 195 + goto out; 198 196 199 197 err = 0; 200 198 out:
+65
tools/testing/selftests/bpf/prog_tests/modify_return.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + /* 4 + * Copyright 2020 Google LLC. 5 + */ 6 + 7 + #include <test_progs.h> 8 + #include "modify_return.skel.h" 9 + 10 + #define LOWER(x) ((x) & 0xffff) 11 + #define UPPER(x) ((x) >> 16) 12 + 13 + 14 + static void run_test(__u32 input_retval, __u16 want_side_effect, __s16 want_ret) 15 + { 16 + struct modify_return *skel = NULL; 17 + int err, prog_fd; 18 + __u32 duration = 0, retval; 19 + __u16 side_effect; 20 + __s16 ret; 21 + 22 + skel = modify_return__open_and_load(); 23 + if (CHECK(!skel, "skel_load", "modify_return skeleton failed\n")) 24 + goto cleanup; 25 + 26 + err = modify_return__attach(skel); 27 + if (CHECK(err, "modify_return", "attach failed: %d\n", err)) 28 + goto cleanup; 29 + 30 + skel->bss->input_retval = input_retval; 31 + prog_fd = bpf_program__fd(skel->progs.fmod_ret_test); 32 + err = bpf_prog_test_run(prog_fd, 1, NULL, 0, NULL, 0, 33 + &retval, &duration); 34 + 35 + CHECK(err, "test_run", "err %d errno %d\n", err, errno); 36 + 37 + side_effect = UPPER(retval); 38 + ret = LOWER(retval); 39 + 40 + CHECK(ret != want_ret, "test_run", 41 + "unexpected ret: %d, expected: %d\n", ret, want_ret); 42 + CHECK(side_effect != want_side_effect, "modify_return", 43 + "unexpected side_effect: %d\n", side_effect); 44 + 45 + CHECK(skel->bss->fentry_result != 1, "modify_return", 46 + "fentry failed\n"); 47 + CHECK(skel->bss->fexit_result != 1, "modify_return", 48 + "fexit failed\n"); 49 + CHECK(skel->bss->fmod_ret_result != 1, "modify_return", 50 + "fmod_ret failed\n"); 51 + 52 + cleanup: 53 + modify_return__destroy(skel); 54 + } 55 + 56 + void test_modify_return(void) 57 + { 58 + run_test(0 /* input_retval */, 59 + 1 /* want_side_effect */, 60 + 4 /* want_ret */); 61 + run_test(-EINVAL /* input_retval */, 62 + 0 /* want_side_effect */, 63 + -EINVAL /* want_ret */); 64 + } 65 +
+49
tools/testing/selftests/bpf/progs/modify_return.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + /* 4 + * Copyright 2020 Google LLC. 5 + */ 6 + 7 + #include <linux/bpf.h> 8 + #include <bpf/bpf_helpers.h> 9 + #include <bpf/bpf_tracing.h> 10 + 11 + char _license[] SEC("license") = "GPL"; 12 + 13 + static int sequence = 0; 14 + __s32 input_retval = 0; 15 + 16 + __u64 fentry_result = 0; 17 + SEC("fentry/bpf_modify_return_test") 18 + int BPF_PROG(fentry_test, int a, __u64 b) 19 + { 20 + sequence++; 21 + fentry_result = (sequence == 1); 22 + return 0; 23 + } 24 + 25 + __u64 fmod_ret_result = 0; 26 + SEC("fmod_ret/bpf_modify_return_test") 27 + int BPF_PROG(fmod_ret_test, int a, int *b, int ret) 28 + { 29 + sequence++; 30 + /* This is the first fmod_ret program, the ret passed should be 0 */ 31 + fmod_ret_result = (sequence == 2 && ret == 0); 32 + return input_retval; 33 + } 34 + 35 + __u64 fexit_result = 0; 36 + SEC("fexit/bpf_modify_return_test") 37 + int BPF_PROG(fexit_test, int a, __u64 b, int ret) 38 + { 39 + sequence++; 40 + /* If the input_reval is non-zero a successful modification should have 41 + * occurred. 42 + */ 43 + if (input_retval) 44 + fexit_result = (sequence == 3 && ret == input_retval); 45 + else 46 + fexit_result = (sequence == 3 && ret == 4); 47 + 48 + return 0; 49 + }