Files
linux-stable-mirror/tools/testing/selftests/bpf/progs/wakeup_source_fail.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

77 lines
1.5 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright 2026 Google LLC */
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"
struct bpf_ws_lock;
struct bpf_ws_lock *bpf_wakeup_sources_read_lock(void) __ksym;
void bpf_wakeup_sources_read_unlock(struct bpf_ws_lock *lock) __ksym;
void *bpf_wakeup_sources_get_head(void) __ksym;
SEC("syscall")
__failure __msg("BPF_EXIT instruction in main prog would lead to reference leak")
int wakeup_source_lock_no_unlock(void *ctx)
{
struct bpf_ws_lock *lock;
lock = bpf_wakeup_sources_read_lock();
if (!lock)
return 0;
return 0;
}
SEC("syscall")
__failure __msg("access beyond struct")
int wakeup_source_access_lock_fields(void *ctx)
{
struct bpf_ws_lock *lock;
int val;
lock = bpf_wakeup_sources_read_lock();
if (!lock)
return 0;
val = *(int *)lock;
bpf_wakeup_sources_read_unlock(lock);
return val;
}
SEC("syscall")
__failure __msg("release kfunc bpf_wakeup_sources_read_unlock expects referenced PTR_TO_BTF_ID passed to R1")
int wakeup_source_unlock_no_lock(void *ctx)
{
struct bpf_ws_lock *lock = (void *)0x1;
bpf_wakeup_sources_read_unlock(lock);
return 0;
}
SEC("syscall")
__failure __msg("Possibly NULL pointer passed to trusted")
int wakeup_source_unlock_null(void *ctx)
{
bpf_wakeup_sources_read_unlock(NULL);
return 0;
}
SEC("syscall")
__failure __msg("R0 invalid mem access 'scalar'")
int wakeup_source_unsafe_dereference(void *ctx)
{
struct list_head *head = bpf_wakeup_sources_get_head();
if (head->next)
return 1;
return 0;
}
char _license[] SEC("license") = "GPL";