BPF fixes:

- Fix a UAF in socket clone early bailout paths (Matt Bobrowski)
 
 - Reject unhashed UDP sockets on sockmap update to prevent refcount leaks
   (Michal Luczaj)
 
 - Account for receive queue data in FIONREAD on sockmap sockets without a
   verdict program (Mattia Meleleo)
 
 - Reject negative constant offsets for verifier buffer pointers (Sun Jian)
 
 - Fix for tracing of kfuncs with implicit arguments (Ihor Solodrai)
 
 Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQR0/74CM8h5+1E9pgtHLTd7Y1QvgwUCalp3/AAKCRBHLTd7Y1Qv
 g9J/AQCJzidAmStp1i074sZC7sC1uq+aYIvwycpMTp6hVgyP+AEAw7oKcnGkPGXs
 jeEDctaLtxBnPMXGAR5UaeqdNqu9ywQ=
 =zQgn
 -----END PGP SIGNATURE-----

Merge tag 'bpf-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf

Pull bpf fixes from Kumar Kartikeya Dwivedi:

 - Fix a UAF in socket clone early bailout paths (Matt Bobrowski)

 - Reject unhashed UDP sockets on sockmap update to prevent refcount
   leaks (Michal Luczaj)

 - Account for receive queue data in FIONREAD on sockmap sockets without
   a verdict program (Mattia Meleleo)

 - Reject negative constant offsets for verifier buffer pointers (Sun
   Jian)

 - Fix for tracing of kfuncs with implicit arguments (Ihor Solodrai)

* tag 'bpf-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf:
  selftests/bpf: Cover tracing implicit kfunc args
  bpf: Fix tracing of kfuncs with implicit args
  selftests/bpf: Cover negative buffer pointer offsets
  bpf: Reject negative const offsets for buffer pointers
  selftests/bpf: Test FIONREAD on a sockmap socket without a verdict program
  bpf, sockmap: Account for receive queue in FIONREAD without a verdict program
  selftests/bpf: Fail unbound UDP on sockmap update
  selftests/bpf: Adapt sockmap update error handling
  bpf, sockmap: Reject unhashed UDP sockets on sockmap update
  selftests/bpf: Ensure UDP sockets are bound
  bpf: Fix UAF in sock clone early bailouts
This commit is contained in:
Linus Torvalds 2026-07-17 12:55:24 -07:00
commit 94515f3a7d
18 changed files with 397 additions and 105 deletions

View File

@ -578,6 +578,7 @@ const char *btf_str_by_offset(const struct btf *btf, u32 offset);
struct btf *btf_parse_vmlinux(void);
struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog);
u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog);
int btf_kfunc_check_flag(const struct btf *btf, u32 kfunc_btf_id, u32 flag);
bool btf_kfunc_is_allowed(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog);
u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
const struct bpf_prog *prog);

View File

@ -551,20 +551,6 @@ static inline void psock_progs_drop(struct sk_psock_progs *progs)
psock_set_prog(&progs->skb_verdict, NULL);
}
/* for tcp only, sk is locked */
static inline ssize_t sk_psock_msg_inq(struct sock *sk)
{
struct sk_psock *psock;
ssize_t inq = 0;
psock = sk_psock_get(sk);
if (likely(psock)) {
inq = sk_psock_get_msg_len_nolock(psock);
sk_psock_put(sk, psock);
}
return inq;
}
/* for udp only, sk is not locked */
static inline ssize_t sk_msg_first_len(struct sock *sk)
{

View File

@ -9114,6 +9114,35 @@ u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_p
return btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
}
/*
* Check a single KF_* @flag on a kfunc across all of its hook sets.
* Returns:
* * 1 if @flag is set
* * 0 if @flag is not set
* * -EINVAL if @flag is set inconsistently across the sets
* * -ENOENT if kfunc_btf_id is not a registered kfunc
*/
int btf_kfunc_check_flag(const struct btf *btf, u32 kfunc_btf_id, u32 flag)
{
enum btf_kfunc_hook hook;
int res = -ENOENT;
bool is_set;
u32 *flags;
for (hook = 0; hook < BTF_KFUNC_HOOK_MAX; hook++) {
flags = btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
if (!flags)
continue;
is_set = *flags & flag;
if (res < 0)
res = is_set;
else if (res != is_set)
return -EINVAL;
}
return res;
}
u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
const struct bpf_prog *prog)
{

View File

@ -2584,24 +2584,25 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset)
#define KF_IMPL_SUFFIX "_impl"
static const struct btf_type *find_kfunc_impl_proto(struct bpf_verifier_env *env,
static const struct btf_type *find_kfunc_impl_proto(struct bpf_verifier_log *log,
struct btf *btf,
const char *func_name)
{
char *buf = env->tmp_str_buf;
const struct btf_type *func;
char buf[KSYM_NAME_LEN];
s32 impl_id;
int len;
len = snprintf(buf, TMP_STR_BUF_LEN, "%s%s", func_name, KF_IMPL_SUFFIX);
if (len < 0 || len >= TMP_STR_BUF_LEN) {
verbose(env, "function name %s%s is too long\n", func_name, KF_IMPL_SUFFIX);
len = snprintf(buf, sizeof(buf), "%s%s", func_name, KF_IMPL_SUFFIX);
if (len < 0 || len >= sizeof(buf)) {
bpf_log(log, "function name %s%s is too long\n",
func_name, KF_IMPL_SUFFIX);
return NULL;
}
impl_id = btf_find_by_name_kind(btf, buf, BTF_KIND_FUNC);
if (impl_id <= 0) {
verbose(env, "cannot find function %s in BTF\n", buf);
bpf_log(log, "cannot find function %s in BTF\n", buf);
return NULL;
}
@ -2653,7 +2654,7 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env,
* can be found through the counterpart _impl kfunc.
*/
if (kfunc_flags && (*kfunc_flags & KF_IMPLICIT_ARGS))
func_proto = find_kfunc_impl_proto(env, btf, func_name);
func_proto = find_kfunc_impl_proto(&env->log, btf, func_name);
else
func_proto = btf_type_by_id(btf, func->type);
@ -5326,14 +5327,11 @@ static int check_max_stack_depth(struct bpf_verifier_env *env)
static int __check_buffer_access(struct bpf_verifier_env *env,
const char *buf_info,
const struct bpf_reg_state *reg,
argno_t argno, int off, int size)
argno_t argno, int off, int size,
u32 *access_end)
{
if (off < 0) {
verbose(env,
"%s invalid %s buffer access: off=%d, size=%d\n",
reg_arg_name(env, argno), buf_info, off, size);
return -EACCES;
}
s64 start;
if (!tnum_is_const(reg->var_off)) {
char tn_buf[48];
@ -5344,6 +5342,15 @@ static int __check_buffer_access(struct bpf_verifier_env *env,
return -EACCES;
}
start = (s64)reg->var_off.value + off;
if (start < 0) {
verbose(env,
"%s invalid negative %s buffer offset: off=%d, var_off=%lld\n",
reg_arg_name(env, argno), buf_info, off, (s64)reg->var_off.value);
return -EACCES;
}
*access_end = start + size;
return 0;
}
@ -5351,14 +5358,14 @@ static int check_tp_buffer_access(struct bpf_verifier_env *env,
const struct bpf_reg_state *reg,
argno_t argno, int off, int size)
{
u32 access_end;
int err;
err = __check_buffer_access(env, "tracepoint", reg, argno, off, size);
err = __check_buffer_access(env, "tracepoint", reg, argno, off, size, &access_end);
if (err)
return err;
env->prog->aux->max_tp_access = max(reg->var_off.value + off + size,
env->prog->aux->max_tp_access);
env->prog->aux->max_tp_access = max(access_end, env->prog->aux->max_tp_access);
return 0;
}
@ -5370,13 +5377,14 @@ static int check_buffer_access(struct bpf_verifier_env *env,
u32 *max_access)
{
const char *buf_info = type_is_rdonly_mem(reg->type) ? "rdonly" : "rdwr";
u32 access_end;
int err;
err = __check_buffer_access(env, buf_info, reg, argno, off, size);
err = __check_buffer_access(env, buf_info, reg, argno, off, size, &access_end);
if (err)
return err;
*max_access = max(reg->var_off.value + off + size, *max_access);
*max_access = max(access_end, *max_access);
return 0;
}
@ -18873,6 +18881,47 @@ static int btf_id_allow_sleepable(u32 btf_id, unsigned long addr, const struct b
return -EINVAL;
}
/*
* Resolve the prototype describing a trace target's real ABI. A
* KF_IMPLICIT_ARGS kfunc has its injected args stripped from the public
* prototype, so use the _impl prototype; other targets use their own.
*/
static const struct btf_type *
btf_attach_func_proto(struct bpf_verifier_log *log, struct btf *btf, u32 func_id)
{
const struct btf_type *func;
struct module *mod = NULL;
const char *name;
int implicit;
func = btf_type_by_id(btf, func_id);
if (!func || !btf_type_is_func(func))
return NULL;
name = btf_name_by_offset(btf, func->name_off);
/*
* btf_kfunc_check_flag() reads kfunc_set_tab, which for a module is
* stable only once it is live; hold a module ref across the read to
* exclude a concurrent module load.
*/
if (btf_is_module(btf)) {
mod = btf_try_get_module(btf);
if (!mod)
return NULL;
}
implicit = btf_kfunc_check_flag(btf, func_id, KF_IMPLICIT_ARGS);
module_put(mod);
if (implicit == -EINVAL) {
bpf_log(log, "kfunc %s has inconsistent KF_IMPLICIT_ARGS\n", name);
return NULL;
}
if (implicit > 0)
return find_kfunc_impl_proto(log, btf, name);
return btf_type_by_id(btf, func->type);
}
int bpf_check_attach_target(struct bpf_verifier_log *log,
const struct bpf_prog *prog,
const struct bpf_prog *tgt_prog,
@ -19121,8 +19170,8 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
if (prog_extension &&
btf_check_type_match(log, prog, btf, t))
return -EINVAL;
t = btf_type_by_id(btf, t->type);
if (!btf_type_is_func_proto(t))
t = btf_attach_func_proto(log, btf, btf_id);
if (!t || !btf_type_is_func_proto(t))
return -EINVAL;
if ((prog->aux->saved_dst_prog_type || prog->aux->saved_dst_attach_type) &&
@ -19405,10 +19454,8 @@ int bpf_check_attach_btf_id_multi(struct btf *btf, struct bpf_prog *prog, u32 bt
tname = btf_name_by_offset(btf, t->name_off);
if (!tname)
return -EINVAL;
if (!btf_type_is_func(t))
return -EINVAL;
t = btf_type_by_id(btf, t->type);
if (!btf_type_is_func_proto(t))
t = btf_attach_func_proto(NULL, btf, btf_id);
if (!t || !btf_type_is_func_proto(t))
return -EINVAL;
err = btf_distill_func_proto(NULL, btf, t, tname, &tgt_info->fmodel);
if (err < 0)

View File

@ -158,8 +158,6 @@ int bpf_sk_storage_clone(const struct sock *sk, struct sock *newsk)
struct bpf_local_storage_elem *selem;
int ret = 0;
RCU_INIT_POINTER(newsk->sk_bpf_storage, NULL);
rcu_read_lock_dont_migrate();
sk_storage = rcu_dereference(sk->sk_bpf_storage);

View File

@ -2492,6 +2492,9 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,
sock_copy(newsk, sk);
newsk->sk_prot_creator = prot;
#ifdef CONFIG_BPF_SYSCALL
RCU_INIT_POINTER(newsk->sk_bpf_storage, NULL);
#endif
/* SANITY */
if (likely(newsk->sk_net_refcnt)) {

View File

@ -542,6 +542,8 @@ static bool sock_map_sk_state_allowed(const struct sock *sk)
{
if (sk_is_tcp(sk))
return (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_LISTEN);
if (sk_is_udp(sk))
return sk_hashed(sk);
if (sk_is_stream_unix(sk))
return (1 << READ_ONCE(sk->sk_state)) & TCPF_ESTABLISHED;
if (sk_is_vsock(sk) &&

View File

@ -334,6 +334,7 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk,
static int tcp_bpf_ioctl(struct sock *sk, int cmd, int *karg)
{
struct sk_psock *psock;
bool slow;
if (cmd != SIOCINQ)
@ -344,7 +345,21 @@ static int tcp_bpf_ioctl(struct sock *sk, int cmd, int *karg)
return -EINVAL;
slow = lock_sock_fast(sk);
*karg = sk_psock_msg_inq(sk);
psock = sk_psock_get(sk);
if (unlikely(!psock)) {
unlock_sock_fast(sk, slow);
return tcp_ioctl(sk, cmd, karg);
}
*karg = sk_psock_get_msg_len_nolock(psock);
/* Without a verdict program, ingress data is never diverted to
* ingress_msg: it stays in sk_receive_queue and is read through
* the fallback to tcp_recvmsg(), so account for it like
* tcp_ioctl() does.
*/
if (!READ_ONCE(psock->progs.stream_verdict) &&
!READ_ONCE(psock->progs.skb_verdict))
*karg += tcp_inq(sk);
sk_psock_put(sk, psock);
unlock_sock_fast(sk, slow);
return 0;

View File

@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
#include <test_progs.h>
#include "kfunc_implicit_args_tracing.skel.h"
void test_kfunc_implicit_args_tracing(void)
{
struct kfunc_implicit_args_tracing *skel;
LIBBPF_OPTS(bpf_test_run_opts, topts);
int err, fd;
skel = kfunc_implicit_args_tracing__open_and_load();
if (!ASSERT_OK_PTR(skel, "open_and_load"))
return;
err = kfunc_implicit_args_tracing__attach(skel);
if (!ASSERT_OK(err, "attach"))
goto cleanup;
fd = bpf_program__fd(skel->progs.trigger_implicit_arg);
err = bpf_prog_test_run_opts(fd, &topts);
if (!ASSERT_OK(err, "test_run"))
goto cleanup;
ASSERT_EQ(topts.retval, 5, "kfunc_retval");
ASSERT_EQ(skel->bss->fentry_arg_cnt, 2, "fentry_arg_cnt");
ASSERT_NEQ(skel->bss->fentry_aux_arg, 0, "fentry_aux_arg");
ASSERT_EQ(skel->bss->fentry_result, 1, "fentry_result");
ASSERT_EQ(skel->bss->fexit_arg_cnt, 2, "fexit_arg_cnt");
ASSERT_NEQ(skel->bss->fexit_aux_arg, 0, "fexit_aux_arg");
ASSERT_EQ(skel->bss->fexit_result, 1, "fexit_result");
cleanup:
kfunc_implicit_args_tracing__destroy(skel);
}

View File

@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>
#include "test_kmods/bpf_testmod.h"
#include "bpf_util.h"
static void check_attach_reject(const struct bpf_insn *program, size_t prog_len)
{
LIBBPF_OPTS(bpf_prog_load_opts, opts);
char error[4096];
int bpf_fd, tp_fd;
opts.log_level = 2;
opts.log_buf = error;
opts.log_size = sizeof(error);
bpf_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, NULL, "GPL v2",
program, prog_len, &opts);
if (!ASSERT_GE(bpf_fd, 0, "prog_load"))
return;
tp_fd = bpf_raw_tracepoint_open("bpf_testmod_test_writable_bare_tp", bpf_fd);
ASSERT_EQ(tp_fd, -EINVAL, "bpf_raw_tracepoint_open");
if (tp_fd >= 0)
close(tp_fd);
close(bpf_fd);
}
void test_raw_tp_writable_reject_bad_access(void)
{
const struct bpf_insn program[] = {
/* r6 is our tp buffer */
BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
/* one byte beyond the end of the writable context */
BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6,
sizeof(struct bpf_testmod_test_writable_ctx)),
BPF_EXIT_INSN(),
};
const struct bpf_insn negative_var_off_program[] = {
BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
/* make var_off negative, but keep the effective access offset non-negative */
BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8),
/* one byte beyond the end of the writable context */
BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6,
sizeof(struct bpf_testmod_test_writable_ctx) + 8),
BPF_EXIT_INSN(),
};
if (test__start_subtest("past_end"))
check_attach_reject(program, ARRAY_SIZE(program));
if (test__start_subtest("negative_var_off_past_end"))
check_attach_reject(negative_var_off_program,
ARRAY_SIZE(negative_var_off_program));
}

View File

@ -1,43 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>
#include <linux/nbd.h>
#include "bpf_util.h"
void test_raw_tp_writable_reject_nbd_invalid(void)
{
__u32 duration = 0;
char error[4096];
int bpf_fd = -1, tp_fd = -1;
const struct bpf_insn program[] = {
/* r6 is our tp buffer */
BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
/* one byte beyond the end of the nbd_request struct */
BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6,
sizeof(struct nbd_request)),
BPF_EXIT_INSN(),
};
LIBBPF_OPTS(bpf_prog_load_opts, opts,
.log_level = 2,
.log_buf = error,
.log_size = sizeof(error),
);
bpf_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, NULL, "GPL v2",
program, ARRAY_SIZE(program),
&opts);
if (CHECK(bpf_fd < 0, "bpf_raw_tracepoint_writable load",
"failed: %d errno %d\n", bpf_fd, errno))
return;
tp_fd = bpf_raw_tracepoint_open("nbd_send_request", bpf_fd);
if (CHECK(tp_fd >= 0, "bpf_raw_tracepoint_writable open",
"erroneously succeeded\n"))
goto out_bpffd;
close(tp_fd);
out_bpffd:
close(bpf_fd);
}

View File

@ -853,7 +853,7 @@ static void test_sockmap_many_socket(void)
return;
}
udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK);
if (udp < 0) {
close(dgram);
close(tcp);
@ -922,7 +922,7 @@ static void test_sockmap_many_maps(void)
return;
}
udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK);
if (udp < 0) {
close(dgram);
close(tcp);
@ -993,7 +993,7 @@ static void test_sockmap_same_sock(void)
return;
}
udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK);
if (udp < 0) {
close(dgram);
close(tcp);
@ -1373,6 +1373,43 @@ static void test_sockmap_multi_channels(int sotype)
test_sockmap_pass_prog__destroy(skel);
}
/* A socket in a sockmap without a verdict program keeps its ingress data
* in sk_receive_queue: FIONREAD must account for it.
*/
static void test_sockmap_no_verdict_fionread(void)
{
int err, map, zero = 0, sent, avail;
int c0 = -1, c1 = -1, p0 = -1, p1 = -1;
struct test_sockmap_pass_prog *skel;
char buf[256] = "0123456789";
skel = test_sockmap_pass_prog__open_and_load();
if (!ASSERT_OK_PTR(skel, "open_and_load"))
return;
map = bpf_map__fd(skel->maps.sock_map_rx);
err = create_socket_pairs(AF_INET, SOCK_STREAM, &c0, &c1, &p0, &p1);
if (!ASSERT_OK(err, "create_socket_pairs()"))
goto out;
err = bpf_map_update_elem(map, &zero, &c1, BPF_NOEXIST);
if (!ASSERT_OK(err, "bpf_map_update_elem(c1)"))
goto out_close;
sent = xsend(p1, &buf, sizeof(buf), 0);
ASSERT_EQ(sent, sizeof(buf), "xsend(p1)");
avail = wait_for_fionread(c1, sizeof(buf), IO_TIMEOUT_SEC);
ASSERT_EQ(avail, sizeof(buf), "ioctl(FIONREAD)");
out_close:
close(c0);
close(p0);
close(c1);
close(p1);
out:
test_sockmap_pass_prog__destroy(skel);
}
void test_sockmap_basic(void)
{
if (test__start_subtest("sockmap create_update_free"))
@ -1415,6 +1452,8 @@ void test_sockmap_basic(void)
test_sockmap_skb_verdict_shutdown();
if (test__start_subtest("sockmap skb_verdict fionread"))
test_sockmap_skb_verdict_fionread(true);
if (test__start_subtest("sockmap no_verdict fionread"))
test_sockmap_no_verdict_fionread();
if (test__start_subtest("sockmap skb_verdict fionread on drop"))
test_sockmap_skb_verdict_fionread(false);
if (test__start_subtest("sockmap skb_verdict change tail"))

View File

@ -53,8 +53,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused,
int family, int sotype, int mapfd)
{
u32 key = 0;
u64 value;
int err, s;
u64 value;
s = xsocket(family, sotype, 0);
if (s == -1)
@ -63,11 +63,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused,
errno = 0;
value = s;
err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST);
if (sotype == SOCK_STREAM) {
if (!err || errno != EOPNOTSUPP)
FAIL_ERRNO("map_update: expected EOPNOTSUPP");
} else if (err)
FAIL_ERRNO("map_update: expected success");
ASSERT_ERR(err, "map_update");
ASSERT_EQ(errno, EOPNOTSUPP, "errno");
xclose(s);
}
@ -77,8 +74,8 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused,
struct sockaddr_storage addr;
socklen_t len = 0;
u32 key = 0;
u64 value;
int err, s;
u64 value;
init_addr_loopback(family, &addr, &len);
@ -93,8 +90,12 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused,
errno = 0;
value = s;
err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST);
if (!err || errno != EOPNOTSUPP)
FAIL_ERRNO("map_update: expected EOPNOTSUPP");
if (sotype == SOCK_STREAM) {
ASSERT_ERR(err, "map_update");
ASSERT_EQ(errno, EOPNOTSUPP, "errno");
} else {
ASSERT_OK(err, "map_update");
}
close:
xclose(s);
}
@ -1289,7 +1290,7 @@ static void test_ops(struct test_sockmap_listen *skel, struct bpf_map *map,
/* insert */
TEST(test_insert_invalid),
TEST(test_insert_opened),
TEST(test_insert_bound, SOCK_STREAM),
TEST(test_insert_bound),
TEST(test_insert),
/* delete */
TEST(test_delete_after_insert),

View File

@ -78,6 +78,7 @@
#include "verifier_precision.skel.h"
#include "verifier_prevent_map_lookup.skel.h"
#include "verifier_private_stack.skel.h"
#include "verifier_ptr_to_buf.skel.h"
#include "verifier_raw_stack.skel.h"
#include "verifier_raw_tp_writable.skel.h"
#include "verifier_reg_equal.skel.h"
@ -230,6 +231,7 @@ void test_verifier_or_jmp32_k(void) { RUN(verifier_or_jmp32_k); }
void test_verifier_precision(void) { RUN(verifier_precision); }
void test_verifier_prevent_map_lookup(void) { RUN(verifier_prevent_map_lookup); }
void test_verifier_private_stack(void) { RUN(verifier_private_stack); }
void test_verifier_ptr_to_buf(void) { RUN(verifier_ptr_to_buf); }
void test_verifier_raw_stack(void) { RUN(verifier_raw_stack); }
void test_verifier_raw_tp_writable(void) { RUN(verifier_raw_tp_writable); }
void test_verifier_reg_equal(void) { RUN(verifier_reg_equal); }

View File

@ -0,0 +1,77 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <errno.h>
extern int bpf_kfunc_implicit_arg(int a) __weak __ksym;
char _license[] SEC("license") = "GPL";
/* Shared arg checks; reports arg count and aux, returns 1 on success. */
static __always_inline __u64
check_implicit_args(void *ctx, __u64 *arg_cnt, __u64 *aux_arg)
{
__u64 a = 0, aux = 0, z = 0;
__u64 result;
__s64 err;
*arg_cnt = bpf_get_func_arg_cnt(ctx);
result = *arg_cnt == 2;
err = bpf_get_func_arg(ctx, 0, &a);
result &= err == 0 && (int)a == 5;
err = bpf_get_func_arg(ctx, 1, &aux);
*aux_arg = aux;
result &= err == 0 && aux != 0;
err = bpf_get_func_arg(ctx, 2, &z);
result &= err == -EINVAL;
return result;
}
__u64 fentry_result;
__u64 fentry_arg_cnt;
__u64 fentry_aux_arg;
SEC("fentry/bpf_kfunc_implicit_arg")
int BPF_PROG(trace_implicit_arg_fentry)
{
__u64 ret = 0;
__s64 err;
fentry_result = check_implicit_args(ctx, &fentry_arg_cnt, &fentry_aux_arg);
err = bpf_get_func_ret(ctx, &ret);
fentry_result &= err == -EOPNOTSUPP;
return 0;
}
__u64 fexit_result;
__u64 fexit_arg_cnt;
__u64 fexit_aux_arg;
SEC("fexit/bpf_kfunc_implicit_arg")
int BPF_PROG(trace_implicit_arg_fexit)
{
__u64 ret = 0;
__s64 err;
fexit_result = check_implicit_args(ctx, &fexit_arg_cnt, &fexit_aux_arg);
err = bpf_get_func_ret(ctx, &ret);
fexit_result &= err == 0 && ret == 5;
return 0;
}
SEC("syscall")
int trigger_implicit_arg(void *ctx)
{
return bpf_kfunc_implicit_arg(5);
}

View File

@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"
SEC("iter/bpf_map_elem")
__description("PTR_TO_BUF: reject negative const offset")
__failure
__msg("invalid negative rdwr buffer offset")
__naked void ptr_to_buf_reject_negative_const_offset(void)
{
asm volatile ("r0 = 0; \
r2 = *(u64 *)(r1 + %[value_off]); \
if r2 == 0 goto l0_%=; \
r2 += -8; \
r0 = *(u64 *)(r2 + 0); \
l0_%=: \
exit; \
"
:
: __imm_const(value_off,
offsetof(struct bpf_iter__bpf_map_elem, value))
: __clobber_all);
}
char _license[] SEC("license") = "GPL";

View File

@ -47,4 +47,20 @@ l0_%=: /* shift the buffer pointer to a variable location */\
: __clobber_all);
}
SEC("raw_tracepoint.w")
__description("raw_tracepoint_writable: reject negative const offset")
__failure
__msg("invalid negative tracepoint buffer offset")
__naked void tracepoint_writable_reject_negative_const_offset(void)
{
asm volatile (" \
r6 = *(u64 *)(r1 + 0); \
r6 += -8; \
r0 = *(u64 *)(r6 + 0); \
exit; \
" :
:
: __clobber_all);
}
char _license[] SEC("license") = "GPL";

View File

@ -759,16 +759,15 @@ static void test_sockmap(unsigned int tasks, void *data)
goto out_sockmap;
}
/* Test update with unsupported UDP socket */
/* Test update with unsupported unbound UDP socket */
udp = socket(AF_INET, SOCK_DGRAM, 0);
i = 0;
err = bpf_map_update_elem(fd, &i, &udp, BPF_ANY);
if (err) {
printf("Failed socket update SOCK_DGRAM '%i:%i'\n",
i, udp);
CHECK(udp < 0, "socket(AF_INET, SOCK_DGRAM)", "errno:%d\n", errno);
err = bpf_map_update_elem(fd, &(int){0}, &udp, BPF_ANY);
close(udp);
if (!err) {
printf("Unexpectedly succeeded unbound UDP update '0:%i'\n", udp);
goto out_sockmap;
}
close(udp);
/* Test update without programs */
for (i = 0; i < 6; i++) {