mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 18:13:41 +02:00
Merge branch 'bpf-verifier-improve-precision-of-bpf_mul'
Matan Shachnai says: ==================== This patch-set aims to improve precision of BPF_MUL and add testcases to illustrate precision gains using signed and unsigned bounds. Changes from v1: - Fixed typo made in patch. Changes from v2: - Added signed multiplication to BPF_MUL. - Added test cases to exercise BPF_MUL. - Reordered patches in the series. Changes from v3: - Coding style fixes. ==================== Link: https://patch.msgid.link/20241218032337.12214-1-m.shachnai@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
commit
34ea973dd4
|
|
@ -14084,64 +14084,56 @@ static void scalar_min_max_sub(struct bpf_reg_state *dst_reg,
|
|||
static void scalar32_min_max_mul(struct bpf_reg_state *dst_reg,
|
||||
struct bpf_reg_state *src_reg)
|
||||
{
|
||||
s32 smin_val = src_reg->s32_min_value;
|
||||
u32 umin_val = src_reg->u32_min_value;
|
||||
u32 umax_val = src_reg->u32_max_value;
|
||||
s32 *dst_smin = &dst_reg->s32_min_value;
|
||||
s32 *dst_smax = &dst_reg->s32_max_value;
|
||||
u32 *dst_umin = &dst_reg->u32_min_value;
|
||||
u32 *dst_umax = &dst_reg->u32_max_value;
|
||||
s32 tmp_prod[4];
|
||||
|
||||
if (smin_val < 0 || dst_reg->s32_min_value < 0) {
|
||||
/* Ain't nobody got time to multiply that sign */
|
||||
__mark_reg32_unbounded(dst_reg);
|
||||
return;
|
||||
}
|
||||
/* Both values are positive, so we can work with unsigned and
|
||||
* copy the result to signed (unless it exceeds S32_MAX).
|
||||
*/
|
||||
if (umax_val > U16_MAX || dst_reg->u32_max_value > U16_MAX) {
|
||||
/* Potential overflow, we know nothing */
|
||||
__mark_reg32_unbounded(dst_reg);
|
||||
return;
|
||||
}
|
||||
dst_reg->u32_min_value *= umin_val;
|
||||
dst_reg->u32_max_value *= umax_val;
|
||||
if (dst_reg->u32_max_value > S32_MAX) {
|
||||
if (check_mul_overflow(*dst_umax, src_reg->u32_max_value, dst_umax) ||
|
||||
check_mul_overflow(*dst_umin, src_reg->u32_min_value, dst_umin)) {
|
||||
/* Overflow possible, we know nothing */
|
||||
dst_reg->s32_min_value = S32_MIN;
|
||||
dst_reg->s32_max_value = S32_MAX;
|
||||
*dst_umin = 0;
|
||||
*dst_umax = U32_MAX;
|
||||
}
|
||||
if (check_mul_overflow(*dst_smin, src_reg->s32_min_value, &tmp_prod[0]) ||
|
||||
check_mul_overflow(*dst_smin, src_reg->s32_max_value, &tmp_prod[1]) ||
|
||||
check_mul_overflow(*dst_smax, src_reg->s32_min_value, &tmp_prod[2]) ||
|
||||
check_mul_overflow(*dst_smax, src_reg->s32_max_value, &tmp_prod[3])) {
|
||||
/* Overflow possible, we know nothing */
|
||||
*dst_smin = S32_MIN;
|
||||
*dst_smax = S32_MAX;
|
||||
} else {
|
||||
dst_reg->s32_min_value = dst_reg->u32_min_value;
|
||||
dst_reg->s32_max_value = dst_reg->u32_max_value;
|
||||
*dst_smin = min_array(tmp_prod, 4);
|
||||
*dst_smax = max_array(tmp_prod, 4);
|
||||
}
|
||||
}
|
||||
|
||||
static void scalar_min_max_mul(struct bpf_reg_state *dst_reg,
|
||||
struct bpf_reg_state *src_reg)
|
||||
{
|
||||
s64 smin_val = src_reg->smin_value;
|
||||
u64 umin_val = src_reg->umin_value;
|
||||
u64 umax_val = src_reg->umax_value;
|
||||
s64 *dst_smin = &dst_reg->smin_value;
|
||||
s64 *dst_smax = &dst_reg->smax_value;
|
||||
u64 *dst_umin = &dst_reg->umin_value;
|
||||
u64 *dst_umax = &dst_reg->umax_value;
|
||||
s64 tmp_prod[4];
|
||||
|
||||
if (smin_val < 0 || dst_reg->smin_value < 0) {
|
||||
/* Ain't nobody got time to multiply that sign */
|
||||
__mark_reg64_unbounded(dst_reg);
|
||||
return;
|
||||
}
|
||||
/* Both values are positive, so we can work with unsigned and
|
||||
* copy the result to signed (unless it exceeds S64_MAX).
|
||||
*/
|
||||
if (umax_val > U32_MAX || dst_reg->umax_value > U32_MAX) {
|
||||
/* Potential overflow, we know nothing */
|
||||
__mark_reg64_unbounded(dst_reg);
|
||||
return;
|
||||
}
|
||||
dst_reg->umin_value *= umin_val;
|
||||
dst_reg->umax_value *= umax_val;
|
||||
if (dst_reg->umax_value > S64_MAX) {
|
||||
if (check_mul_overflow(*dst_umax, src_reg->umax_value, dst_umax) ||
|
||||
check_mul_overflow(*dst_umin, src_reg->umin_value, dst_umin)) {
|
||||
/* Overflow possible, we know nothing */
|
||||
dst_reg->smin_value = S64_MIN;
|
||||
dst_reg->smax_value = S64_MAX;
|
||||
*dst_umin = 0;
|
||||
*dst_umax = U64_MAX;
|
||||
}
|
||||
if (check_mul_overflow(*dst_smin, src_reg->smin_value, &tmp_prod[0]) ||
|
||||
check_mul_overflow(*dst_smin, src_reg->smax_value, &tmp_prod[1]) ||
|
||||
check_mul_overflow(*dst_smax, src_reg->smin_value, &tmp_prod[2]) ||
|
||||
check_mul_overflow(*dst_smax, src_reg->smax_value, &tmp_prod[3])) {
|
||||
/* Overflow possible, we know nothing */
|
||||
*dst_smin = S64_MIN;
|
||||
*dst_smax = S64_MAX;
|
||||
} else {
|
||||
dst_reg->smin_value = dst_reg->umin_value;
|
||||
dst_reg->smax_value = dst_reg->umax_value;
|
||||
*dst_smin = min_array(tmp_prod, 4);
|
||||
*dst_smax = max_array(tmp_prod, 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1200,4 +1200,138 @@ l0_%=: r0 = 0; \
|
|||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("tc")
|
||||
__description("multiply mixed sign bounds. test 1")
|
||||
__success __log_level(2)
|
||||
__msg("r6 *= r7 {{.*}}; R6_w=scalar(smin=umin=0x1bc16d5cd4927ee1,smax=umax=0x1bc16d674ec80000,smax32=0x7ffffeff,umax32=0xfffffeff,var_off=(0x1bc16d4000000000; 0x3ffffffeff))")
|
||||
__naked void mult_mixed0_sign(void)
|
||||
{
|
||||
asm volatile (
|
||||
"call %[bpf_get_prandom_u32];"
|
||||
"r6 = r0;"
|
||||
"call %[bpf_get_prandom_u32];"
|
||||
"r7 = r0;"
|
||||
"r6 &= 0xf;"
|
||||
"r6 -= 1000000000;"
|
||||
"r7 &= 0xf;"
|
||||
"r7 -= 2000000000;"
|
||||
"r6 *= r7;"
|
||||
"exit"
|
||||
:
|
||||
: __imm(bpf_get_prandom_u32),
|
||||
__imm(bpf_skb_store_bytes)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("tc")
|
||||
__description("multiply mixed sign bounds. test 2")
|
||||
__success __log_level(2)
|
||||
__msg("r6 *= r7 {{.*}}; R6_w=scalar(smin=smin32=-100,smax=smax32=200)")
|
||||
__naked void mult_mixed1_sign(void)
|
||||
{
|
||||
asm volatile (
|
||||
"call %[bpf_get_prandom_u32];"
|
||||
"r6 = r0;"
|
||||
"call %[bpf_get_prandom_u32];"
|
||||
"r7 = r0;"
|
||||
"r6 &= 0xf;"
|
||||
"r6 -= 0xa;"
|
||||
"r7 &= 0xf;"
|
||||
"r7 -= 0x14;"
|
||||
"r6 *= r7;"
|
||||
"exit"
|
||||
:
|
||||
: __imm(bpf_get_prandom_u32),
|
||||
__imm(bpf_skb_store_bytes)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("tc")
|
||||
__description("multiply negative bounds")
|
||||
__success __log_level(2)
|
||||
__msg("r6 *= r7 {{.*}}; R6_w=scalar(smin=umin=smin32=umin32=0x3ff280b0,smax=umax=smax32=umax32=0x3fff0001,var_off=(0x3ff00000; 0xf81ff))")
|
||||
__naked void mult_sign_bounds(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r8 = 0x7fff;"
|
||||
"call %[bpf_get_prandom_u32];"
|
||||
"r6 = r0;"
|
||||
"call %[bpf_get_prandom_u32];"
|
||||
"r7 = r0;"
|
||||
"r6 &= 0xa;"
|
||||
"r6 -= r8;"
|
||||
"r7 &= 0xf;"
|
||||
"r7 -= r8;"
|
||||
"r6 *= r7;"
|
||||
"exit"
|
||||
:
|
||||
: __imm(bpf_get_prandom_u32),
|
||||
__imm(bpf_skb_store_bytes)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("tc")
|
||||
__description("multiply bounds that don't cross signed boundary")
|
||||
__success __log_level(2)
|
||||
__msg("r8 *= r6 {{.*}}; R6_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=11,var_off=(0x0; 0xb)) R8_w=scalar(smin=0,smax=umax=0x7b96bb0a94a3a7cd,var_off=(0x0; 0x7fffffffffffffff))")
|
||||
__naked void mult_no_sign_crossing(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r6 = 0xb;"
|
||||
"r8 = 0xb3c3f8c99262687 ll;"
|
||||
"call %[bpf_get_prandom_u32];"
|
||||
"r7 = r0;"
|
||||
"r6 &= r7;"
|
||||
"r8 *= r6;"
|
||||
"exit"
|
||||
:
|
||||
: __imm(bpf_get_prandom_u32),
|
||||
__imm(bpf_skb_store_bytes)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("tc")
|
||||
__description("multiplication overflow, result in unbounded reg. test 1")
|
||||
__success __log_level(2)
|
||||
__msg("r6 *= r7 {{.*}}; R6_w=scalar()")
|
||||
__naked void mult_unsign_ovf(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r8 = 0x7ffffffffff ll;"
|
||||
"call %[bpf_get_prandom_u32];"
|
||||
"r6 = r0;"
|
||||
"call %[bpf_get_prandom_u32];"
|
||||
"r7 = r0;"
|
||||
"r6 &= 0x7fffffff;"
|
||||
"r7 &= r8;"
|
||||
"r6 *= r7;"
|
||||
"exit"
|
||||
:
|
||||
: __imm(bpf_get_prandom_u32),
|
||||
__imm(bpf_skb_store_bytes)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("tc")
|
||||
__description("multiplication overflow, result in unbounded reg. test 2")
|
||||
__success __log_level(2)
|
||||
__msg("r6 *= r7 {{.*}}; R6_w=scalar()")
|
||||
__naked void mult_sign_ovf(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r8 = 0x7ffffffff ll;"
|
||||
"call %[bpf_get_prandom_u32];"
|
||||
"r6 = r0;"
|
||||
"call %[bpf_get_prandom_u32];"
|
||||
"r7 = r0;"
|
||||
"r6 &= 0xa;"
|
||||
"r6 -= r8;"
|
||||
"r7 &= 0x7fffffff;"
|
||||
"r6 *= r7;"
|
||||
"exit"
|
||||
:
|
||||
: __imm(bpf_get_prandom_u32),
|
||||
__imm(bpf_skb_store_bytes)
|
||||
: __clobber_all);
|
||||
}
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user