bpf-fixes

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+soXsSLHKoYyzcli6rmadz2vbToFAmoRvpcACgkQ6rmadz2v
 bTow3w/+L3PVujliBpziQFTnHJ2SoTiwUoyjJpsQOc2o1yDEWJXvTcnQ5EURah4t
 aDMjPBBWgDea7HHWvC/vbRf2D8AjCf3gZBBpzW6uTQ2F1whD3DRCZ4O6XPvfdJEQ
 R0JkqZyjBjH9fkKBy30PcF+XM9iJ5pY/mkx6nCrcYvsvbj5cIkZnmP03vBGh1jeI
 yanlYb6N2XHwQp98PKoiN4/BP4ZOQx2HhBX0TmhTcRXVAyyX5SQy4ukrp1y2CSji
 YjpM2qHdEMtMeFFwcy1K2hJwNbjhrvfgHaKbwSuM3eLjug2AMBX0zp/4Zvw7mb2o
 B6zMRo0UgOt+kJzunmqnfNe01YZ+Z+So+FkinLSTba91gwCgxa3Qm3gNsZBtxv5V
 ayrrrFoB1PCxsRJqC0Jio7WXY1JRUkusHOdzR/8pygmwcp+vy6XEzJwhGD+DeMcu
 T4VJj2bp1bCK4iZwqjyxNAoniYSIjwxzwVDw8s0Zz1Bk+92YJEnZatahFTYFzJRK
 G9hnJaht0dK960LnudBUwKXz37dvM3LxAAt0ckAepfHAOwwrdB5XhgLQjfPZejot
 J6FWsxVoS1L+lXV7104QPy2Y9zmJ7ElOzQHWRcoBWs7Srar1a+PUFD0nkuSKmPcu
 7P3ukMr6NyekE0zGlOWSZNetlZpdzvUrpuRY2WOIl+sezwCp2xg=
 =04VP
 -----END PGP SIGNATURE-----

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

Pull bpf fixes from Alexei Starovoitov:

 - Fix bpf_throw() and global subprog combination (Kumar Kartikeya
   Dwivedi)

 - Fix out of bounds access in BPF interpreter (Yazhou Tang)

 - Fix potential out of bounds access in inner per-cpu array map
   (Guannan Wang)

 - Reject NULL data/sig in bpf_verify_pkcs7_signature (KP Singh)

* tag 'bpf-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf:
  libbpf: fix off-by-one in emit_signature_match jump offset
  bpf: Reject NULL data/sig in bpf_verify_pkcs7_signature
  selftests/bpf: Cover global subprog exception leaks
  bpf: Check global subprog exception paths
  bpf: make bpf_session_is_return() reference optional
  bpf: Use array_map_meta_equal for percpu array inner map replacement
  selftests/bpf: Add test for large offset bpf-to-bpf call
  bpf: Fix s16 truncation for large bpf-to-bpf call offsets
  bpf: Fix out-of-bounds read in bpf_patch_call_args()
This commit is contained in:
Linus Torvalds 2026-05-24 09:53:17 -07:00
commit f0e77c598e
16 changed files with 251 additions and 27 deletions

View File

@ -2917,7 +2917,13 @@ int bpf_check_uarg_tail_zero(bpfptr_t uaddr, size_t expected_size,
int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size);
#ifndef CONFIG_BPF_JIT_ALWAYS_ON
void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
int bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
s32 bpf_call_args_imm(s16 idx);
#else
static inline s32 bpf_call_args_imm(s16 idx)
{
return 0;
}
#endif
struct btf *bpf_get_btf_vmlinux(void);

View File

@ -729,6 +729,7 @@ struct bpf_subprog_info {
*/
s16 fastcall_stack_off;
bool has_tail_call: 1;
bool might_throw: 1;
bool tail_call_reachable: 1;
bool has_ld_abs: 1;
bool is_cb: 1;
@ -1308,6 +1309,7 @@ void bpf_fmt_stack_mask(char *buf, ssize_t buf_sz, u64 stack_mask);
bool bpf_subprog_is_global(const struct bpf_verifier_env *env, int subprog);
int bpf_find_subprog(struct bpf_verifier_env *env, int off);
bool bpf_is_throw_kfunc(struct bpf_insn *insn);
int bpf_compute_const_regs(struct bpf_verifier_env *env);
int bpf_prune_dead_branches(struct bpf_verifier_env *env);
int bpf_check_cfg(struct bpf_verifier_env *env);

View File

@ -1151,9 +1151,6 @@ bool sk_filter_charge(struct sock *sk, struct sk_filter *fp);
void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp);
u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
#define __bpf_call_base_args \
((u64 (*)(u64, u64, u64, u64, u64, const struct bpf_insn *)) \
(void *)__bpf_call_base)
struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog);
void bpf_jit_compile(struct bpf_prog *prog);

