Skip to content

Commit 329a672

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf-verifier-range-computation-improvements'
Cupertino Miranda says: ==================== bpf/verifier: range computation improvements Hi everyone, This is what I hope to be the last version. :) Regards, Cupertino Changes from v1: - Reordered patches in the series. - Fix refactor to be acurate with original code. - Fixed other mentioned small problems. Changes from v2: - Added a patch to replace mark_reg_unknowon for __mark_reg_unknown in the context of range computation. - Reverted implementation of refactor to v1 which used a simpler boolean return value in check function. - Further relaxed MUL to allow it to still compute a range when neither of its registers is a known value. - Simplified tests based on Eduards example. - Added messages in selftest commits. Changes from v3: - Improved commit message of patch nr 1. - Coding style fixes. - Improve XOR and OR tests. - Made function calls to pass struct bpf_reg_state pointer instead. - Improved final code as a last patch. Changes from v4: - Merged patch nr 7 in 2. ==================== Link: https://lore.kernel.org/r/20240506141849.185293-1-cupertino.miranda@oracle.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2 parents 41b307a + 9295678 commit 329a672

2 files changed

Lines changed: 104 additions & 65 deletions

File tree

kernel/bpf/verifier.c

Lines changed: 41 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -13878,6 +13878,46 @@ static void scalar_min_max_arsh(struct bpf_reg_state *dst_reg,
1387813878
__update_reg_bounds(dst_reg);
1387913879
}
1388013880

13881+
static bool is_safe_to_compute_dst_reg_range(struct bpf_insn *insn,
13882+
const struct bpf_reg_state *src_reg)
13883+
{
13884+
bool src_is_const = false;
13885+
u64 insn_bitness = (BPF_CLASS(insn->code) == BPF_ALU64) ? 64 : 32;
13886+
13887+
if (insn_bitness == 32) {
13888+
if (tnum_subreg_is_const(src_reg->var_off)
13889+
&& src_reg->s32_min_value == src_reg->s32_max_value
13890+
&& src_reg->u32_min_value == src_reg->u32_max_value)
13891+
src_is_const = true;
13892+
} else {
13893+
if (tnum_is_const(src_reg->var_off)
13894+
&& src_reg->smin_value == src_reg->smax_value
13895+
&& src_reg->umin_value == src_reg->umax_value)
13896+
src_is_const = true;
13897+
}
13898+
13899+
switch (BPF_OP(insn->code)) {
13900+
case BPF_ADD:
13901+
case BPF_SUB:
13902+
case BPF_AND:
13903+
case BPF_XOR:
13904+
case BPF_OR:
13905+
case BPF_MUL:
13906+
return true;
13907+
13908+
/* Shift operators range is only computable if shift dimension operand
13909+
* is a constant. Shifts greater than 31 or 63 are undefined. This
13910+
* includes shifts by a negative number.
13911+
*/
13912+
case BPF_LSH:
13913+
case BPF_RSH:
13914+
case BPF_ARSH:
13915+
return (src_is_const && src_reg->umax_value < insn_bitness);
13916+
default:
13917+
return false;
13918+
}
13919+
}
13920+
1388113921
/* WARNING: This function does calculations on 64-bit values, but the actual
1388213922
* execution may occur on 32-bit values. Therefore, things like bitshifts
1388313923
* need extra checks in the 32-bit case.
@@ -13887,53 +13927,11 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
1388713927
struct bpf_reg_state *dst_reg,
1388813928
struct bpf_reg_state src_reg)
1388913929
{
13890-
struct bpf_reg_state *regs = cur_regs(env);
1389113930
u8 opcode = BPF_OP(insn->code);
13892-
bool src_known;
13893-
s64 smin_val, smax_val;
13894-
u64 umin_val, umax_val;
13895-
s32 s32_min_val, s32_max_val;
13896-
u32 u32_min_val, u32_max_val;
13897-
u64 insn_bitness = (BPF_CLASS(insn->code) == BPF_ALU64) ? 64 : 32;
1389813931
bool alu32 = (BPF_CLASS(insn->code) != BPF_ALU64);
1389913932
int ret;
1390013933

13901-
smin_val = src_reg.smin_value;
13902-
smax_val = src_reg.smax_value;
13903-
umin_val = src_reg.umin_value;
13904-
umax_val = src_reg.umax_value;
13905-
13906-
s32_min_val = src_reg.s32_min_value;
13907-
s32_max_val = src_reg.s32_max_value;
13908-
u32_min_val = src_reg.u32_min_value;
13909-
u32_max_val = src_reg.u32_max_value;
13910-
13911-
if (alu32) {
13912-
src_known = tnum_subreg_is_const(src_reg.var_off);
13913-
if ((src_known &&
13914-
(s32_min_val != s32_max_val || u32_min_val != u32_max_val)) ||
13915-
s32_min_val > s32_max_val || u32_min_val > u32_max_val) {
13916-
/* Taint dst register if offset had invalid bounds
13917-
* derived from e.g. dead branches.
13918-
*/
13919-
__mark_reg_unknown(env, dst_reg);
13920-
return 0;
13921-
}
13922-
} else {
13923-
src_known = tnum_is_const(src_reg.var_off);
13924-
if ((src_known &&
13925-
(smin_val != smax_val || umin_val != umax_val)) ||
13926-
smin_val > smax_val || umin_val > umax_val) {
13927-
/* Taint dst register if offset had invalid bounds
13928-
* derived from e.g. dead branches.
13929-
*/
13930-
__mark_reg_unknown(env, dst_reg);
13931-
return 0;
13932-
}
13933-
}
13934-
13935-
if (!src_known &&
13936-
opcode != BPF_ADD && opcode != BPF_SUB && opcode != BPF_AND) {
13934+
if (!is_safe_to_compute_dst_reg_range(insn, &src_reg)) {
1393713935
__mark_reg_unknown(env, dst_reg);
1393813936
return 0;
1393913937
}
@@ -13990,46 +13988,24 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
1399013988
scalar_min_max_xor(dst_reg, &src_reg);
1399113989
break;
1399213990
case BPF_LSH:
13993-
if (umax_val >= insn_bitness) {
13994-
/* Shifts greater than 31 or 63 are undefined.
13995-
* This includes shifts by a negative number.
13996-
*/
13997-
mark_reg_unknown(env, regs, insn->dst_reg);
13998-
break;
13999-
}
1400013991
if (alu32)
1400113992
scalar32_min_max_lsh(dst_reg, &src_reg);
1400213993
else
1400313994
scalar_min_max_lsh(dst_reg, &src_reg);
1400413995
break;
1400513996
case BPF_RSH:
14006-
if (umax_val >= insn_bitness) {
14007-
/* Shifts greater than 31 or 63 are undefined.
14008-
* This includes shifts by a negative number.
14009-
*/
14010-
mark_reg_unknown(env, regs, insn->dst_reg);
14011-
break;
14012-
}
1401313997
if (alu32)
1401413998
scalar32_min_max_rsh(dst_reg, &src_reg);
1401513999
else
1401614000
scalar_min_max_rsh(dst_reg, &src_reg);
1401714001
break;
1401814002
case BPF_ARSH:
14019-
if (umax_val >= insn_bitness) {
14020-
/* Shifts greater than 31 or 63 are undefined.
14021-
* This includes shifts by a negative number.
14022-
*/
14023-
mark_reg_unknown(env, regs, insn->dst_reg);
14024-
break;
14025-
}
1402614003
if (alu32)
1402714004
scalar32_min_max_arsh(dst_reg, &src_reg);
1402814005
else
1402914006
scalar_min_max_arsh(dst_reg, &src_reg);
1403014007
break;
1403114008
default:
14032-
mark_reg_unknown(env, regs, insn->dst_reg);
1403314009
break;
1403414010
}
1403514011

