mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
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:
commit
94515f3a7d
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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) &&
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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"))
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
27
tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c
Normal file
27
tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c
Normal 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";
|
||||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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++) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user