View File

@ -827,7 +827,7 @@ const struct bpf_map_ops array_map_ops = {
};
const struct bpf_map_ops percpu_array_map_ops = {
.map_meta_equal = bpf_map_meta_equal,
.map_meta_equal = array_map_meta_equal,
.map_alloc_check = array_map_alloc_check,
.map_alloc = array_map_alloc,
.map_free = array_map_free,

View File

@ -64,11 +64,19 @@ static void mark_subprog_might_sleep(struct bpf_verifier_env *env, int off)
subprog->might_sleep = true;
}
static void mark_subprog_might_throw(struct bpf_verifier_env *env, int off)
{
struct bpf_subprog_info *subprog;
subprog = bpf_find_containing_subprog(env, off);
subprog->might_throw = true;
}
/* 't' is an index of a call-site.
* 'w' is a callee entry point.
* Eventually this function would be called when env->cfg.insn_state[w] == EXPLORED.
* Rely on DFS traversal order and absence of recursive calls to guarantee that
* callee's change_pkt_data marks would be correct at that moment.
* callee's effect marks would be correct at that moment.
*/
static void merge_callee_effects(struct bpf_verifier_env *env, int t, int w)
{
@ -78,6 +86,7 @@ static void merge_callee_effects(struct bpf_verifier_env *env, int t, int w)
callee = bpf_find_containing_subprog(env, w);
caller->changes_pkt_data |= callee->changes_pkt_data;
caller->might_sleep |= callee->might_sleep;
caller->might_throw |= callee->might_throw;
}
enum {
@ -509,6 +518,8 @@ static int visit_insn(int t, struct bpf_verifier_env *env)
mark_subprog_might_sleep(env, t);
if (ret == 0 && bpf_is_kfunc_pkt_changing(&meta))
mark_subprog_changes_pkt_data(env, t);
if (ret == 0 && bpf_is_throw_kfunc(insn))
mark_subprog_might_throw(env, t);
}
return visit_func_call_insn(t, insns, env, insn->src_reg == BPF_PSEUDO_CALL);

View File

@ -1771,6 +1771,9 @@ static u32 abs_s32(s32 x)
return x >= 0 ? (u32)x : -(u32)x;
}
static u64 (*interpreters_args[])(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5,
const struct bpf_insn *insn);
/**
* ___bpf_prog_run - run eBPF program on a given context
* @regs: is the array of MAX_BPF_EXT_REG eBPF pseudo-registers
@ -2077,10 +2080,9 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
CONT;
JMP_CALL_ARGS:
BPF_R0 = (__bpf_call_base_args + insn->imm)(BPF_R1, BPF_R2,
BPF_R3, BPF_R4,
BPF_R5,
insn + insn->off + 1);
BPF_R0 = interpreters_args[insn->off](BPF_R1, BPF_R2, BPF_R3,
BPF_R4, BPF_R5,
insn + insn->imm + 1);
CONT;
JMP_TAIL_CALL: {
@ -2394,13 +2396,22 @@ EVAL4(PROG_NAME_LIST, 416, 448, 480, 512)
#undef PROG_NAME_LIST
#ifdef CONFIG_BPF_SYSCALL
void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth)
int bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth)
{
stack_depth = max_t(u32, stack_depth, 1);
insn->off = (s16) insn->imm;
insn->imm = interpreters_args[(round_up(stack_depth, 32) / 32) - 1] -
__bpf_call_base_args;
/* Prevent out-of-bounds read to interpreters_args */
if (stack_depth > MAX_BPF_STACK)
return -EINVAL;
insn->off = (round_up(stack_depth, 32) / 32) - 1;
insn->code = BPF_JMP | BPF_CALL_ARGS;
return 0;
}
s32 bpf_call_args_imm(s16 idx)
{
if (WARN_ON_ONCE(idx < 0 || idx >= ARRAY_SIZE(interpreters_args)))
return 0;
return BPF_CALL_IMM(interpreters_args[idx]);
}
#endif
#endif

View File

@ -1250,9 +1250,9 @@ static int jit_subprogs(struct bpf_verifier_env *env)
}
if (!bpf_pseudo_call(insn))
continue;
insn->off = env->insn_aux_data[i].call_imm;
subprog = bpf_find_subprog(env, i + insn->off + 1);
insn->imm = subprog;
insn->imm = env->insn_aux_data[i].call_imm;
subprog = bpf_find_subprog(env, i + insn->imm + 1);
insn->off = subprog;
}
prog->jited = 1;
@ -1416,7 +1416,12 @@ int bpf_fixup_call_args(struct bpf_verifier_env *env)
depth = get_callee_stack_depth(env, insn, i);
if (depth < 0)
return depth;
bpf_patch_call_args(insn, depth);
err = bpf_patch_call_args(insn, depth);
if (err) {
verbose(env, "stack depth %d exceeds interpreter stack depth limit\n",
depth);
return err;
}
}
err = 0;
#endif

View File

@ -4241,8 +4241,13 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_p,
data_len = __bpf_dynptr_size(data_ptr);
data = __bpf_dynptr_data(data_ptr, data_len);
if (!data)
return -EINVAL;
sig_len = __bpf_dynptr_size(sig_ptr);
sig = __bpf_dynptr_data(sig_ptr, sig_len);
if (!sig)
return -EINVAL;
return verify_pkcs7_signature(data, data_len, sig, sig_len,
trusted_keyring->key,

View File

@ -4919,6 +4919,29 @@ static const struct bpf_map *bpf_map_from_imm(const struct bpf_prog *prog,
return map;
}
static void prepare_dump_pseudo_call(struct bpf_insn *insn)
{
s32 call_off = insn->imm;
/*
* BPF_CALL_ARGS only exists for interpreter fallback.
* 1. For interpreter (BPF_CALL_ARGS): insn->off is the index of
* interpreters_args array, so here using bpf_call_args_imm()
* to get the real address offset.
* 2. For JIT (BPF_CALL): insn->off is the subprog id.
*/
if (insn->code == (BPF_JMP | BPF_CALL_ARGS))
insn->imm = bpf_call_args_imm(insn->off);
else
insn->imm = insn->off;
/* Avoid dumping a truncated and misleading pc-relative offset. */
if (call_off > S16_MAX || call_off < S16_MIN)
insn->off = 0;
else
insn->off = call_off;
}
static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog,
const struct cred *f_cred)
{
@ -4944,6 +4967,9 @@ static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog,
}
if (code == (BPF_JMP | BPF_CALL) ||
code == (BPF_JMP | BPF_CALL_ARGS)) {
/* Restore the legacy xlated dump layout. */
if (insns[i].src_reg == BPF_PSEUDO_CALL)
prepare_dump_pseudo_call(&insns[i]);
if (code == (BPF_JMP | BPF_CALL_ARGS))
insns[i].code = BPF_JMP | BPF_CALL;
if (!bpf_dump_raw_ok(f_cred))

View File

@ -442,7 +442,6 @@ static bool is_dynptr_ref_function(enum bpf_func_id func_id)
static bool is_sync_callback_calling_kfunc(u32 btf_id);
static bool is_async_callback_calling_kfunc(u32 btf_id);
static bool is_callback_calling_kfunc(u32 btf_id);
static bool is_bpf_throw_kfunc(struct bpf_insn *insn);
static bool is_bpf_wq_set_callback_kfunc(u32 btf_id);
static bool is_task_work_add_kfunc(u32 func_id);
@ -5405,7 +5404,7 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
if (bpf_pseudo_kfunc_call(insn + i) && !insn[i].off) {
bool err = false;
if (!is_bpf_throw_kfunc(insn + i))
if (!bpf_is_throw_kfunc(insn + i))
continue;
for (tmp = idx; tmp >= 0 && !err; tmp = dinfo[tmp].caller) {
if (subprog[tmp].is_cb) {
@ -9499,6 +9498,9 @@ static int push_callback_call(struct bpf_verifier_env *env, struct bpf_insn *ins
return 0;
}
static int process_bpf_exit_full(struct bpf_verifier_env *env,
bool *do_print_state, bool exception_exit);
static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
int *insn_idx)
{
@ -9552,6 +9554,17 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
caller->regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG;
}
if (env->subprog_info[subprog].might_throw) {
struct bpf_verifier_state *branch;
branch = push_stack(env, *insn_idx + 1, *insn_idx, false);
if (IS_ERR(branch)) {
verbose(env, "failed to push state for global subprog exception path\n");
return PTR_ERR(branch);
}
return process_bpf_exit_full(env, NULL, true);
}
/* continue with next insn after call */
return 0;
}
@ -11263,7 +11276,11 @@ BTF_ID(func, bpf_task_work_schedule_resume)
BTF_ID(func, bpf_arena_alloc_pages)
BTF_ID(func, bpf_arena_free_pages)
BTF_ID(func, bpf_arena_reserve_pages)
#ifdef CONFIG_BPF_EVENTS
BTF_ID(func, bpf_session_is_return)
#else
BTF_ID_UNUSED
#endif
BTF_ID(func, bpf_stream_vprintk)
BTF_ID(func, bpf_stream_print_stack)
@ -11778,7 +11795,7 @@ static bool is_async_callback_calling_kfunc(u32 btf_id)
is_task_work_add_kfunc(btf_id);
}
static bool is_bpf_throw_kfunc(struct bpf_insn *insn)
bool bpf_is_throw_kfunc(struct bpf_insn *insn)
{
return bpf_pseudo_kfunc_call(insn) && insn->off == 0 &&
insn->imm == special_kfunc_list[KF_bpf_throw];
@ -12968,8 +12985,6 @@ static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_kfunc_ca
}
static int check_return_code(struct bpf_verifier_env *env, int regno, const char *reg_name);
static int process_bpf_exit_full(struct bpf_verifier_env *env,
bool *do_print_state, bool exception_exit);
static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
int *insn_idx_p)
@ -13350,7 +13365,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
if (meta.func_id == special_kfunc_list[KF_bpf_session_cookie])
env->prog->call_session_cookie = true;
if (is_bpf_throw_kfunc(insn))
if (bpf_is_throw_kfunc(insn))
return process_bpf_exit_full(env, NULL, true);
return 0;

View File

@ -592,13 +592,12 @@ static void emit_signature_match(struct bpf_gen *gen)
gen->hash_insn_offset[i] = gen->insn_cur - gen->insn_start;
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_3, 0, 0, 0, 0, 0));
off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 1;
off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 2;
if (is_simm16(off)) {
emit(gen, BPF_MOV64_IMM(BPF_REG_7, -EINVAL));
emit(gen, BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, off));
} else {
gen->error = -ERANGE;
emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, -1));
}
}
}

View File

@ -14,7 +14,7 @@ static struct {
const char *prog_name;
int expected_runtime_err;
} kfunc_dynptr_tests[] = {
{"dynptr_data_null", -EBADMSG},
{"dynptr_data_null", -EINVAL},
};
static bool kfunc_not_supported;

View File

@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>
/*
* Test that replacing an inner percpu array map with one that has different
* max_entries is rejected. percpu_array_map_gen_lookup() inlines the
* template's index_mask, so allowing a smaller replacement would cause OOB.
*/
void test_percpu_array_inner_map(void)
{
LIBBPF_OPTS(bpf_map_create_opts, opts);
int outer_fd, tmpl_fd, good_fd, bad_fd, err;
int zero = 0;
/* Create template: percpu array with 8 entries */
tmpl_fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, "tmpl",
sizeof(int), sizeof(long), 8, NULL);
if (!ASSERT_OK_FD(tmpl_fd, "create_tmpl"))
return;
/* Create outer array-of-maps using template */
opts.inner_map_fd = tmpl_fd;
outer_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY_OF_MAPS, "outer",
sizeof(int), sizeof(int), 1, &opts);
if (!ASSERT_OK_FD(outer_fd, "create_outer"))
goto close_tmpl;
/* Insert template as initial inner map */
err = bpf_map_update_elem(outer_fd, &zero, &tmpl_fd, 0);
if (!ASSERT_OK(err, "insert_tmpl"))
goto close_outer;
/* Replacement with same max_entries should succeed */
good_fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, "good",
sizeof(int), sizeof(long), 8, NULL);
if (!ASSERT_OK_FD(good_fd, "create_good"))
goto close_outer;
err = bpf_map_update_elem(outer_fd, &zero, &good_fd, 0);
ASSERT_OK(err, "replace_same_max_entries");
close(good_fd);
/* Replacement with fewer max_entries must fail */
bad_fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, "bad",
sizeof(int), sizeof(long), 2, NULL);
if (!ASSERT_OK_FD(bad_fd, "create_bad"))
goto close_outer;
err = bpf_map_update_elem(outer_fd, &zero, &bad_fd, 0);
ASSERT_ERR(err, "replace_smaller_max_entries");
close(bad_fd);
close_outer:
close(outer_fd);
close_tmpl:
close(tmpl_fd);
}

View File

@ -22,6 +22,7 @@
#include "verifier_bswap.skel.h"
#include "verifier_btf_ctx_access.skel.h"
#include "verifier_btf_unreliable_prog.skel.h"
#include "verifier_call_large_imm.skel.h"
#include "verifier_cfg.skel.h"
#include "verifier_cgroup_inv_retcode.skel.h"
#include "verifier_cgroup_skb.skel.h"
@ -170,6 +171,7 @@ void test_verifier_bpf_trap(void) { RUN(verifier_bpf_trap); }
void test_verifier_bswap(void) { RUN(verifier_bswap); }
void test_verifier_btf_ctx_access(void) { RUN(verifier_btf_ctx_access); }
void test_verifier_btf_unreliable_prog(void) { RUN(verifier_btf_unreliable_prog); }
void test_verifier_call_large_imm(void) { RUN(verifier_call_large_imm); }
void test_verifier_cfg(void) { RUN(verifier_cfg); }
void test_verifier_cgroup_inv_retcode(void) { RUN(verifier_cgroup_inv_retcode); }
void test_verifier_cgroup_skb(void) { RUN(verifier_cgroup_skb); }

View File

@ -208,6 +208,28 @@ int reject_with_reference(void *ctx)
return 0;
}
__noinline int global_subprog_may_throw(struct __sk_buff *ctx)
{
if (ctx->len)
bpf_throw(0);
return 0;
}
SEC("?tc")
__failure __msg("Unreleased reference")
int reject_global_subprog_throw_with_reference(struct __sk_buff *ctx)
{
struct foo *f;
f = bpf_obj_new(typeof(*f));
if (!f)
return 0;
if (ctx->protocol)
global_subprog_may_throw(ctx);
bpf_obj_drop(f);
return 0;
}
__noinline static int subprog_ref(struct __sk_buff *ctx)
{
struct foo *f;

View File

@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"
int call_happened = 0;
/*
* 32765 is the exact minimum number of padding instructions needed to
* trigger the verifier failure, because:
* 1. Counting the wrapper instructions around the padding block (one
* "r0=0" and two "exit" instructions), the actual jump distance
* evaluates to N + 3.
* 2. To overflow the s16 max bound (32767), we need N + 3 > 32767.
* Thus, N = 32765 is the exact minimum padding size required.
*/
static __attribute__((noinline)) void padding_subprog(void)
{
asm volatile (
"r0 = 0;"
".rept 32765;"
"r0 += 0;"
".endr;"
::: __clobber_all);
}
static __attribute__((noinline)) int target_subprog(void)
{
/* Use volatile variable here to prevent optimization. */
volatile int magic_ret = 3;
return magic_ret;
}
SEC("syscall")
__success __retval(3)
int call_large_imm_test(void *ctx)
{
/*
* Landing pad to handle call error on kernel without the fix,
* preventing kernel panic.
*/
asm volatile (
"r0 = 0;"
".rept 32768;"
"r0 += 0;"
".endr;"
::: __clobber_all);
/*
* The call_happened variable is 1 only when the call insn wrongly
* go back to the landing pad above.
*/
if (call_happened == 1) {
/* Use volatile variable here to prevent optimization. */
volatile int flag = -1;
return flag;
}
call_happened = 1;
padding_subprog();
return target_subprog();
}
char LICENSE[] SEC("license") = "GPL";