Files
linux-stable-mirror/tools/testing/selftests/bpf/prog_tests/cb_refs.c
T
Amery HungandAlexei Starovoitov bcfcb15fde bpf: Unify release handling for helpers and kfuncs
Introduce release_reg() to consolidate the release logic shared by both
helpers and kfuncs: dynptr release, kptr_xchg percpu-to-RCU conversion,
regular reference release, and NULL pass-through. NULL pass-through is
only allowed if the prototype indicates the argument may be null.

Determine release_regno from the function prototype/metadata before
argument checking, rather than discovering it dynamically during
argument processing. For helpers, scan the arg_type array in
check_func_proto() via check_proto_release_reg(). For kfuncs, set
release_regno to BPF_REG_1 in bpf_fetch_kfunc_arg_meta() when
KF_RELEASE is set. In the future when we start adding decl_tag to
kfunc arguments, we can just look at the function prototype instead
of a release_regno.

Extract ref_convert_alloc_rcu_protected() and
invalidate_rcu_protected_refs() to make it more clear what the code is
doing. For ref_convert_alloc_rcu_protected(), it pre-converts
MEM_ALLOC | MEM_PERCPU registers to MEM_RCU (clearing id so they
survive), then calls release_reference() to invalidate the remaining
registers and release the reference state.

Add KF_RELEASE to bpf_dynptr_file_discard() so its release_regno is set
via fetch_kfunc_meta rather than being assigned manually in the dynptr
argument processing. Set arg_type to ARG_PTR_TO_DYNPTR for
KF_ARG_PTR_TO_DYNPTR so that check_func_arg_reg_off() correctly allows
non-zero stack offsets for dynptr release arguments same as helper.

Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Amery Hung <ameryhung@gmail.com>
Link: https://lore.kernel.org/r/20260529014936.2811085-9-ameryhung@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2026-06-01 18:31:41 -07:00

49 lines
1.6 KiB
C

// 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", "release kfunc bpf_kfunc_call_test_release expects referenced PTR_TO_BTF_ID passed to R1" },
{ "leak_prog", "Possibly NULL pointer passed to helper R2" },
{ "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);
}
}