mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-04-06 06:43:13 -04:00
selftests/bpf: Add tests for improved linked register tracking
Add tests for linked register tracking with negative offsets, BPF_SUB, and alu32. These test for all edge cases like overflows, etc. Signed-off-by: Puranjay Mohan <puranjay@kernel.org> Acked-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://lore.kernel.org/r/20260204151741.2678118-3-puranjay@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
committed by
Alexei Starovoitov
parent
7a433e5193
commit
47fcf4dc0a
@@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <limits.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "bpf_misc.h"
|
||||
|
||||
@@ -18,9 +19,9 @@ __naked void scalars(void)
|
||||
r4 = r1; \
|
||||
w2 += 0x7FFFFFFF; \
|
||||
w4 += 0; \
|
||||
if r2 == 0 goto l1; \
|
||||
if r2 == 0 goto l0_%=; \
|
||||
exit; \
|
||||
l1: \
|
||||
l0_%=: \
|
||||
r4 >>= 63; \
|
||||
r3 = 1; \
|
||||
r3 -= r4; \
|
||||
@@ -64,4 +65,302 @@ l0_%=: \
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__success
|
||||
__naked void scalars_neg(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
r0 &= 0xff; \
|
||||
r1 = r0; \
|
||||
r1 += -4; \
|
||||
if r1 s< 0 goto l0_%=; \
|
||||
if r0 != 0 goto l0_%=; \
|
||||
r0 /= 0; \
|
||||
l0_%=: \
|
||||
r0 = 0; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
/* Same test but using BPF_SUB instead of BPF_ADD with negative immediate */
|
||||
SEC("socket")
|
||||
__success
|
||||
__naked void scalars_neg_sub(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
r0 &= 0xff; \
|
||||
r1 = r0; \
|
||||
r1 -= 4; \
|
||||
if r1 s< 0 goto l0_%=; \
|
||||
if r0 != 0 goto l0_%=; \
|
||||
r0 /= 0; \
|
||||
l0_%=: \
|
||||
r0 = 0; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
/* alu32 with negative offset */
|
||||
SEC("socket")
|
||||
__success
|
||||
__naked void scalars_neg_alu32_add(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
w0 &= 0xff; \
|
||||
w1 = w0; \
|
||||
w1 += -4; \
|
||||
if w1 s< 0 goto l0_%=; \
|
||||
if w0 != 0 goto l0_%=; \
|
||||
r0 /= 0; \
|
||||
l0_%=: \
|
||||
r0 = 0; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
/* alu32 with negative offset using SUB */
|
||||
SEC("socket")
|
||||
__success
|
||||
__naked void scalars_neg_alu32_sub(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
w0 &= 0xff; \
|
||||
w1 = w0; \
|
||||
w1 -= 4; \
|
||||
if w1 s< 0 goto l0_%=; \
|
||||
if w0 != 0 goto l0_%=; \
|
||||
r0 /= 0; \
|
||||
l0_%=: \
|
||||
r0 = 0; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
/* Positive offset: r1 = r0 + 4, then if r1 >= 6, r0 >= 2, so r0 != 0 */
|
||||
SEC("socket")
|
||||
__success
|
||||
__naked void scalars_pos(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
r0 &= 0xff; \
|
||||
r1 = r0; \
|
||||
r1 += 4; \
|
||||
if r1 < 6 goto l0_%=; \
|
||||
if r0 != 0 goto l0_%=; \
|
||||
r0 /= 0; \
|
||||
l0_%=: \
|
||||
r0 = 0; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
/* SUB with negative immediate: r1 -= -4 is equivalent to r1 += 4 */
|
||||
SEC("socket")
|
||||
__success
|
||||
__naked void scalars_sub_neg_imm(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
r0 &= 0xff; \
|
||||
r1 = r0; \
|
||||
r1 -= -4; \
|
||||
if r1 < 6 goto l0_%=; \
|
||||
if r0 != 0 goto l0_%=; \
|
||||
r0 /= 0; \
|
||||
l0_%=: \
|
||||
r0 = 0; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
/* Double ADD clears the ID (can't accumulate offsets) */
|
||||
SEC("socket")
|
||||
__failure
|
||||
__msg("div by zero")
|
||||
__naked void scalars_double_add(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
r0 &= 0xff; \
|
||||
r1 = r0; \
|
||||
r1 += 2; \
|
||||
r1 += 2; \
|
||||
if r1 < 6 goto l0_%=; \
|
||||
if r0 != 0 goto l0_%=; \
|
||||
r0 /= 0; \
|
||||
l0_%=: \
|
||||
r0 = 0; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that sync_linked_regs() correctly handles large offset differences.
|
||||
* r1.off = S32_MIN, r2.off = 1, delta = S32_MIN - 1 requires 64-bit math.
|
||||
*/
|
||||
SEC("socket")
|
||||
__success
|
||||
__naked void scalars_sync_delta_overflow(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
r0 &= 0xff; \
|
||||
r1 = r0; \
|
||||
r2 = r0; \
|
||||
r1 += %[s32_min]; \
|
||||
r2 += 1; \
|
||||
if r2 s< 100 goto l0_%=; \
|
||||
if r1 s< 0 goto l0_%=; \
|
||||
r0 /= 0; \
|
||||
l0_%=: \
|
||||
r0 = 0; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32),
|
||||
[s32_min]"i"(INT_MIN)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
/*
|
||||
* Another large delta case: r1.off = S32_MAX, r2.off = -1.
|
||||
* delta = S32_MAX - (-1) = S32_MAX + 1 requires 64-bit math.
|
||||
*/
|
||||
SEC("socket")
|
||||
__success
|
||||
__naked void scalars_sync_delta_overflow_large_range(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
r0 &= 0xff; \
|
||||
r1 = r0; \
|
||||
r2 = r0; \
|
||||
r1 += %[s32_max]; \
|
||||
r2 += -1; \
|
||||
if r2 s< 0 goto l0_%=; \
|
||||
if r1 s>= 0 goto l0_%=; \
|
||||
r0 /= 0; \
|
||||
l0_%=: \
|
||||
r0 = 0; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32),
|
||||
[s32_max]"i"(INT_MAX)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test linked scalar tracking with alu32 and large positive offset (0x7FFFFFFF).
|
||||
* After w1 += 0x7FFFFFFF, w1 wraps to negative for any r0 >= 1.
|
||||
* If w1 is signed-negative, then r0 >= 1, so r0 != 0.
|
||||
*/
|
||||
SEC("socket")
|
||||
__success
|
||||
__naked void scalars_alu32_big_offset(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
w0 &= 0xff; \
|
||||
w1 = w0; \
|
||||
w1 += 0x7FFFFFFF; \
|
||||
if w1 s>= 0 goto l0_%=; \
|
||||
if w0 != 0 goto l0_%=; \
|
||||
r0 /= 0; \
|
||||
l0_%=: \
|
||||
r0 = 0; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__failure
|
||||
__msg("div by zero")
|
||||
__naked void scalars_alu32_basic(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
r1 = r0; \
|
||||
w1 += 1; \
|
||||
if r1 > 10 goto 1f; \
|
||||
r0 >>= 32; \
|
||||
if r0 == 0 goto 1f; \
|
||||
r0 /= 0; \
|
||||
1: \
|
||||
r0 = 0; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test alu32 linked register tracking with wrapping.
|
||||
* R0 is bounded to [0xffffff00, 0xffffffff] (high 32-bit values)
|
||||
* w1 += 0x100 causes R1 to wrap to [0, 0xff]
|
||||
*
|
||||
* After sync_linked_regs, if bounds are computed correctly:
|
||||
* R0 should be [0x00000000_ffffff00, 0x00000000_ffffff80]
|
||||
* R0 >> 32 == 0, so div by zero is unreachable
|
||||
*
|
||||
* If bounds are computed incorrectly (64-bit underflow):
|
||||
* R0 becomes [0xffffffff_ffffff00, 0xffffffff_ffffff80]
|
||||
* R0 >> 32 == 0xffffffff != 0, so div by zero is reachable
|
||||
*/
|
||||
SEC("socket")
|
||||
__success
|
||||
__naked void scalars_alu32_wrap(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
call %[bpf_get_prandom_u32]; \
|
||||
w0 |= 0xffffff00; \
|
||||
r1 = r0; \
|
||||
w1 += 0x100; \
|
||||
if r1 > 0x80 goto l0_%=; \
|
||||
r2 = r0; \
|
||||
r2 >>= 32; \
|
||||
if r2 == 0 goto l0_%=; \
|
||||
r0 /= 0; \
|
||||
l0_%=: \
|
||||
r0 = 0; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_get_prandom_u32)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__success
|
||||
void alu32_negative_offset(void)
|
||||
{
|
||||
volatile char path[5];
|
||||
volatile int offset = bpf_get_prandom_u32();
|
||||
int off = offset;
|
||||
|
||||
if (off >= 5 && off < 10)
|
||||
path[off - 5] = '.';
|
||||
|
||||
/* So compiler doesn't say: error: variable 'path' set but not used */
|
||||
__sink(path[0]);
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
Reference in New Issue
Block a user