Commit 35f14dbd authored by Kumar Kartikeya Dwivedi's avatar Kumar Kartikeya Dwivedi Committed by Alexei Starovoitov

selftests/bpf: Add tests for reference state fixes for callbacks

These are regression tests to ensure we don't end up in invalid runtime
state for helpers that execute callbacks multiple times. It exercises
the fixes to verifier callback handling for reference state in previous
patches.
Signed-off-by: default avatarKumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20220823013226.24988-1-memxor@gmail.comSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent 9d9d00ac
// SPDX-License-Identifier: GPL-2.0
#include "bpf/libbpf.h"
#include <test_progs.h>
#include <network_helpers.h>
#include "cb_refs.skel.h"
static char log_buf[1024 * 1024];
struct {
const char *prog_name;
const char *err_msg;
} cb_refs_tests[] = {
{ "underflow_prog", "reference has not been acquired before" },
{ "leak_prog", "Unreleased reference" },
{ "nested_cb", "Unreleased reference id=4 alloc_insn=2" }, /* alloc_insn=2{4,5} */
{ "non_cb_transfer_ref", "Unreleased reference id=4 alloc_insn=1" }, /* alloc_insn=1{1,2} */
};
void test_cb_refs(void)
{
LIBBPF_OPTS(bpf_object_open_opts, opts, .kernel_log_buf = log_buf,
.kernel_log_size = sizeof(log_buf),
.kernel_log_level = 1);
struct bpf_program *prog;
struct cb_refs *skel;
int i;
for (i = 0; i < ARRAY_SIZE(cb_refs_tests); i++) {
LIBBPF_OPTS(bpf_test_run_opts, run_opts,
.data_in = &pkt_v4,
.data_size_in = sizeof(pkt_v4),
.repeat = 1,
);
skel = cb_refs__open_opts(&opts);
if (!ASSERT_OK_PTR(skel, "cb_refs__open_and_load"))
return;
prog = bpf_object__find_program_by_name(skel->obj, cb_refs_tests[i].prog_name);
bpf_program__set_autoload(prog, true);
if (!ASSERT_ERR(cb_refs__load(skel), "cb_refs__load"))
bpf_prog_test_run_opts(bpf_program__fd(prog), &run_opts);
if (!ASSERT_OK_PTR(strstr(log_buf, cb_refs_tests[i].err_msg), "expected error message")) {
fprintf(stderr, "Expected: %s\n", cb_refs_tests[i].err_msg);
fprintf(stderr, "Verifier: %s\n", log_buf);
}
cb_refs__destroy(skel);
}
}
// SPDX-License-Identifier: GPL-2.0
#include <vmlinux.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_helpers.h>
struct map_value {
struct prog_test_ref_kfunc __kptr_ref *ptr;
};
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, int);
__type(value, struct map_value);
__uint(max_entries, 16);
} array_map SEC(".maps");
extern struct prog_test_ref_kfunc *bpf_kfunc_call_test_acquire(unsigned long *sp) __ksym;
extern void bpf_kfunc_call_test_release(struct prog_test_ref_kfunc *p) __ksym;
static __noinline int cb1(void *map, void *key, void *value, void *ctx)
{
void *p = *(void **)ctx;
bpf_kfunc_call_test_release(p);
/* Without the fix this would cause underflow */
return 0;
}
SEC("?tc")
int underflow_prog(void *ctx)
{
struct prog_test_ref_kfunc *p;
unsigned long sl = 0;
p = bpf_kfunc_call_test_acquire(&sl);
if (!p)
return 0;
bpf_for_each_map_elem(&array_map, cb1, &p, 0);
return 0;
}
static __always_inline int cb2(void *map, void *key, void *value, void *ctx)
{
unsigned long sl = 0;
*(void **)ctx = bpf_kfunc_call_test_acquire(&sl);
/* Without the fix this would leak memory */
return 0;
}
SEC("?tc")
int leak_prog(void *ctx)
{
struct prog_test_ref_kfunc *p;
struct map_value *v;
unsigned long sl;
v = bpf_map_lookup_elem(&array_map, &(int){0});
if (!v)
return 0;
p = NULL;
bpf_for_each_map_elem(&array_map, cb2, &p, 0);
p = bpf_kptr_xchg(&v->ptr, p);
if (p)
bpf_kfunc_call_test_release(p);
return 0;
}
static __always_inline int cb(void *map, void *key, void *value, void *ctx)
{
return 0;
}
static __always_inline int cb3(void *map, void *key, void *value, void *ctx)
{
unsigned long sl = 0;
void *p;
bpf_kfunc_call_test_acquire(&sl);
bpf_for_each_map_elem(&array_map, cb, &p, 0);
/* It should only complain here, not in cb. This is why we need
* callback_ref to be set to frameno.
*/
return 0;
}
SEC("?tc")
int nested_cb(void *ctx)
{
struct prog_test_ref_kfunc *p;
unsigned long sl = 0;
int sp = 0;
p = bpf_kfunc_call_test_acquire(&sl);
if (!p)
return 0;
bpf_for_each_map_elem(&array_map, cb3, &sp, 0);
bpf_kfunc_call_test_release(p);
return 0;
}
SEC("?tc")
int non_cb_transfer_ref(void *ctx)
{
struct prog_test_ref_kfunc *p;
unsigned long sl = 0;
p = bpf_kfunc_call_test_acquire(&sl);
if (!p)
return 0;
cb1(NULL, NULL, NULL, &p);
bpf_kfunc_call_test_acquire(&sl);
return 0;
}
char _license[] SEC("license") = "GPL";
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment