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:
commit
99523094de
|
@ -273,9 +273,9 @@ struct bpf_id_pair {
|
|||
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
|
||||
/* 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 {
|
||||
/* call stack tracking */
|
||||
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;
|
||||
|
||||
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)
|
||||
/* explored state can't have used this */
|
||||
return true;
|
||||
|
@ -13080,6 +13071,8 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
|
|||
return false;
|
||||
switch (base_type(rold->type)) {
|
||||
case SCALAR_VALUE:
|
||||
if (equal)
|
||||
return true;
|
||||
if (env->explore_alu_limits)
|
||||
return false;
|
||||
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 &&
|
||||
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:
|
||||
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 */
|
||||
return range_within(rold, rcur) &&
|
||||
tnum_in(rold->var_off, rcur->var_off);
|
||||
case PTR_TO_CTX:
|
||||
case CONST_PTR_TO_MAP:
|
||||
case PTR_TO_PACKET_END:
|
||||
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
|
||||
case 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;
|
||||
default:
|
||||
/* Don't know what's going on, just say it's not safe */
|
||||
return false;
|
||||
/* Only valid matches are exact, which memcmp() */
|
||||
return equal;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
memset(env->idmap_scratch, 0, sizeof(env->idmap_scratch));
|
||||
for (i = 0; i < MAX_BPF_REG; i++)
|
||||
if (!regsafe(env, &old->regs[i], &cur->regs[i],
|
||||
env->idmap_scratch))
|
||||
|
@ -13297,14 +13284,25 @@ static bool states_equal(struct bpf_verifier_env *env,
|
|||
if (old->curframe != cur->curframe)
|
||||
return false;
|
||||
|
||||
memset(env->idmap_scratch, 0, sizeof(env->idmap_scratch));
|
||||
|
||||
/* Verification state from speculative execution simulation
|
||||
* must never prune a non-speculative execution one.
|
||||
*/
|
||||
if (old->speculative && !cur->speculative)
|
||||
return false;
|
||||
|
||||
if (old->active_lock.ptr != cur->active_lock.ptr ||
|
||||
old->active_lock.id != cur->active_lock.id)
|
||||
if (old->active_lock.ptr != cur->active_lock.ptr)
|
||||
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;
|
||||
|
||||
if (old->active_rcu_lock != cur->active_rcu_lock)
|
||||
|
|
|
@ -2305,3 +2305,85 @@
|
|||
.errstr = "!read_ok",
|
||||
.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,
|
||||
.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",
|
||||
.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,
|
||||
.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,
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue