mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-08 12:52:40 -04:00
Merge branch 'irq-save-restore'
Kumar Kartikeya Dwivedi says: ==================== IRQ save/restore This set introduces support for managing IRQ state from BPF programs. Two new kfuncs, bpf_local_irq_save, and bpf_local_irq_restore are introduced to enable this functionality. Intended use cases are writing IRQ safe data structures (e.g. memory allocator) in BPF programs natively, and use in new spin locking primitives intended to be introduced in the next few weeks. The set begins with some refactoring patches before the actual functionality is introduced. Patch 1 consolidates all resource related state in bpf_verifier_state, and moves it out from bpf_func_state. Patch 2 refactor acquire and release functions for reference state to make them reusable without duplication for other resource types. After this, patch 3 refactors stack slot liveness marking logic to be shared between dynptr, and iterators, in preparation for introducing same logic for irq flag object on stack. Finally, patch 4 and 7 introduce the new kfuncs and their selftests. For more details, please inspect the patch commit logs. Patch 5 makes the error message in case of resource leaks under BPF_EXIT a bit clearer. Patch 6 expands coverage of existing preempt-disable selftest to cover sleepable kfuncs. See individual patches for more details. Changelog: ---------- v5 -> v6 v5: https://lore.kernel.org/bpf/20241129001632.3828611-1-memxor@gmail.com * Add Eduard's Acked-by on patch 2 * Remove gen_id parameter to acquire_reference_state (Alexei) * Remove space before REF_TYPE_LOCK (Alexei) * Fix link to v4 in changelog v4 -> v5 v4: https://lore.kernel.org/bpf/20241127213535.3657472-1-memxor@gmail.com * Do regno - 1 when printing argument * Pass verifier state explicitly into print_{insn,verifier}_state (Eduard) * Pass frameno instead of bpf_func_state (Eduard) * Move bpf_reference_state *refs after parent to fill two holes in bpf_verifier_state (Eduard). The hunk fixing that bug is in the commit adding IRQ save/restore kfuncs, as it is only needed then. * Fix bug in release_reference_state breaking stack property (Eduard) * Add selftest for triggering and reproducing bug found by Eduard irq_ooo_refs_array in final patch * Print insn_idx and active_irq_id on error (Eduard) * Add more acks v3 -> v4 v3: https://lore.kernel.org/bpf/20241127165846.2001009-1-memxor@gmail.com * Add yet another missing kfunc declaration to silence s390 CI v2 -> v3 v2: https://lore.kernel.org/bpf/20241127153306.1484562-1-memxor@gmail.com * Drop REF_TYPE_LOCK_MASK * Add kfunc declarations to selftest to silence s390 CI errors v1 -> v2 v1: https://lore.kernel.org/bpf/20241121005329.408873-1-memxor@gmail.com * Drop reference -> resource renaming in the verifier (Eduard, Alexei) * Change verifier log for check_resource_leak for BPF_EXIT (Eduard) * Remove id parameter from acquire_resource_state, read s->id (Eduard) * Rename erase to release for reference state (Eduard) * Move resource state to bpf_verifier_state (Eduard, Alexei) * Drop unnecessary casting to/from u64 in helpers (Eduard) * Add test for arg != PTR_TO_STACK (Eduard) * Drop now redundant tests (Eduard) * Address some other misc nits * Add Reviewed-by and Acked-by from Eduard ==================== Link: https://patch.msgid.link/20241204030400.208005-1-memxor@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
@@ -233,6 +233,7 @@ enum bpf_stack_slot_type {
|
||||
*/
|
||||
STACK_DYNPTR,
|
||||
STACK_ITER,
|
||||
STACK_IRQ_FLAG,
|
||||
};
|
||||
|
||||
#define BPF_REG_SIZE 8 /* size of eBPF register in bytes */
|
||||
@@ -254,8 +255,9 @@ struct bpf_reference_state {
|
||||
* default to pointer reference on zero initialization of a state.
|
||||
*/
|
||||
enum ref_state_type {
|
||||
REF_TYPE_PTR = 0,
|
||||
REF_TYPE_LOCK,
|
||||
REF_TYPE_PTR = 1,
|
||||
REF_TYPE_IRQ = 2,
|
||||
REF_TYPE_LOCK = 3,
|
||||
} type;
|
||||
/* Track each reference created with a unique id, even if the same
|
||||
* instruction creates the reference multiple times (eg, via CALL).
|
||||
@@ -315,9 +317,6 @@ struct bpf_func_state {
|
||||
u32 callback_depth;
|
||||
|
||||
/* The following fields should be last. See copy_func_state() */
|
||||
int acquired_refs;
|
||||
int active_locks;
|
||||
struct bpf_reference_state *refs;
|
||||
/* The state of the stack. Each element of the array describes BPF_REG_SIZE
|
||||
* (i.e. 8) bytes worth of stack memory.
|
||||
* stack[0] represents bytes [*(r10-8)..*(r10-1)]
|
||||
@@ -370,6 +369,8 @@ struct bpf_verifier_state {
|
||||
/* call stack tracking */
|
||||
struct bpf_func_state *frame[MAX_CALL_FRAMES];
|
||||
struct bpf_verifier_state *parent;
|
||||
/* Acquired reference states */
|
||||
struct bpf_reference_state *refs;
|
||||
/*
|
||||
* 'branches' field is the number of branches left to explore:
|
||||
* 0 - all possible paths from this state reached bpf_exit or
|
||||
@@ -419,9 +420,13 @@ struct bpf_verifier_state {
|
||||
u32 insn_idx;
|
||||
u32 curframe;
|
||||
|
||||
bool speculative;
|
||||
u32 acquired_refs;
|
||||
u32 active_locks;
|
||||
u32 active_preempt_locks;
|
||||
u32 active_irq_id;
|
||||
bool active_rcu_lock;
|
||||
u32 active_preempt_lock;
|
||||
|
||||
bool speculative;
|
||||
/* If this state was ever pointed-to by other state's loop_entry field
|
||||
* this flag would be set to true. Used to avoid freeing such states
|
||||
* while they are still in use.
|
||||
@@ -979,8 +984,9 @@ const char *dynptr_type_str(enum bpf_dynptr_type type);
|
||||
const char *iter_type_str(const struct btf *btf, u32 btf_id);
|
||||
const char *iter_state_str(enum bpf_iter_state state);
|
||||
|
||||
void print_verifier_state(struct bpf_verifier_env *env,
|
||||
const struct bpf_func_state *state, bool print_all);
|
||||
void print_insn_state(struct bpf_verifier_env *env, const struct bpf_func_state *state);
|
||||
void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_verifier_state *vstate,
|
||||
u32 frameno, bool print_all);
|
||||
void print_insn_state(struct bpf_verifier_env *env, const struct bpf_verifier_state *vstate,
|
||||
u32 frameno);
|
||||
|
||||
#endif /* _LINUX_BPF_VERIFIER_H */
|
||||
|
||||
@@ -3057,6 +3057,21 @@ __bpf_kfunc int bpf_copy_from_user_str(void *dst, u32 dst__sz, const void __user
|
||||
return ret + 1;
|
||||
}
|
||||
|
||||
/* Keep unsinged long in prototype so that kfunc is usable when emitted to
|
||||
* vmlinux.h in BPF programs directly, but note that while in BPF prog, the
|
||||
* unsigned long always points to 8-byte region on stack, the kernel may only
|
||||
* read and write the 4-bytes on 32-bit.
|
||||
*/
|
||||
__bpf_kfunc void bpf_local_irq_save(unsigned long *flags__irq_flag)
|
||||
{
|
||||
local_irq_save(*flags__irq_flag);
|
||||
}
|
||||
|
||||
__bpf_kfunc void bpf_local_irq_restore(unsigned long *flags__irq_flag)
|
||||
{
|
||||
local_irq_restore(*flags__irq_flag);
|
||||
}
|
||||
|
||||
__bpf_kfunc_end_defs();
|
||||
|
||||
BTF_KFUNCS_START(generic_btf_ids)
|
||||
@@ -3149,6 +3164,8 @@ BTF_ID_FLAGS(func, bpf_get_kmem_cache)
|
||||
BTF_ID_FLAGS(func, bpf_iter_kmem_cache_new, KF_ITER_NEW | KF_SLEEPABLE)
|
||||
BTF_ID_FLAGS(func, bpf_iter_kmem_cache_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPABLE)
|
||||
BTF_ID_FLAGS(func, bpf_iter_kmem_cache_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
|
||||
BTF_ID_FLAGS(func, bpf_local_irq_save)
|
||||
BTF_ID_FLAGS(func, bpf_local_irq_restore)
|
||||
BTF_KFUNCS_END(common_btf_ids)
|
||||
|
||||
static const struct btf_kfunc_id_set common_kfunc_set = {
|
||||
|
||||
@@ -537,6 +537,7 @@ static char slot_type_char[] = {
|
||||
[STACK_ZERO] = '0',
|
||||
[STACK_DYNPTR] = 'd',
|
||||
[STACK_ITER] = 'i',
|
||||
[STACK_IRQ_FLAG] = 'f'
|
||||
};
|
||||
|
||||
static void print_liveness(struct bpf_verifier_env *env,
|
||||
@@ -753,9 +754,10 @@ static void print_reg_state(struct bpf_verifier_env *env,
|
||||
verbose(env, ")");
|
||||
}
|
||||
|
||||
void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_func_state *state,
|
||||
bool print_all)
|
||||
void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_verifier_state *vstate,
|
||||
u32 frameno, bool print_all)
|
||||
{
|
||||
const struct bpf_func_state *state = vstate->frame[frameno];
|
||||
const struct bpf_reg_state *reg;
|
||||
int i;
|
||||
|
||||
@@ -843,11 +845,11 @@ void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_func_st
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (state->acquired_refs && state->refs[0].id) {
|
||||
verbose(env, " refs=%d", state->refs[0].id);
|
||||
for (i = 1; i < state->acquired_refs; i++)
|
||||
if (state->refs[i].id)
|
||||
verbose(env, ",%d", state->refs[i].id);
|
||||
if (vstate->acquired_refs && vstate->refs[0].id) {
|
||||
verbose(env, " refs=%d", vstate->refs[0].id);
|
||||
for (i = 1; i < vstate->acquired_refs; i++)
|
||||
if (vstate->refs[i].id)
|
||||
verbose(env, ",%d", vstate->refs[i].id);
|
||||
}
|
||||
if (state->in_callback_fn)
|
||||
verbose(env, " cb");
|
||||
@@ -864,7 +866,8 @@ static inline u32 vlog_alignment(u32 pos)
|
||||
BPF_LOG_MIN_ALIGNMENT) - pos - 1;
|
||||
}
|
||||
|
||||
void print_insn_state(struct bpf_verifier_env *env, const struct bpf_func_state *state)
|
||||
void print_insn_state(struct bpf_verifier_env *env, const struct bpf_verifier_state *vstate,
|
||||
u32 frameno)
|
||||
{
|
||||
if (env->prev_log_pos && env->prev_log_pos == env->log.end_pos) {
|
||||
/* remove new line character */
|
||||
@@ -873,5 +876,5 @@ void print_insn_state(struct bpf_verifier_env *env, const struct bpf_func_state
|
||||
} else {
|
||||
verbose(env, "%d:", env->insn_idx);
|
||||
}
|
||||
print_verifier_state(env, state, false);
|
||||
print_verifier_state(env, vstate, frameno, false);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -98,6 +98,7 @@
|
||||
#include "verifier_xdp_direct_packet_access.skel.h"
|
||||
#include "verifier_bits_iter.skel.h"
|
||||
#include "verifier_lsm.skel.h"
|
||||
#include "irq.skel.h"
|
||||
|
||||
#define MAX_ENTRIES 11
|
||||
|
||||
@@ -225,6 +226,7 @@ void test_verifier_xdp(void) { RUN(verifier_xdp); }
|
||||
void test_verifier_xdp_direct_packet_access(void) { RUN(verifier_xdp_direct_packet_access); }
|
||||
void test_verifier_bits_iter(void) { RUN(verifier_bits_iter); }
|
||||
void test_verifier_lsm(void) { RUN(verifier_lsm); }
|
||||
void test_irq(void) { RUN(irq); }
|
||||
|
||||
void test_verifier_mtu(void)
|
||||
{
|
||||
|
||||
@@ -131,7 +131,7 @@ int reject_subprog_with_lock(void *ctx)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_rcu_read_lock-ed region")
|
||||
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_rcu_read_lock-ed region")
|
||||
int reject_with_rcu_read_lock(void *ctx)
|
||||
{
|
||||
bpf_rcu_read_lock();
|
||||
@@ -147,7 +147,7 @@ __noinline static int throwing_subprog(struct __sk_buff *ctx)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_rcu_read_lock-ed region")
|
||||
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_rcu_read_lock-ed region")
|
||||
int reject_subprog_with_rcu_read_lock(void *ctx)
|
||||
{
|
||||
bpf_rcu_read_lock();
|
||||
|
||||
444
tools/testing/selftests/bpf/progs/irq.c
Normal file
444
tools/testing/selftests/bpf/progs/irq.c
Normal file
@@ -0,0 +1,444 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "bpf_misc.h"
|
||||
#include "bpf_experimental.h"
|
||||
|
||||
unsigned long global_flags;
|
||||
|
||||
extern void bpf_local_irq_save(unsigned long *) __weak __ksym;
|
||||
extern void bpf_local_irq_restore(unsigned long *) __weak __ksym;
|
||||
extern int bpf_copy_from_user_str(void *dst, u32 dst__sz, const void *unsafe_ptr__ign, u64 flags) __weak __ksym;
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("arg#0 doesn't point to an irq flag on stack")
|
||||
int irq_save_bad_arg(struct __sk_buff *ctx)
|
||||
{
|
||||
bpf_local_irq_save(&global_flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("arg#0 doesn't point to an irq flag on stack")
|
||||
int irq_restore_bad_arg(struct __sk_buff *ctx)
|
||||
{
|
||||
bpf_local_irq_restore(&global_flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_local_irq_save-ed region")
|
||||
int irq_restore_missing_2(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags1;
|
||||
unsigned long flags2;
|
||||
|
||||
bpf_local_irq_save(&flags1);
|
||||
bpf_local_irq_save(&flags2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_local_irq_save-ed region")
|
||||
int irq_restore_missing_3(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags1;
|
||||
unsigned long flags2;
|
||||
unsigned long flags3;
|
||||
|
||||
bpf_local_irq_save(&flags1);
|
||||
bpf_local_irq_save(&flags2);
|
||||
bpf_local_irq_save(&flags3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_local_irq_save-ed region")
|
||||
int irq_restore_missing_3_minus_2(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags1;
|
||||
unsigned long flags2;
|
||||
unsigned long flags3;
|
||||
|
||||
bpf_local_irq_save(&flags1);
|
||||
bpf_local_irq_save(&flags2);
|
||||
bpf_local_irq_save(&flags3);
|
||||
bpf_local_irq_restore(&flags3);
|
||||
bpf_local_irq_restore(&flags2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __noinline void local_irq_save(unsigned long *flags)
|
||||
{
|
||||
bpf_local_irq_save(flags);
|
||||
}
|
||||
|
||||
static __noinline void local_irq_restore(unsigned long *flags)
|
||||
{
|
||||
bpf_local_irq_restore(flags);
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_local_irq_save-ed region")
|
||||
int irq_restore_missing_1_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(&flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_local_irq_save-ed region")
|
||||
int irq_restore_missing_2_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags1;
|
||||
unsigned long flags2;
|
||||
|
||||
local_irq_save(&flags1);
|
||||
local_irq_save(&flags2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_local_irq_save-ed region")
|
||||
int irq_restore_missing_3_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags1;
|
||||
unsigned long flags2;
|
||||
unsigned long flags3;
|
||||
|
||||
local_irq_save(&flags1);
|
||||
local_irq_save(&flags2);
|
||||
local_irq_save(&flags3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_local_irq_save-ed region")
|
||||
int irq_restore_missing_3_minus_2_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags1;
|
||||
unsigned long flags2;
|
||||
unsigned long flags3;
|
||||
|
||||
local_irq_save(&flags1);
|
||||
local_irq_save(&flags2);
|
||||
local_irq_save(&flags3);
|
||||
local_irq_restore(&flags3);
|
||||
local_irq_restore(&flags2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__success
|
||||
int irq_balance(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(&flags);
|
||||
local_irq_restore(&flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__success
|
||||
int irq_balance_n(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags1;
|
||||
unsigned long flags2;
|
||||
unsigned long flags3;
|
||||
|
||||
local_irq_save(&flags1);
|
||||
local_irq_save(&flags2);
|
||||
local_irq_save(&flags3);
|
||||
local_irq_restore(&flags3);
|
||||
local_irq_restore(&flags2);
|
||||
local_irq_restore(&flags1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __noinline void local_irq_balance(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(&flags);
|
||||
local_irq_restore(&flags);
|
||||
}
|
||||
|
||||
static __noinline void local_irq_balance_n(void)
|
||||
{
|
||||
unsigned long flags1;
|
||||
unsigned long flags2;
|
||||
unsigned long flags3;
|
||||
|
||||
local_irq_save(&flags1);
|
||||
local_irq_save(&flags2);
|
||||
local_irq_save(&flags3);
|
||||
local_irq_restore(&flags3);
|
||||
local_irq_restore(&flags2);
|
||||
local_irq_restore(&flags1);
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__success
|
||||
int irq_balance_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
local_irq_balance();
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
|
||||
__failure __msg("sleepable helper bpf_copy_from_user#")
|
||||
int irq_sleepable_helper(void *ctx)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 data;
|
||||
|
||||
local_irq_save(&flags);
|
||||
bpf_copy_from_user(&data, sizeof(data), NULL);
|
||||
local_irq_restore(&flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
|
||||
__failure __msg("kernel func bpf_copy_from_user_str is sleepable within IRQ-disabled region")
|
||||
int irq_sleepable_kfunc(void *ctx)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 data;
|
||||
|
||||
local_irq_save(&flags);
|
||||
bpf_copy_from_user_str(&data, sizeof(data), NULL, 0);
|
||||
local_irq_restore(&flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __noinline global_local_irq_balance(void)
|
||||
{
|
||||
local_irq_balance_n();
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("global function calls are not allowed with IRQs disabled")
|
||||
int irq_global_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
bpf_local_irq_save(&flags);
|
||||
global_local_irq_balance();
|
||||
bpf_local_irq_restore(&flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("cannot restore irq state out of order")
|
||||
int irq_restore_ooo(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags1;
|
||||
unsigned long flags2;
|
||||
|
||||
bpf_local_irq_save(&flags1);
|
||||
bpf_local_irq_save(&flags2);
|
||||
bpf_local_irq_restore(&flags1);
|
||||
bpf_local_irq_restore(&flags2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("cannot restore irq state out of order")
|
||||
int irq_restore_ooo_3(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags1;
|
||||
unsigned long flags2;
|
||||
unsigned long flags3;
|
||||
|
||||
bpf_local_irq_save(&flags1);
|
||||
bpf_local_irq_save(&flags2);
|
||||
bpf_local_irq_restore(&flags2);
|
||||
bpf_local_irq_save(&flags3);
|
||||
bpf_local_irq_restore(&flags1);
|
||||
bpf_local_irq_restore(&flags3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __noinline void local_irq_save_3(unsigned long *flags1, unsigned long *flags2,
|
||||
unsigned long *flags3)
|
||||
{
|
||||
local_irq_save(flags1);
|
||||
local_irq_save(flags2);
|
||||
local_irq_save(flags3);
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__success
|
||||
int irq_restore_3_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags1;
|
||||
unsigned long flags2;
|
||||
unsigned long flags3;
|
||||
|
||||
local_irq_save_3(&flags1, &flags2, &flags3);
|
||||
bpf_local_irq_restore(&flags3);
|
||||
bpf_local_irq_restore(&flags2);
|
||||
bpf_local_irq_restore(&flags1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("cannot restore irq state out of order")
|
||||
int irq_restore_4_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags1;
|
||||
unsigned long flags2;
|
||||
unsigned long flags3;
|
||||
unsigned long flags4;
|
||||
|
||||
local_irq_save_3(&flags1, &flags2, &flags3);
|
||||
bpf_local_irq_restore(&flags3);
|
||||
bpf_local_irq_save(&flags4);
|
||||
bpf_local_irq_restore(&flags4);
|
||||
bpf_local_irq_restore(&flags1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("cannot restore irq state out of order")
|
||||
int irq_restore_ooo_3_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags1;
|
||||
unsigned long flags2;
|
||||
unsigned long flags3;
|
||||
|
||||
local_irq_save_3(&flags1, &flags2, &flags3);
|
||||
bpf_local_irq_restore(&flags3);
|
||||
bpf_local_irq_restore(&flags2);
|
||||
bpf_local_irq_save(&flags3);
|
||||
bpf_local_irq_restore(&flags1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("expected an initialized")
|
||||
int irq_restore_invalid(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags1;
|
||||
unsigned long flags = 0xfaceb00c;
|
||||
|
||||
bpf_local_irq_save(&flags1);
|
||||
bpf_local_irq_restore(&flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("expected uninitialized")
|
||||
int irq_save_invalid(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags1;
|
||||
|
||||
bpf_local_irq_save(&flags1);
|
||||
bpf_local_irq_save(&flags1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("expected an initialized")
|
||||
int irq_restore_iter(struct __sk_buff *ctx)
|
||||
{
|
||||
struct bpf_iter_num it;
|
||||
|
||||
bpf_iter_num_new(&it, 0, 42);
|
||||
bpf_local_irq_restore((unsigned long *)&it);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("Unreleased reference id=1")
|
||||
int irq_save_iter(struct __sk_buff *ctx)
|
||||
{
|
||||
struct bpf_iter_num it;
|
||||
|
||||
/* Ensure same sized slot has st->ref_obj_id set, so we reject based on
|
||||
* slot_type != STACK_IRQ_FLAG...
|
||||
*/
|
||||
_Static_assert(sizeof(it) == sizeof(unsigned long), "broken iterator size");
|
||||
|
||||
bpf_iter_num_new(&it, 0, 42);
|
||||
bpf_local_irq_save((unsigned long *)&it);
|
||||
bpf_local_irq_restore((unsigned long *)&it);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("expected an initialized")
|
||||
int irq_flag_overwrite(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
bpf_local_irq_save(&flags);
|
||||
flags = 0xdeadbeef;
|
||||
bpf_local_irq_restore(&flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("expected an initialized")
|
||||
int irq_flag_overwrite_partial(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
bpf_local_irq_save(&flags);
|
||||
*(((char *)&flags) + 1) = 0xff;
|
||||
bpf_local_irq_restore(&flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("cannot restore irq state out of order")
|
||||
int irq_ooo_refs_array(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags[4];
|
||||
struct { int i; } *p;
|
||||
|
||||
/* refs=1 */
|
||||
bpf_local_irq_save(&flags[0]);
|
||||
|
||||
/* refs=1,2 */
|
||||
p = bpf_obj_new(typeof(*p));
|
||||
if (!p) {
|
||||
bpf_local_irq_restore(&flags[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* refs=1,2,3 */
|
||||
bpf_local_irq_save(&flags[1]);
|
||||
|
||||
/* refs=1,2,3,4 */
|
||||
bpf_local_irq_save(&flags[2]);
|
||||
|
||||
/* Now when we remove ref=2, the verifier must not break the ordering in
|
||||
* the refs array between 1,3,4. With an older implementation, the
|
||||
* verifier would swap the last element with the removed element, but to
|
||||
* maintain the stack property we need to use memmove.
|
||||
*/
|
||||
bpf_obj_drop(p);
|
||||
|
||||
/* Save and restore to reset active_irq_id to 3, as the ordering is now
|
||||
* refs=1,4,3. When restoring the linear scan will find prev_id in order
|
||||
* as 3 instead of 4.
|
||||
*/
|
||||
bpf_local_irq_save(&flags[3]);
|
||||
bpf_local_irq_restore(&flags[3]);
|
||||
|
||||
/* With the incorrect implementation, we can release flags[1], flags[2],
|
||||
* and flags[0], i.e. in the wrong order.
|
||||
*/
|
||||
bpf_local_irq_restore(&flags[1]);
|
||||
bpf_local_irq_restore(&flags[2]);
|
||||
bpf_local_irq_restore(&flags[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -5,8 +5,10 @@
|
||||
#include "bpf_misc.h"
|
||||
#include "bpf_experimental.h"
|
||||
|
||||
extern int bpf_copy_from_user_str(void *dst, u32 dst__sz, const void *unsafe_ptr__ign, u64 flags) __weak __ksym;
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
|
||||
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_preempt_disable-ed region")
|
||||
int preempt_lock_missing_1(struct __sk_buff *ctx)
|
||||
{
|
||||
bpf_preempt_disable();
|
||||
@@ -14,7 +16,7 @@ int preempt_lock_missing_1(struct __sk_buff *ctx)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
|
||||
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_preempt_disable-ed region")
|
||||
int preempt_lock_missing_2(struct __sk_buff *ctx)
|
||||
{
|
||||
bpf_preempt_disable();
|
||||
@@ -23,7 +25,7 @@ int preempt_lock_missing_2(struct __sk_buff *ctx)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
|
||||
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_preempt_disable-ed region")
|
||||
int preempt_lock_missing_3(struct __sk_buff *ctx)
|
||||
{
|
||||
bpf_preempt_disable();
|
||||
@@ -33,7 +35,7 @@ int preempt_lock_missing_3(struct __sk_buff *ctx)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
|
||||
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_preempt_disable-ed region")
|
||||
int preempt_lock_missing_3_minus_2(struct __sk_buff *ctx)
|
||||
{
|
||||
bpf_preempt_disable();
|
||||
@@ -55,7 +57,7 @@ static __noinline void preempt_enable(void)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
|
||||
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_preempt_disable-ed region")
|
||||
int preempt_lock_missing_1_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
preempt_disable();
|
||||
@@ -63,7 +65,7 @@ int preempt_lock_missing_1_subprog(struct __sk_buff *ctx)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
|
||||
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_preempt_disable-ed region")
|
||||
int preempt_lock_missing_2_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
preempt_disable();
|
||||
@@ -72,7 +74,7 @@ int preempt_lock_missing_2_subprog(struct __sk_buff *ctx)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
|
||||
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_preempt_disable-ed region")
|
||||
int preempt_lock_missing_2_minus_1_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
preempt_disable();
|
||||
@@ -113,6 +115,18 @@ int preempt_sleepable_helper(void *ctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
|
||||
__failure __msg("kernel func bpf_copy_from_user_str is sleepable within non-preemptible region")
|
||||
int preempt_sleepable_kfunc(void *ctx)
|
||||
{
|
||||
u32 data;
|
||||
|
||||
bpf_preempt_disable();
|
||||
bpf_copy_from_user_str(&data, sizeof(data), NULL, 0);
|
||||
bpf_preempt_enable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __noinline preempt_global_subprog(void)
|
||||
{
|
||||
preempt_balance_subprog();
|
||||
|
||||
@@ -187,7 +187,7 @@ l0_%=: r6 = r0; \
|
||||
|
||||
SEC("cgroup/skb")
|
||||
__description("spin_lock: test6 missing unlock")
|
||||
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_spin_lock-ed region")
|
||||
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_spin_lock-ed region")
|
||||
__failure_unpriv __msg_unpriv("")
|
||||
__naked void spin_lock_test6_missing_unlock(void)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user