mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-10 11:40:19 -04:00
Merge branch 'stricter register ID checking in regsafe()'
Eduard Zingerman says:
====================
This patch-set consists of a series of bug fixes for register ID
tracking in verifier.c:states_equal()/regsafe() functions:
- for registers of type PTR_TO_MAP_{KEY,VALUE}, PTR_TO_PACKET[_META]
the regsafe() should call check_ids() even if registers are
byte-to-byte equal;
- states_equal() must maintain idmap that covers all function frames
in the state because functions like mark_ptr_or_null_regs() operate
on all registers in the state;
- regsafe() must compare spin lock ids for PTR_TO_MAP_VALUE registers.
The last point covers issue reported by Kumar Kartikeya Dwivedi in [1],
I borrowed the test commit from there.
Note, that there is also an issue with register id tracking for
scalars described here [2], it would be addressed separately.
[1] https://lore.kernel.org/bpf/20221111202719.982118-1-memxor@gmail.com/
[2] https://lore.kernel.org/bpf/20221128163442.280187-2-eddyz87@gmail.com/
Eduard Zingerman (6):
bpf: regsafe() must not skip check_ids()
selftests/bpf: test cases for regsafe() bug skipping check_id()
bpf: states_equal() must build idmap for all function frames
selftests/bpf: verify states_equal() maintains idmap across all frames
bpf: use check_ids() for active_lock comparison
selftests/bpf: test case for relaxed prunning of active_lock.id
====================
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
@@ -273,9 +273,9 @@ struct bpf_id_pair {
|
|||||||
u32 cur;
|
u32 cur;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Maximum number of register states that can exist at once */
|
|
||||||
#define BPF_ID_MAP_SIZE (MAX_BPF_REG + MAX_BPF_STACK / BPF_REG_SIZE)
|
|
||||||
#define MAX_CALL_FRAMES 8
|
#define MAX_CALL_FRAMES 8
|
||||||
|
/* Maximum number of register states that can exist at once */
|
||||||
|
#define BPF_ID_MAP_SIZE ((MAX_BPF_REG + MAX_BPF_STACK / BPF_REG_SIZE) * MAX_CALL_FRAMES)
|
||||||
struct bpf_verifier_state {
|
struct bpf_verifier_state {
|
||||||
/* call stack tracking */
|
/* call stack tracking */
|
||||||
struct bpf_func_state *frame[MAX_CALL_FRAMES];
|
struct bpf_func_state *frame[MAX_CALL_FRAMES];
|
||||||
|
|||||||
@@ -13064,15 +13064,6 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
|
|||||||
|
|
||||||
equal = memcmp(rold, rcur, offsetof(struct bpf_reg_state, parent)) == 0;
|
equal = memcmp(rold, rcur, offsetof(struct bpf_reg_state, parent)) == 0;
|
||||||
|
|
||||||
if (rold->type == PTR_TO_STACK)
|
|
||||||
/* two stack pointers are equal only if they're pointing to
|
|
||||||
* the same stack frame, since fp-8 in foo != fp-8 in bar
|
|
||||||
*/
|
|
||||||
return equal && rold->frameno == rcur->frameno;
|
|
||||||
|
|
||||||
if (equal)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (rold->type == NOT_INIT)
|
if (rold->type == NOT_INIT)
|
||||||
/* explored state can't have used this */
|
/* explored state can't have used this */
|
||||||
return true;
|
return true;
|
||||||
@@ -13080,6 +13071,8 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
|
|||||||
return false;
|
return false;
|
||||||
switch (base_type(rold->type)) {
|
switch (base_type(rold->type)) {
|
||||||
case SCALAR_VALUE:
|
case SCALAR_VALUE:
|
||||||
|
if (equal)
|
||||||
|
return true;
|
||||||
if (env->explore_alu_limits)
|
if (env->explore_alu_limits)
|
||||||
return false;
|
return false;
|
||||||
if (rcur->type == SCALAR_VALUE) {
|
if (rcur->type == SCALAR_VALUE) {
|
||||||
@@ -13126,7 +13119,8 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
|
|||||||
*/
|
*/
|
||||||
return memcmp(rold, rcur, offsetof(struct bpf_reg_state, id)) == 0 &&
|
return memcmp(rold, rcur, offsetof(struct bpf_reg_state, id)) == 0 &&
|
||||||
range_within(rold, rcur) &&
|
range_within(rold, rcur) &&
|
||||||
tnum_in(rold->var_off, rcur->var_off);
|
tnum_in(rold->var_off, rcur->var_off) &&
|
||||||
|
check_ids(rold->id, rcur->id, idmap);
|
||||||
case PTR_TO_PACKET_META:
|
case PTR_TO_PACKET_META:
|
||||||
case PTR_TO_PACKET:
|
case PTR_TO_PACKET:
|
||||||
if (rcur->type != rold->type)
|
if (rcur->type != rold->type)
|
||||||
@@ -13150,20 +13144,14 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
|
|||||||
/* new val must satisfy old val knowledge */
|
/* new val must satisfy old val knowledge */
|
||||||
return range_within(rold, rcur) &&
|
return range_within(rold, rcur) &&
|
||||||
tnum_in(rold->var_off, rcur->var_off);
|
tnum_in(rold->var_off, rcur->var_off);
|
||||||
case PTR_TO_CTX:
|
case PTR_TO_STACK:
|
||||||
case CONST_PTR_TO_MAP:
|
/* two stack pointers are equal only if they're pointing to
|
||||||
case PTR_TO_PACKET_END:
|
* the same stack frame, since fp-8 in foo != fp-8 in bar
|
||||||
case PTR_TO_FLOW_KEYS:
|
|
||||||
case PTR_TO_SOCKET:
|
|
||||||
case PTR_TO_SOCK_COMMON:
|
|
||||||
case PTR_TO_TCP_SOCK:
|
|
||||||
case PTR_TO_XDP_SOCK:
|
|
||||||
/* Only valid matches are exact, which memcmp() above
|
|
||||||
* would have accepted
|
|
||||||
*/
|
*/
|
||||||
|
return equal && rold->frameno == rcur->frameno;
|
||||||
default:
|
default:
|
||||||
/* Don't know what's going on, just say it's not safe */
|
/* Only valid matches are exact, which memcmp() */
|
||||||
return false;
|
return equal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Shouldn't get here; if we do, say it's not safe */
|
/* Shouldn't get here; if we do, say it's not safe */
|
||||||
@@ -13273,7 +13261,6 @@ static bool func_states_equal(struct bpf_verifier_env *env, struct bpf_func_stat
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
memset(env->idmap_scratch, 0, sizeof(env->idmap_scratch));
|
|
||||||
for (i = 0; i < MAX_BPF_REG; i++)
|
for (i = 0; i < MAX_BPF_REG; i++)
|
||||||
if (!regsafe(env, &old->regs[i], &cur->regs[i],
|
if (!regsafe(env, &old->regs[i], &cur->regs[i],
|
||||||
env->idmap_scratch))
|
env->idmap_scratch))
|
||||||
@@ -13297,14 +13284,25 @@ static bool states_equal(struct bpf_verifier_env *env,
|
|||||||
if (old->curframe != cur->curframe)
|
if (old->curframe != cur->curframe)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
memset(env->idmap_scratch, 0, sizeof(env->idmap_scratch));
|
||||||
|
|
||||||
/* Verification state from speculative execution simulation
|
/* Verification state from speculative execution simulation
|
||||||
* must never prune a non-speculative execution one.
|
* must never prune a non-speculative execution one.
|
||||||
*/
|
*/
|
||||||
if (old->speculative && !cur->speculative)
|
if (old->speculative && !cur->speculative)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (old->active_lock.ptr != cur->active_lock.ptr ||
|
if (old->active_lock.ptr != cur->active_lock.ptr)
|
||||||
old->active_lock.id != cur->active_lock.id)
|
return false;
|
||||||
|
|
||||||
|
/* Old and cur active_lock's have to be either both present
|
||||||
|
* or both absent.
|
||||||
|
*/
|
||||||
|
if (!!old->active_lock.id != !!cur->active_lock.id)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (old->active_lock.id &&
|
||||||
|
!check_ids(old->active_lock.id, cur->active_lock.id, env->idmap_scratch))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (old->active_rcu_lock != cur->active_rcu_lock)
|
if (old->active_rcu_lock != cur->active_rcu_lock)
|
||||||
|
|||||||
@@ -2305,3 +2305,85 @@
|
|||||||
.errstr = "!read_ok",
|
.errstr = "!read_ok",
|
||||||
.result = REJECT,
|
.result = REJECT,
|
||||||
},
|
},
|
||||||
|
/* Make sure that verifier.c:states_equal() considers IDs from all
|
||||||
|
* frames when building 'idmap' for check_ids().
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
"calls: check_ids() across call boundary",
|
||||||
|
.insns = {
|
||||||
|
/* Function main() */
|
||||||
|
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||||
|
/* fp[-24] = map_lookup_elem(...) ; get a MAP_VALUE_PTR_OR_NULL with some ID */
|
||||||
|
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||||
|
BPF_LD_MAP_FD(BPF_REG_1,
|
||||||
|
0),
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
|
||||||
|
BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_0, -24),
|
||||||
|
/* fp[-32] = map_lookup_elem(...) ; get a MAP_VALUE_PTR_OR_NULL with some ID */
|
||||||
|
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||||
|
BPF_LD_MAP_FD(BPF_REG_1,
|
||||||
|
0),
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
|
||||||
|
BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_0, -32),
|
||||||
|
/* call foo(&fp[-24], &fp[-32]) ; both arguments have IDs in the current
|
||||||
|
* ; stack frame
|
||||||
|
*/
|
||||||
|
BPF_MOV64_REG(BPF_REG_1, BPF_REG_FP),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -24),
|
||||||
|
BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -32),
|
||||||
|
BPF_CALL_REL(2),
|
||||||
|
/* exit 0 */
|
||||||
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
/* Function foo()
|
||||||
|
*
|
||||||
|
* r9 = &frame[0].fp[-24] ; save arguments in the callee saved registers,
|
||||||
|
* r8 = &frame[0].fp[-32] ; arguments are pointers to pointers to map value
|
||||||
|
*/
|
||||||
|
BPF_MOV64_REG(BPF_REG_9, BPF_REG_1),
|
||||||
|
BPF_MOV64_REG(BPF_REG_8, BPF_REG_2),
|
||||||
|
/* r7 = ktime_get_ns() */
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns),
|
||||||
|
BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),
|
||||||
|
/* r6 = ktime_get_ns() */
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns),
|
||||||
|
BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
|
||||||
|
/* if r6 > r7 goto +1 ; no new information about the state is derived from
|
||||||
|
* ; this check, thus produced verifier states differ
|
||||||
|
* ; only in 'insn_idx'
|
||||||
|
* r9 = r8
|
||||||
|
*/
|
||||||
|
BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
|
||||||
|
BPF_MOV64_REG(BPF_REG_9, BPF_REG_8),
|
||||||
|
/* r9 = *r9 ; verifier get's to this point via two paths:
|
||||||
|
* ; (I) one including r9 = r8, verified first;
|
||||||
|
* ; (II) one excluding r9 = r8, verified next.
|
||||||
|
* ; After load of *r9 to r9 the frame[0].fp[-24].id == r9.id.
|
||||||
|
* ; Suppose that checkpoint is created here via path (I).
|
||||||
|
* ; When verifying via (II) the r9.id must be compared against
|
||||||
|
* ; frame[0].fp[-24].id, otherwise (I) and (II) would be
|
||||||
|
* ; incorrectly deemed equivalent.
|
||||||
|
* if r9 == 0 goto <exit>
|
||||||
|
*/
|
||||||
|
BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_9, 0),
|
||||||
|
BPF_JMP_IMM(BPF_JEQ, BPF_REG_9, 0, 1),
|
||||||
|
/* r8 = *r8 ; read map value via r8, this is not safe
|
||||||
|
* r0 = *r8 ; because r8 might be not equal to r9.
|
||||||
|
*/
|
||||||
|
BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_8, 0),
|
||||||
|
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_8, 0),
|
||||||
|
/* exit 0 */
|
||||||
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.flags = BPF_F_TEST_STATE_FREQ,
|
||||||
|
.fixup_map_hash_8b = { 3, 9 },
|
||||||
|
.result = REJECT,
|
||||||
|
.errstr = "R8 invalid mem access 'map_value_or_null'",
|
||||||
|
.result_unpriv = REJECT,
|
||||||
|
.errstr_unpriv = "",
|
||||||
|
.prog_type = BPF_PROG_TYPE_CGROUP_SKB,
|
||||||
|
},
|
||||||
|
|||||||
@@ -654,3 +654,57 @@
|
|||||||
.result = ACCEPT,
|
.result = ACCEPT,
|
||||||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"direct packet access: test30 (check_id() in regsafe(), bad access)",
|
||||||
|
.insns = {
|
||||||
|
/* r9 = ctx */
|
||||||
|
BPF_MOV64_REG(BPF_REG_9, BPF_REG_1),
|
||||||
|
/* r7 = ktime_get_ns() */
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns),
|
||||||
|
BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),
|
||||||
|
/* r6 = ktime_get_ns() */
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns),
|
||||||
|
BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
|
||||||
|
/* r2 = ctx->data
|
||||||
|
* r3 = ctx->data
|
||||||
|
* r4 = ctx->data_end
|
||||||
|
*/
|
||||||
|
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_9, offsetof(struct __sk_buff, data)),
|
||||||
|
BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_9, offsetof(struct __sk_buff, data)),
|
||||||
|
BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_9, offsetof(struct __sk_buff, data_end)),
|
||||||
|
/* if r6 > 100 goto exit
|
||||||
|
* if r7 > 100 goto exit
|
||||||
|
*/
|
||||||
|
BPF_JMP_IMM(BPF_JGT, BPF_REG_6, 100, 9),
|
||||||
|
BPF_JMP_IMM(BPF_JGT, BPF_REG_7, 100, 8),
|
||||||
|
/* r2 += r6 ; this forces assignment of ID to r2
|
||||||
|
* r2 += 1 ; get some fixed off for r2
|
||||||
|
* r3 += r7 ; this forces assignment of ID to r3
|
||||||
|
* r3 += 1 ; get some fixed off for r3
|
||||||
|
*/
|
||||||
|
BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_6),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1),
|
||||||
|
BPF_ALU64_REG(BPF_ADD, BPF_REG_3, BPF_REG_7),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 1),
|
||||||
|
/* if r6 > r7 goto +1 ; no new information about the state is derived from
|
||||||
|
* ; this check, thus produced verifier states differ
|
||||||
|
* ; only in 'insn_idx'
|
||||||
|
* r2 = r3 ; optionally share ID between r2 and r3
|
||||||
|
*/
|
||||||
|
BPF_JMP_REG(BPF_JNE, BPF_REG_6, BPF_REG_7, 1),
|
||||||
|
BPF_MOV64_REG(BPF_REG_2, BPF_REG_3),
|
||||||
|
/* if r3 > ctx->data_end goto exit */
|
||||||
|
BPF_JMP_REG(BPF_JGT, BPF_REG_3, BPF_REG_4, 1),
|
||||||
|
/* r5 = *(u8 *) (r2 - 1) ; access packet memory using r2,
|
||||||
|
* ; this is not always safe
|
||||||
|
*/
|
||||||
|
BPF_LDX_MEM(BPF_B, BPF_REG_5, BPF_REG_2, -1),
|
||||||
|
/* exit(0) */
|
||||||
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.flags = BPF_F_TEST_STATE_FREQ,
|
||||||
|
.result = REJECT,
|
||||||
|
.errstr = "invalid access to packet, off=0 size=1, R2",
|
||||||
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||||
|
},
|
||||||
|
|||||||
@@ -331,3 +331,117 @@
|
|||||||
.errstr = "inside bpf_spin_lock",
|
.errstr = "inside bpf_spin_lock",
|
||||||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"spin_lock: regsafe compare reg->id for map value",
|
||||||
|
.insns = {
|
||||||
|
BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
|
||||||
|
BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_6, offsetof(struct __sk_buff, mark)),
|
||||||
|
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||||
|
BPF_MOV64_REG(BPF_REG_9, BPF_REG_1),
|
||||||
|
BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
|
||||||
|
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
|
||||||
|
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||||
|
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),
|
||||||
|
BPF_MOV64_REG(BPF_REG_1, BPF_REG_9),
|
||||||
|
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
|
||||||
|
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||||
|
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),
|
||||||
|
BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
|
||||||
|
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_lock),
|
||||||
|
BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 1),
|
||||||
|
BPF_JMP_IMM(BPF_JA, 0, 0, 1),
|
||||||
|
BPF_MOV64_REG(BPF_REG_7, BPF_REG_8),
|
||||||
|
BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
|
||||||
|
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_unlock),
|
||||||
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.fixup_map_spin_lock = { 2 },
|
||||||
|
.result = REJECT,
|
||||||
|
.errstr = "bpf_spin_unlock of different lock",
|
||||||
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||||
|
.flags = BPF_F_TEST_STATE_FREQ,
|
||||||
|
},
|
||||||
|
/* Make sure that regsafe() compares ids for spin lock records using
|
||||||
|
* check_ids():
|
||||||
|
* 1: r9 = map_lookup_elem(...) ; r9.id == 1
|
||||||
|
* 2: r8 = map_lookup_elem(...) ; r8.id == 2
|
||||||
|
* 3: r7 = ktime_get_ns()
|
||||||
|
* 4: r6 = ktime_get_ns()
|
||||||
|
* 5: if r6 > r7 goto <9>
|
||||||
|
* 6: spin_lock(r8)
|
||||||
|
* 7: r9 = r8
|
||||||
|
* 8: goto <10>
|
||||||
|
* 9: spin_lock(r9)
|
||||||
|
* 10: spin_unlock(r9) ; r9.id == 1 || r9.id == 2 and lock is active,
|
||||||
|
* ; second visit to (10) should be considered safe
|
||||||
|
* ; if check_ids() is used.
|
||||||
|
* 11: exit(0)
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
"spin_lock: regsafe() check_ids() similar id mappings",
|
||||||
|
.insns = {
|
||||||
|
BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
|
||||||
|
/* r9 = map_lookup_elem(...) */
|
||||||
|
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
|
||||||
|
BPF_LD_MAP_FD(BPF_REG_1,
|
||||||
|
0),
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
|
||||||
|
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 24),
|
||||||
|
BPF_MOV64_REG(BPF_REG_9, BPF_REG_0),
|
||||||
|
/* r8 = map_lookup_elem(...) */
|
||||||
|
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
|
||||||
|
BPF_LD_MAP_FD(BPF_REG_1,
|
||||||
|
0),
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
|
||||||
|
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 18),
|
||||||
|
BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),
|
||||||
|
/* r7 = ktime_get_ns() */
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns),
|
||||||
|
BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),
|
||||||
|
/* r6 = ktime_get_ns() */
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns),
|
||||||
|
BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
|
||||||
|
/* if r6 > r7 goto +5 ; no new information about the state is derived from
|
||||||
|
* ; this check, thus produced verifier states differ
|
||||||
|
* ; only in 'insn_idx'
|
||||||
|
* spin_lock(r8)
|
||||||
|
* r9 = r8
|
||||||
|
* goto unlock
|
||||||
|
*/
|
||||||
|
BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 5),
|
||||||
|
BPF_MOV64_REG(BPF_REG_1, BPF_REG_8),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_spin_lock),
|
||||||
|
BPF_MOV64_REG(BPF_REG_9, BPF_REG_8),
|
||||||
|
BPF_JMP_A(3),
|
||||||
|
/* spin_lock(r9) */
|
||||||
|
BPF_MOV64_REG(BPF_REG_1, BPF_REG_9),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_spin_lock),
|
||||||
|
/* spin_unlock(r9) */
|
||||||
|
BPF_MOV64_REG(BPF_REG_1, BPF_REG_9),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_spin_unlock),
|
||||||
|
/* exit(0) */
|
||||||
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.fixup_map_spin_lock = { 3, 10 },
|
||||||
|
.result = VERBOSE_ACCEPT,
|
||||||
|
.errstr = "28: safe",
|
||||||
|
.result_unpriv = REJECT,
|
||||||
|
.errstr_unpriv = "",
|
||||||
|
.prog_type = BPF_PROG_TYPE_CGROUP_SKB,
|
||||||
|
.flags = BPF_F_TEST_STATE_FREQ,
|
||||||
|
},
|
||||||
|
|||||||
@@ -169,3 +169,52 @@
|
|||||||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||||
.result = ACCEPT,
|
.result = ACCEPT,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"MAP_VALUE_OR_NULL check_ids() in regsafe()",
|
||||||
|
.insns = {
|
||||||
|
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||||
|
/* r9 = map_lookup_elem(...) */
|
||||||
|
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||||
|
BPF_LD_MAP_FD(BPF_REG_1,
|
||||||
|
0),
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
|
||||||
|
BPF_MOV64_REG(BPF_REG_9, BPF_REG_0),
|
||||||
|
/* r8 = map_lookup_elem(...) */
|
||||||
|
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||||
|
BPF_LD_MAP_FD(BPF_REG_1,
|
||||||
|
0),
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
|
||||||
|
BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),
|
||||||
|
/* r7 = ktime_get_ns() */
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns),
|
||||||
|
BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),
|
||||||
|
/* r6 = ktime_get_ns() */
|
||||||
|
BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns),
|
||||||
|
BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
|
||||||
|
/* if r6 > r7 goto +1 ; no new information about the state is derived from
|
||||||
|
* ; this check, thus produced verifier states differ
|
||||||
|
* ; only in 'insn_idx'
|
||||||
|
* r9 = r8 ; optionally share ID between r9 and r8
|
||||||
|
*/
|
||||||
|
BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
|
||||||
|
BPF_MOV64_REG(BPF_REG_9, BPF_REG_8),
|
||||||
|
/* if r9 == 0 goto <exit> */
|
||||||
|
BPF_JMP_IMM(BPF_JEQ, BPF_REG_9, 0, 1),
|
||||||
|
/* read map value via r8, this is not always
|
||||||
|
* safe because r8 might be not equal to r9.
|
||||||
|
*/
|
||||||
|
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_8, 0),
|
||||||
|
/* exit 0 */
|
||||||
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.flags = BPF_F_TEST_STATE_FREQ,
|
||||||
|
.fixup_map_hash_8b = { 3, 9 },
|
||||||
|
.result = REJECT,
|
||||||
|
.errstr = "R8 invalid mem access 'map_value_or_null'",
|
||||||
|
.result_unpriv = REJECT,
|
||||||
|
.errstr_unpriv = "",
|
||||||
|
.prog_type = BPF_PROG_TYPE_CGROUP_SKB,
|
||||||
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user