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 Signed-off-by: Menglong Dong <imagedong@tencent.com>
This commit is contained in:
parent
54e8a2943a
commit
469ea903a9
|
|
@ -10,6 +10,7 @@
|
|||
#include <net/bpf_sk_storage.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/tcp.h>
|
||||
#include <linux/error-injection.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/bpf_test_run.h>
|
||||
|
|
@ -143,6 +144,14 @@ int noinline bpf_fentry_test6(u64 a, void *b, short c, int d, void *e, u64 f)
|
|||
return a + (long)b + c + d + (long)e + f;
|
||||
}
|
||||
|
||||
int noinline bpf_modify_return_test(int a, int *b)
|
||||
{
|
||||
*b += 1;
|
||||
return a + *b;
|
||||
}
|
||||
|
||||
ALLOW_ERROR_INJECTION(bpf_modify_return_test, ERRNO);
|
||||
|
||||
static void *bpf_test_init(const union bpf_attr *kattr, u32 size,
|
||||
u32 headroom, u32 tailroom)
|
||||
{
|
||||
|
|
@ -168,7 +177,9 @@ int bpf_prog_test_run_tracing(struct bpf_prog *prog,
|
|||
const union bpf_attr *kattr,
|
||||
union bpf_attr __user *uattr)
|
||||
{
|
||||
int err = -EFAULT;
|
||||
u16 side_effect = 0, ret = 0;
|
||||
int b = 2, err = -EFAULT;
|
||||
u32 retval = 0;
|
||||
|
||||
switch (prog->expected_attach_type) {
|
||||
case BPF_TRACE_FENTRY:
|
||||
|
|
@ -181,10 +192,19 @@ int bpf_prog_test_run_tracing(struct bpf_prog *prog,
|
|||
bpf_fentry_test6(16, (void *)17, 18, 19, (void *)20, 21) != 111)
|
||||
goto out;
|
||||
break;
|
||||
case BPF_MODIFY_RETURN:
|
||||
ret = bpf_modify_return_test(1, &b);
|
||||
if (b != 2)
|
||||
side_effect = 1;
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
|
||||
retval = ((u32)side_effect << 16) | ret;
|
||||
if (copy_to_user(&uattr->test.retval, &retval, sizeof(retval)))
|
||||
goto out;
|
||||
|
||||
err = 0;
|
||||
out:
|
||||
trace_bpf_test_finish(&err);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* Copyright 2020 Google LLC.
|
||||
*/
|
||||
|
||||
#include <test_progs.h>
|
||||
#include "modify_return.skel.h"
|
||||
|
||||
#define LOWER(x) ((x) & 0xffff)
|
||||
#define UPPER(x) ((x) >> 16)
|
||||
|
||||
|
||||
static void run_test(__u32 input_retval, __u16 want_side_effect, __s16 want_ret)
|
||||
{
|
||||
struct modify_return *skel = NULL;
|
||||
int err, prog_fd;
|
||||
__u16 side_effect;
|
||||
__s16 ret;
|
||||
LIBBPF_OPTS(bpf_test_run_opts, topts);
|
||||
|
||||
skel = modify_return__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "skel_load"))
|
||||
goto cleanup;
|
||||
|
||||
err = modify_return__attach(skel);
|
||||
if (!ASSERT_OK(err, "modify_return__attach failed"))
|
||||
goto cleanup;
|
||||
|
||||
skel->bss->input_retval = input_retval;
|
||||
prog_fd = bpf_program__fd(skel->progs.fmod_ret_test);
|
||||
err = bpf_prog_test_run_opts(prog_fd, &topts);
|
||||
ASSERT_OK(err, "test_run");
|
||||
|
||||
side_effect = UPPER(topts.retval);
|
||||
ret = LOWER(topts.retval);
|
||||
|
||||
ASSERT_EQ(ret, want_ret, "test_run ret");
|
||||
ASSERT_EQ(side_effect, want_side_effect, "modify_return side_effect");
|
||||
ASSERT_EQ(skel->bss->fentry_result, 1, "modify_return fentry_result");
|
||||
ASSERT_EQ(skel->bss->fexit_result, 1, "modify_return fexit_result");
|
||||
ASSERT_EQ(skel->bss->fmod_ret_result, 1, "modify_return fmod_ret_result");
|
||||
|
||||
cleanup:
|
||||
modify_return__destroy(skel);
|
||||
}
|
||||
|
||||
/* TODO: conflict with get_func_ip_test */
|
||||
void serial_test_modify_return(void)
|
||||
{
|
||||
run_test(0 /* input_retval */,
|
||||
1 /* want_side_effect */,
|
||||
4 /* want_ret */);
|
||||
run_test(-EINVAL /* input_retval */,
|
||||
0 /* want_side_effect */,
|
||||
-EINVAL /* want_ret */);
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* Copyright 2020 Google LLC.
|
||||
*/
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
static int sequence = 0;
|
||||
__s32 input_retval = 0;
|
||||
|
||||
__u64 fentry_result = 0;
|
||||
SEC("fentry/bpf_modify_return_test")
|
||||
int BPF_PROG(fentry_test, int a, __u64 b)
|
||||
{
|
||||
sequence++;
|
||||
fentry_result = (sequence == 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
__u64 fmod_ret_result = 0;
|
||||
SEC("fmod_ret/bpf_modify_return_test")
|
||||
int BPF_PROG(fmod_ret_test, int a, int *b, int ret)
|
||||
{
|
||||
sequence++;
|
||||
/* This is the first fmod_ret program, the ret passed should be 0 */
|
||||
fmod_ret_result = (sequence == 2 && ret == 0);
|
||||
return input_retval;
|
||||
}
|
||||
|
||||
__u64 fexit_result = 0;
|
||||
SEC("fexit/bpf_modify_return_test")
|
||||
int BPF_PROG(fexit_test, int a, __u64 b, int ret)
|
||||
{
|
||||
sequence++;
|
||||
/* If the input_reval is non-zero a successful modification should have
|
||||
* occurred.
|
||||
*/
|
||||
if (input_retval)
|
||||
fexit_result = (sequence == 3 && ret == input_retval);
|
||||
else
|
||||
fexit_result = (sequence == 3 && ret == 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue