-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
bpf/selftests: Add selftests for new task kfuncs
A previous change added a series of kfuncs for storing struct task_struct objects as referenced kptrs. This patch adds a new task_kfunc test suite for validating their expected behavior. Signed-off-by: David Vernet <void@manifault.com> Link: https://lore.kernel.org/r/20221120051004.3605026-5-void@manifault.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
- Loading branch information
David Vernet
authored and
Alexei Starovoitov
committed
Nov 20, 2022
1 parent
9066030
commit fe14795
Showing
5 changed files
with
640 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ | ||
|
||
#define _GNU_SOURCE | ||
#include <sys/wait.h> | ||
#include <test_progs.h> | ||
#include <unistd.h> | ||
|
||
#include "task_kfunc_failure.skel.h" | ||
#include "task_kfunc_success.skel.h" | ||
|
||
static size_t log_buf_sz = 1 << 20; /* 1 MB */ | ||
static char obj_log_buf[1048576]; | ||
|
||
static struct task_kfunc_success *open_load_task_kfunc_skel(void) | ||
{ | ||
struct task_kfunc_success *skel; | ||
int err; | ||
|
||
skel = task_kfunc_success__open(); | ||
if (!ASSERT_OK_PTR(skel, "skel_open")) | ||
return NULL; | ||
|
||
skel->bss->pid = getpid(); | ||
|
||
err = task_kfunc_success__load(skel); | ||
if (!ASSERT_OK(err, "skel_load")) | ||
goto cleanup; | ||
|
||
return skel; | ||
|
||
cleanup: | ||
task_kfunc_success__destroy(skel); | ||
return NULL; | ||
} | ||
|
||
static void run_success_test(const char *prog_name) | ||
{ | ||
struct task_kfunc_success *skel; | ||
int status; | ||
pid_t child_pid; | ||
struct bpf_program *prog; | ||
struct bpf_link *link = NULL; | ||
|
||
skel = open_load_task_kfunc_skel(); | ||
if (!ASSERT_OK_PTR(skel, "open_load_skel")) | ||
return; | ||
|
||
if (!ASSERT_OK(skel->bss->err, "pre_spawn_err")) | ||
goto cleanup; | ||
|
||
prog = bpf_object__find_program_by_name(skel->obj, prog_name); | ||
if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) | ||
goto cleanup; | ||
|
||
link = bpf_program__attach(prog); | ||
if (!ASSERT_OK_PTR(link, "attached_link")) | ||
goto cleanup; | ||
|
||
child_pid = fork(); | ||
if (!ASSERT_GT(child_pid, -1, "child_pid")) | ||
goto cleanup; | ||
if (child_pid == 0) | ||
_exit(0); | ||
waitpid(child_pid, &status, 0); | ||
|
||
ASSERT_OK(skel->bss->err, "post_wait_err"); | ||
|
||
cleanup: | ||
bpf_link__destroy(link); | ||
task_kfunc_success__destroy(skel); | ||
} | ||
|
||
static const char * const success_tests[] = { | ||
"test_task_acquire_release_argument", | ||
"test_task_acquire_release_current", | ||
"test_task_acquire_leave_in_map", | ||
"test_task_xchg_release", | ||
"test_task_get_release", | ||
"test_task_current_acquire_release", | ||
}; | ||
|
||
static struct { | ||
const char *prog_name; | ||
const char *expected_err_msg; | ||
} failure_tests[] = { | ||
{"task_kfunc_acquire_untrusted", "R1 must be referenced or trusted"}, | ||
{"task_kfunc_acquire_fp", "arg#0 pointer type STRUCT task_struct must point"}, | ||
{"task_kfunc_acquire_unsafe_kretprobe", "reg type unsupported for arg#0 function"}, | ||
{"task_kfunc_acquire_trusted_walked", "R1 must be referenced or trusted"}, | ||
{"task_kfunc_acquire_null", "arg#0 pointer type STRUCT task_struct must point"}, | ||
{"task_kfunc_acquire_unreleased", "Unreleased reference"}, | ||
{"task_kfunc_get_non_kptr_param", "arg#0 expected pointer to map value"}, | ||
{"task_kfunc_get_non_kptr_acquired", "arg#0 expected pointer to map value"}, | ||
{"task_kfunc_get_null", "arg#0 expected pointer to map value"}, | ||
{"task_kfunc_xchg_unreleased", "Unreleased reference"}, | ||
{"task_kfunc_get_unreleased", "Unreleased reference"}, | ||
{"task_kfunc_release_untrusted", "arg#0 is untrusted_ptr_or_null_ expected ptr_ or socket"}, | ||
{"task_kfunc_release_fp", "arg#0 pointer type STRUCT task_struct must point"}, | ||
{"task_kfunc_release_null", "arg#0 is ptr_or_null_ expected ptr_ or socket"}, | ||
{"task_kfunc_release_unacquired", "release kernel function bpf_task_release expects"}, | ||
}; | ||
|
||
static void verify_fail(const char *prog_name, const char *expected_err_msg) | ||
{ | ||
LIBBPF_OPTS(bpf_object_open_opts, opts); | ||
struct task_kfunc_failure *skel; | ||
int err, i; | ||
|
||
opts.kernel_log_buf = obj_log_buf; | ||
opts.kernel_log_size = log_buf_sz; | ||
opts.kernel_log_level = 1; | ||
|
||
skel = task_kfunc_failure__open_opts(&opts); | ||
if (!ASSERT_OK_PTR(skel, "task_kfunc_failure__open_opts")) | ||
goto cleanup; | ||
|
||
for (i = 0; i < ARRAY_SIZE(failure_tests); i++) { | ||
struct bpf_program *prog; | ||
const char *curr_name = failure_tests[i].prog_name; | ||
|
||
prog = bpf_object__find_program_by_name(skel->obj, curr_name); | ||
if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) | ||
goto cleanup; | ||
|
||
bpf_program__set_autoload(prog, !strcmp(curr_name, prog_name)); | ||
} | ||
|
||
err = task_kfunc_failure__load(skel); | ||
if (!ASSERT_ERR(err, "unexpected load success")) | ||
goto cleanup; | ||
|
||
if (!ASSERT_OK_PTR(strstr(obj_log_buf, expected_err_msg), "expected_err_msg")) { | ||
fprintf(stderr, "Expected err_msg: %s\n", expected_err_msg); | ||
fprintf(stderr, "Verifier output: %s\n", obj_log_buf); | ||
} | ||
|
||
cleanup: | ||
task_kfunc_failure__destroy(skel); | ||
} | ||
|
||
void test_task_kfunc(void) | ||
{ | ||
int i; | ||
|
||
for (i = 0; i < ARRAY_SIZE(success_tests); i++) { | ||
if (!test__start_subtest(success_tests[i])) | ||
continue; | ||
|
||
run_success_test(success_tests[i]); | ||
} | ||
|
||
for (i = 0; i < ARRAY_SIZE(failure_tests); i++) { | ||
if (!test__start_subtest(failure_tests[i].prog_name)) | ||
continue; | ||
|
||
verify_fail(failure_tests[i].prog_name, failure_tests[i].expected_err_msg); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ | ||
|
||
#ifndef _TASK_KFUNC_COMMON_H | ||
#define _TASK_KFUNC_COMMON_H | ||
|
||
#include <errno.h> | ||
#include <vmlinux.h> | ||
#include <bpf/bpf_helpers.h> | ||
#include <bpf/bpf_tracing.h> | ||
|
||
struct __tasks_kfunc_map_value { | ||
struct task_struct __kptr_ref * task; | ||
}; | ||
|
||
struct hash_map { | ||
__uint(type, BPF_MAP_TYPE_HASH); | ||
__type(key, int); | ||
__type(value, struct __tasks_kfunc_map_value); | ||
__uint(max_entries, 1); | ||
} __tasks_kfunc_map SEC(".maps"); | ||
|
||
struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym; | ||
struct task_struct *bpf_task_kptr_get(struct task_struct **pp) __ksym; | ||
void bpf_task_release(struct task_struct *p) __ksym; | ||
|
||
static inline struct __tasks_kfunc_map_value *tasks_kfunc_map_value_lookup(struct task_struct *p) | ||
{ | ||
s32 pid; | ||
long status; | ||
|
||
status = bpf_probe_read_kernel(&pid, sizeof(pid), &p->pid); | ||
if (status) | ||
return NULL; | ||
|
||
return bpf_map_lookup_elem(&__tasks_kfunc_map, &pid); | ||
} | ||
|
||
static inline int tasks_kfunc_map_insert(struct task_struct *p) | ||
{ | ||
struct __tasks_kfunc_map_value local, *v; | ||
long status; | ||
struct task_struct *acquired, *old; | ||
s32 pid; | ||
|
||
status = bpf_probe_read_kernel(&pid, sizeof(pid), &p->pid); | ||
if (status) | ||
return status; | ||
|
||
local.task = NULL; | ||
status = bpf_map_update_elem(&__tasks_kfunc_map, &pid, &local, BPF_NOEXIST); | ||
if (status) | ||
return status; | ||
|
||
v = bpf_map_lookup_elem(&__tasks_kfunc_map, &pid); | ||
if (!v) { | ||
bpf_map_delete_elem(&__tasks_kfunc_map, &pid); | ||
return -ENOENT; | ||
} | ||
|
||
acquired = bpf_task_acquire(p); | ||
old = bpf_kptr_xchg(&v->task, acquired); | ||
if (old) { | ||
bpf_task_release(old); | ||
return -EEXIST; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
#endif /* _TASK_KFUNC_COMMON_H */ |
Oops, something went wrong.