mirror of
https://github.com/torvalds/linux.git
synced 2026-05-25 23:52:08 +02:00
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:
commit
f0e77c598e
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
66
tools/testing/selftests/bpf/progs/verifier_call_large_imm.c
Normal file
66
tools/testing/selftests/bpf/progs/verifier_call_large_imm.c
Normal 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";
|
||||
Loading…
Reference in New Issue
Block a user