Skip to content

Commit

Permalink
selftests/bpf: Add tests for dynamic pointers parameters in kfuncs
Browse files Browse the repository at this point in the history
Add tests to ensure that only supported dynamic pointer types are accepted,
that the passed argument is actually a dynamic pointer, that the passed
argument is a pointer to the stack, and that bpf_verify_pkcs7_signature()
correctly handles dynamic pointers with data set to NULL.

The tests are currently in the deny list for s390x (JIT does not support
calling kernel function).

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
  • Loading branch information
robertosassu authored and Kernel Patches Daemon committed Sep 21, 2022
1 parent 5f2ec49 commit 896c963
Show file tree
Hide file tree
Showing 3 changed files with 259 additions and 0 deletions.
1 change: 1 addition & 0 deletions tools/testing/selftests/bpf/DENYLIST.s390x
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,4 @@ htab_update # failed to attach: ERROR: strerror_r(-
tracing_struct # failed to auto-attach: -524 (trampoline)
lookup_key # JIT does not support calling kernel function (kfunc)
verify_pkcs7_sig # JIT does not support calling kernel function (kfunc)
kfunc_dynptr_param # JIT does not support calling kernel function (kfunc)
164 changes: 164 additions & 0 deletions tools/testing/selftests/bpf/prog_tests/kfunc_dynptr_param.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// SPDX-License-Identifier: GPL-2.0

/*
* Copyright (c) 2022 Facebook
* Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*/

#include <test_progs.h>
#include "test_kfunc_dynptr_param.skel.h"

static size_t log_buf_sz = 1048576; /* 1 MB */
static char obj_log_buf[1048576];

static struct {
const char *prog_name;
const char *expected_verifier_err_msg;
int expected_runtime_err;
} kfunc_dynptr_tests[] = {
{"dynptr_type_not_supp",
"arg#0 pointer type STRUCT bpf_dynptr_kern points to unsupported dynamic pointer type", 0},
{"not_valid_dynptr",
"arg#0 pointer type STRUCT bpf_dynptr_kern must be valid and initialized", 0},
{"not_ptr_to_stack", "arg#0 pointer type STRUCT bpf_dynptr_kern not to stack", 0},
{"dynptr_data_null", NULL, -EBADMSG},
};

static bool kfunc_not_supported;

static int libbpf_print_cb(enum libbpf_print_level level, const char *fmt,
va_list args)
{
if (strcmp(fmt, "libbpf: extern (func ksym) '%s': not found in kernel or module BTFs\n"))
return 0;

if (strcmp(va_arg(args, char *), "bpf_verify_pkcs7_signature"))
return 0;

kfunc_not_supported = true;
return 0;
}

static void verify_fail(const char *prog_name, const char *expected_err_msg)
{
struct test_kfunc_dynptr_param *skel;
LIBBPF_OPTS(bpf_object_open_opts, opts);
libbpf_print_fn_t old_print_cb;
struct bpf_program *prog;
int err;

opts.kernel_log_buf = obj_log_buf;
opts.kernel_log_size = log_buf_sz;
opts.kernel_log_level = 1;

skel = test_kfunc_dynptr_param__open_opts(&opts);
if (!ASSERT_OK_PTR(skel, "test_kfunc_dynptr_param__open_opts"))
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;

bpf_program__set_autoload(prog, true);

bpf_map__set_max_entries(skel->maps.ringbuf, getpagesize());

kfunc_not_supported = false;

old_print_cb = libbpf_set_print(libbpf_print_cb);
err = test_kfunc_dynptr_param__load(skel);
libbpf_set_print(old_print_cb);

if (err < 0 && kfunc_not_supported) {
fprintf(stderr,
"%s:SKIP:bpf_verify_pkcs7_signature() kfunc not supported\n",
__func__);
test__skip();
goto cleanup;
}

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:
test_kfunc_dynptr_param__destroy(skel);
}

static void verify_success(const char *prog_name, int expected_runtime_err)
{
struct test_kfunc_dynptr_param *skel;
libbpf_print_fn_t old_print_cb;
struct bpf_program *prog;
struct bpf_link *link;
__u32 next_id;
int err;

skel = test_kfunc_dynptr_param__open();
if (!ASSERT_OK_PTR(skel, "test_kfunc_dynptr_param__open"))
return;

skel->bss->pid = getpid();

bpf_map__set_max_entries(skel->maps.ringbuf, getpagesize());

kfunc_not_supported = false;

old_print_cb = libbpf_set_print(libbpf_print_cb);
err = test_kfunc_dynptr_param__load(skel);
libbpf_set_print(old_print_cb);

if (err < 0 && kfunc_not_supported) {
fprintf(stderr,
"%s:SKIP:bpf_verify_pkcs7_signature() kfunc not supported\n",
__func__);
test__skip();
goto cleanup;
}

if (!ASSERT_OK(err, "test_kfunc_dynptr_param__load"))
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, "bpf_program__attach"))
goto cleanup;

err = bpf_prog_get_next_id(0, &next_id);

bpf_link__destroy(link);

if (!ASSERT_OK(err, "bpf_prog_get_next_id"))
goto cleanup;

ASSERT_EQ(skel->bss->err, expected_runtime_err, "err");

cleanup:
test_kfunc_dynptr_param__destroy(skel);
}

void test_kfunc_dynptr_param(void)
{
int i;

for (i = 0; i < ARRAY_SIZE(kfunc_dynptr_tests); i++) {
if (!test__start_subtest(kfunc_dynptr_tests[i].prog_name))
continue;

if (kfunc_dynptr_tests[i].expected_verifier_err_msg)
verify_fail(kfunc_dynptr_tests[i].prog_name,
kfunc_dynptr_tests[i].expected_verifier_err_msg);
else
verify_success(kfunc_dynptr_tests[i].prog_name,
kfunc_dynptr_tests[i].expected_runtime_err);
}
}
94 changes: 94 additions & 0 deletions tools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-2.0

/*
* Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*/

#include "vmlinux.h"
#include <errno.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

extern struct bpf_key *bpf_lookup_system_key(__u64 id) __ksym;
extern void bpf_key_put(struct bpf_key *key) __ksym;
extern int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_ptr,
struct bpf_dynptr *sig_ptr,
struct bpf_key *trusted_keyring) __ksym;

struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
} ringbuf SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, __u32);
__type(value, __u32);
} array_map SEC(".maps");

int err, pid;

char _license[] SEC("license") = "GPL";

SEC("?lsm.s/bpf")
int BPF_PROG(dynptr_type_not_supp, int cmd, union bpf_attr *attr,
unsigned int size)
{
char write_data[64] = "hello there, world!!";
struct bpf_dynptr ptr;

bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(write_data), 0, &ptr);

return bpf_verify_pkcs7_signature(&ptr, &ptr, NULL);
}

SEC("?lsm.s/bpf")
int BPF_PROG(not_valid_dynptr, int cmd, union bpf_attr *attr, unsigned int size)
{
unsigned long val;

return bpf_verify_pkcs7_signature((struct bpf_dynptr *)&val,
(struct bpf_dynptr *)&val, NULL);
}

SEC("?lsm.s/bpf")
int BPF_PROG(not_ptr_to_stack, int cmd, union bpf_attr *attr, unsigned int size)
{
unsigned long val;

return bpf_verify_pkcs7_signature((struct bpf_dynptr *)val,
(struct bpf_dynptr *)val, NULL);
}

SEC("lsm.s/bpf")
int BPF_PROG(dynptr_data_null, int cmd, union bpf_attr *attr, unsigned int size)
{
struct bpf_key *trusted_keyring;
struct bpf_dynptr ptr;
__u32 *value;
int ret, zero = 0;

if (bpf_get_current_pid_tgid() >> 32 != pid)
return 0;

value = bpf_map_lookup_elem(&array_map, &zero);
if (!value)
return 0;

/* Pass invalid flags. */
ret = bpf_dynptr_from_mem(value, sizeof(*value), ((__u64)~0ULL), &ptr);
if (ret != -EINVAL)
return 0;

trusted_keyring = bpf_lookup_system_key(0);
if (!trusted_keyring)
return 0;

err = bpf_verify_pkcs7_signature(&ptr, &ptr, trusted_keyring);

bpf_key_put(trusted_keyring);

return 0;
}

0 comments on commit 896c963

Please sign in to comment.