tools/testing/selftests/bpf/progs/verifier_bounds.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,69 @@ l1_%=: r0 = 0; \
885885
: __clobber_all);
886886
}
887887

888+
SEC("socket")
889+
__description("bounds check for non const xor src dst")
890+
__success __log_level(2)
891+
__msg("5: (af) r0 ^= r6 ; R0_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=431,var_off=(0x0; 0x1af))")
892+
__naked void non_const_xor_src_dst(void)
893+
{
894+
asm volatile (" \
895+
call %[bpf_get_prandom_u32]; \
896+
r6 = r0; \
897+
call %[bpf_get_prandom_u32]; \
898+
r6 &= 0xaf; \
899+
r0 &= 0x1a0; \
900+
r0 ^= r6; \
901+
exit; \
902+
" :
903+
: __imm(bpf_map_lookup_elem),
904+
__imm_addr(map_hash_8b),
905+
__imm(bpf_get_prandom_u32)
906+
: __clobber_all);
907+
}
908+
909+
SEC("socket")
910+
__description("bounds check for non const or src dst")
911+
__success __log_level(2)
912+
__msg("5: (4f) r0 |= r6 ; R0_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=431,var_off=(0x0; 0x1af))")
913+
__naked void non_const_or_src_dst(void)
914+
{
915+
asm volatile (" \
916+
call %[bpf_get_prandom_u32]; \
917+
r6 = r0; \
918+
call %[bpf_get_prandom_u32]; \
919+
r6 &= 0xaf; \
920+
r0 &= 0x1a0; \
921+
r0 |= r6; \
922+
exit; \
923+
" :
924+
: __imm(bpf_map_lookup_elem),
925+
__imm_addr(map_hash_8b),
926+
__imm(bpf_get_prandom_u32)
927+
: __clobber_all);
928+
}
929+
930+
SEC("socket")
931+
__description("bounds check for non const mul regs")
932+
__success __log_level(2)
933+
__msg("5: (2f) r0 *= r6 ; R0_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=3825,var_off=(0x0; 0xfff))")
934+
__naked void non_const_mul_regs(void)
935+
{
936+
asm volatile (" \
937+
call %[bpf_get_prandom_u32]; \
938+
r6 = r0; \
939+
call %[bpf_get_prandom_u32]; \
940+
r6 &= 0xff; \
941+
r0 &= 0x0f; \
942+
r0 *= r6; \
943+
exit; \
944+
" :
945+
: __imm(bpf_map_lookup_elem),
946+
__imm_addr(map_hash_8b),
947+
__imm(bpf_get_prandom_u32)
948+
: __clobber_all);
949+
}
950+
888951
SEC("socket")
889952
__description("bounds checks after 32-bit truncation. test 1")
890953
__success __failure_unpriv __msg_unpriv("R0 leaks addr")

0 commit comments

Comments
 (0)