mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-08-09 06:14:34 +02:00
The verifier cleans all dead registers and stack slots in the current state. Adjust expected output in tests or insert dummy stack/register reads. Also update verifier_live_stack tests to adhere to new logging scheme. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://lore.kernel.org/r/20260410-patch-set-v4-11-5d4eecb343db@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
582 lines
17 KiB
C
582 lines
17 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
|
|
/* Converted from tools/testing/selftests/bpf/prog_tests/align.c */
|
|
|
|
#include <linux/bpf.h>
|
|
#include <bpf/bpf_helpers.h>
|
|
#include "bpf_misc.h"
|
|
|
|
/* Four tests of known constants. These aren't staggeringly
|
|
* interesting since we track exact values now.
|
|
*/
|
|
|
|
SEC("tc")
|
|
__success __log_level(2)
|
|
__flag(BPF_F_ANY_ALIGNMENT)
|
|
__msg("0: R1=ctx() R10=fp0")
|
|
__msg("0: {{.*}} R3=2")
|
|
__msg("1: {{.*}} R3=4")
|
|
__msg("2: {{.*}} R3=8")
|
|
__msg("3: {{.*}} R3=16")
|
|
__msg("4: {{.*}} R3=32")
|
|
__naked void mov(void)
|
|
{
|
|
asm volatile (" \
|
|
r3 = 2; \
|
|
r3 = 4; \
|
|
r3 = 8; \
|
|
r3 = 16; \
|
|
r3 = 32; \
|
|
r0 = 0; \
|
|
exit; \
|
|
" ::: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__success __log_level(2)
|
|
__flag(BPF_F_ANY_ALIGNMENT)
|
|
__msg("0: R1=ctx() R10=fp0")
|
|
__msg("0: {{.*}}R3=1")
|
|
__msg("1: {{.*}}R3=2")
|
|
__msg("2: {{.*}}R3=4")
|
|
__msg("3: {{.*}}R3=8")
|
|
__msg("4: {{.*}}R3=16")
|
|
__msg("5: {{.*}}R3=1")
|
|
__msg("6: {{.*}}R4=32")
|
|
__msg("7: {{.*}}R4=16")
|
|
__msg("8: {{.*}}R4=8")
|
|
__msg("9: {{.*}}R4=4")
|
|
__msg("10: {{.*}}R4=2")
|
|
__naked void shift(void)
|
|
{
|
|
asm volatile (" \
|
|
r3 = 1; \
|
|
r3 <<= 1; \
|
|
r3 <<= 1; \
|
|
r3 <<= 1; \
|
|
r3 <<= 1; \
|
|
r3 >>= 4; \
|
|
r4 = 32; \
|
|
r4 >>= 1; \
|
|
r4 >>= 1; \
|
|
r4 >>= 1; \
|
|
r4 >>= 1; \
|
|
r0 = 0; \
|
|
exit; \
|
|
" ::: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__success __log_level(2)
|
|
__flag(BPF_F_ANY_ALIGNMENT)
|
|
__msg("0: R1=ctx() R10=fp0")
|
|
__msg("0: {{.*}}R3=4")
|
|
__msg("1: {{.*}}R3=8")
|
|
__msg("2: {{.*}}R3=10")
|
|
__msg("3: {{.*}}R4=8")
|
|
__msg("4: {{.*}}R4=12")
|
|
__msg("5: {{.*}}R4=14")
|
|
__naked void addsub(void)
|
|
{
|
|
asm volatile (" \
|
|
r3 = 4; \
|
|
r3 += 4; \
|
|
r3 += 2; \
|
|
r4 = 8; \
|
|
r4 += 4; \
|
|
r4 += 2; \
|
|
r0 = 0; \
|
|
exit; \
|
|
" ::: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__success __log_level(2)
|
|
__flag(BPF_F_ANY_ALIGNMENT)
|
|
__msg("0: R1=ctx() R10=fp0")
|
|
__msg("0: {{.*}}R3=7")
|
|
__msg("1: {{.*}}R3=7")
|
|
__msg("2: {{.*}}R3=14")
|
|
__msg("3: {{.*}}R3=56")
|
|
__naked void mul(void)
|
|
{
|
|
asm volatile (" \
|
|
r3 = 7; \
|
|
r3 *= 1; \
|
|
r3 *= 2; \
|
|
r3 *= 4; \
|
|
r0 = 0; \
|
|
exit; \
|
|
" ::: __clobber_all);
|
|
}
|
|
|
|
/* Tests using unknown values */
|
|
|
|
#define PREP_PKT_POINTERS \
|
|
"r2 = *(u32*)(r1 + %[__sk_buff_data]);" \
|
|
"r3 = *(u32*)(r1 + %[__sk_buff_data_end]);"
|
|
|
|
#define __LOAD_UNKNOWN(DST_REG, LBL) \
|
|
"r2 = *(u32*)(r1 + %[__sk_buff_data]);" \
|
|
"r3 = *(u32*)(r1 + %[__sk_buff_data_end]);" \
|
|
"r0 = r2;" \
|
|
"r0 += 8;" \
|
|
"if r3 >= r0 goto " LBL ";" \
|
|
"exit;" \
|
|
LBL ":" \
|
|
DST_REG " = *(u8*)(r2 + 0);"
|
|
|
|
#define LOAD_UNKNOWN(DST_REG) __LOAD_UNKNOWN(DST_REG, "l99_%=")
|
|
|
|
SEC("tc")
|
|
__success __log_level(2)
|
|
__flag(BPF_F_ANY_ALIGNMENT)
|
|
__msg("6: {{.*}} R2=pkt(r=8)")
|
|
__msg("6: {{.*}} R3={{[^)]*}}var_off=(0x0; 0xff)")
|
|
__msg("7: {{.*}} R3={{[^)]*}}var_off=(0x0; 0x1fe)")
|
|
__msg("8: {{.*}} R3={{[^)]*}}var_off=(0x0; 0x3fc)")
|
|
__msg("9: {{.*}} R3={{[^)]*}}var_off=(0x0; 0x7f8)")
|
|
__msg("10: {{.*}} R3={{[^)]*}}var_off=(0x0; 0xff0)")
|
|
__msg("12: {{.*}} R3=pkt_end()")
|
|
__msg("17: {{.*}} R4={{[^)]*}}var_off=(0x0; 0xff)")
|
|
__msg("18: {{.*}} R4={{[^)]*}}var_off=(0x0; 0x1fe0)")
|
|
__msg("19: {{.*}} R4={{[^)]*}}var_off=(0x0; 0xff0)")
|
|
__msg("20: {{.*}} R4={{[^)]*}}var_off=(0x0; 0x7f8)")
|
|
__msg("21: {{.*}} R4={{[^)]*}}var_off=(0x0; 0x3fc)")
|
|
__msg("22: {{.*}} R4={{[^)]*}}var_off=(0x0; 0x1fe)")
|
|
__naked void unknown_shift(void)
|
|
{
|
|
asm volatile (" \
|
|
" __LOAD_UNKNOWN("r3", "l99_%=") " \
|
|
r3 <<= 1; \
|
|
r3 <<= 1; \
|
|
r3 <<= 1; \
|
|
r3 <<= 1; \
|
|
" __LOAD_UNKNOWN("r4", "l98_%=") " \
|
|
r4 <<= 5; \
|
|
r4 >>= 1; \
|
|
r4 >>= 1; \
|
|
r4 >>= 1; \
|
|
r4 >>= 1; \
|
|
r0 = 0; \
|
|
exit; \
|
|
" :
|
|
: __imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
|
|
__imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__success __log_level(2)
|
|
__flag(BPF_F_ANY_ALIGNMENT)
|
|
__msg("6: {{.*}} R3={{[^)]*}}var_off=(0x0; 0xff)")
|
|
__msg("7: {{.*}} R4={{[^)]*}}var_off=(0x0; 0xff)")
|
|
__msg("8: {{.*}} R4={{[^)]*}}var_off=(0x0; 0xff)")
|
|
__msg("9: {{.*}} R4={{[^)]*}}var_off=(0x0; 0xff)")
|
|
__msg("10: {{.*}} R4={{[^)]*}}var_off=(0x0; 0x1fe)")
|
|
__msg("11: {{.*}} R4={{[^)]*}}var_off=(0x0; 0xff)")
|
|
__msg("12: {{.*}} R4={{[^)]*}}var_off=(0x0; 0x3fc)")
|
|
__msg("13: {{.*}} R4={{[^)]*}}var_off=(0x0; 0xff)")
|
|
__msg("14: {{.*}} R4={{[^)]*}}var_off=(0x0; 0x7f8)")
|
|
__msg("15: {{.*}} R4={{[^)]*}}var_off=(0x0; 0xff0)")
|
|
__naked void unknown_mul(void)
|
|
{
|
|
asm volatile (" \
|
|
" LOAD_UNKNOWN("r3") " \
|
|
r4 = r3; \
|
|
r4 *= 1; \
|
|
r4 = r3; \
|
|
r4 *= 2; \
|
|
r4 = r3; \
|
|
r4 *= 4; \
|
|
r4 = r3; \
|
|
r4 *= 8; \
|
|
r4 *= 2; \
|
|
r0 = 0; \
|
|
exit; \
|
|
" :
|
|
: __imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
|
|
__imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__success __log_level(2)
|
|
__msg("2: {{.*}} R5=pkt(r=0)")
|
|
__msg("4: {{.*}} R5=pkt(r=0,imm=14)")
|
|
__msg("5: {{.*}} R4=pkt(r=0,imm=14)")
|
|
__msg("9: {{.*}} R5=pkt(r=18,imm=14)")
|
|
__msg("10: {{.*}} R4={{[^)]*}}var_off=(0x0; 0xff){{.*}} R5=pkt(r=18,imm=14)")
|
|
__msg("13: {{.*}} R4={{[^)]*}}var_off=(0x0; 0xffff)")
|
|
__msg("14: {{.*}} R4={{[^)]*}}var_off=(0x0; 0xffff)")
|
|
__naked void packet_const_offset(void)
|
|
{
|
|
asm volatile (" \
|
|
" PREP_PKT_POINTERS " \
|
|
r5 = r2; \
|
|
r0 = 0; \
|
|
/* Skip over ethernet header. */ \
|
|
r5 += 14; \
|
|
r4 = r5; \
|
|
r4 += 4; \
|
|
if r3 >= r4 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: r4 = *(u8*)(r5 + 0); \
|
|
r4 = *(u8*)(r5 + 1); \
|
|
r4 = *(u8*)(r5 + 2); \
|
|
r4 = *(u8*)(r5 + 3); \
|
|
r4 = *(u16*)(r5 + 0); \
|
|
r4 = *(u16*)(r5 + 2); \
|
|
r4 = *(u32*)(r5 + 0); \
|
|
r0 = 0; \
|
|
exit; \
|
|
" :
|
|
: __imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
|
|
__imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__success __log_level(2)
|
|
__flag(BPF_F_ANY_ALIGNMENT)
|
|
/* Calculated offset in R6 has unknown value, but known
|
|
* alignment of 4.
|
|
*/
|
|
__msg("6: {{.*}} R2=pkt(r=8)")
|
|
__msg("7: {{.*}} R6={{[^)]*}}var_off=(0x0; 0x3fc)")
|
|
/* Offset is added to packet pointer R5, resulting in
|
|
* known fixed offset, and variable offset from R6.
|
|
*/
|
|
__msg("11: {{.*}} R5=pkt(id=1,{{[^)]*}},var_off=(0x2; 0x7fc)")
|
|
/* At the time the word size load is performed from R5,
|
|
* it's total offset is NET_IP_ALIGN + reg->off (0) +
|
|
* reg->aux_off (14) which is 16. Then the variable
|
|
* offset is considered using reg->aux_off_align which
|
|
* is 4 and meets the load's requirements.
|
|
*/
|
|
__msg("15: {{.*}} R5={{[^)]*}}var_off=(0x2; 0x7fc)")
|
|
/* Variable offset is added to R5 packet pointer,
|
|
* resulting in auxiliary alignment of 4. To avoid BPF
|
|
* verifier's precision backtracking logging
|
|
* interfering we also have a no-op R4 = R5
|
|
* instruction to validate R5 state. We also check
|
|
* that R4 is what it should be in such case.
|
|
*/
|
|
__msg("18: {{.*}} R4={{[^)]*}}var_off=(0x0; 0x3fc){{.*}} R5={{[^)]*}}var_off=(0x0; 0x3fc)")
|
|
/* Constant offset is added to R5, resulting in
|
|
* reg->off of 14.
|
|
*/
|
|
__msg("19: {{.*}} R5=pkt(id=2,{{[^)]*}}var_off=(0x2; 0x7fc)")
|
|
/* At the time the word size load is performed from R5,
|
|
* its total fixed offset is NET_IP_ALIGN + reg->off
|
|
* (14) which is 16. Then the variable offset is 4-byte
|
|
* aligned, so the total offset is 4-byte aligned and
|
|
* meets the load's requirements.
|
|
*/
|
|
__msg("24: {{.*}} R5={{[^)]*}}var_off=(0x2; 0x7fc)")
|
|
/* Constant offset is added to R5 packet pointer,
|
|
* resulting in reg->off value of 14.
|
|
*/
|
|
__msg("26: {{.*}} R5=pkt(r=8,imm=14)")
|
|
/* Variable offset is added to R5, resulting in a
|
|
* variable offset of (4n). See comment for insn #18
|
|
* for R4 = R5 trick.
|
|
*/
|
|
__msg("28: {{.*}} R4={{[^)]*}}var_off=(0x2; 0x7fc){{.*}} R5={{[^)]*}}var_off=(0x2; 0x7fc)")
|
|
/* Constant is added to R5 again, setting reg->off to 18. */
|
|
__msg("29: {{.*}} R5=pkt(id=3,{{[^)]*}}var_off=(0x2; 0x7fc)")
|
|
/* And once more we add a variable; resulting {{[^)]*}}var_off
|
|
* is still (4n), fixed offset is not changed.
|
|
* Also, we create a new reg->id.
|
|
*/
|
|
__msg("31: {{.*}} R4={{[^)]*}}var_off=(0x2; 0xffc){{.*}} R5={{[^)]*}}var_off=(0x2; 0xffc)")
|
|
/* At the time the word size load is performed from R5,
|
|
* its total fixed offset is NET_IP_ALIGN + reg->off (18)
|
|
* which is 20. Then the variable offset is (4n), so
|
|
* the total offset is 4-byte aligned and meets the
|
|
* load's requirements.
|
|
*/
|
|
__msg("35: {{.*}} R5={{[^)]*}}var_off=(0x2; 0xffc)")
|
|
__naked void packet_variable_offset(void)
|
|
{
|
|
asm volatile (" \
|
|
" LOAD_UNKNOWN("r6") " \
|
|
r6 <<= 2; \
|
|
/* First, add a constant to the R5 packet pointer,\
|
|
* then a variable with a known alignment. \
|
|
*/ \
|
|
r5 = r2; \
|
|
r5 += 14; \
|
|
r5 += r6; \
|
|
r4 = r5; \
|
|
r4 += 4; \
|
|
if r3 >= r4 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: r4 = *(u32*)(r5 + 0); \
|
|
/* Now, test in the other direction. Adding first\
|
|
* the variable offset to R5, then the constant.\
|
|
*/ \
|
|
r5 = r2; \
|
|
r5 += r6; \
|
|
r4 = r5; \
|
|
r5 += 14; \
|
|
r4 = r5; \
|
|
r4 += 4; \
|
|
if r3 >= r4 goto l1_%=; \
|
|
exit; \
|
|
l1_%=: r4 = *(u32*)(r5 + 0); \
|
|
/* Test multiple accumulations of unknown values\
|
|
* into a packet pointer. \
|
|
*/ \
|
|
r5 = r2; \
|
|
r5 += 14; \
|
|
r5 += r6; \
|
|
r4 = r5; \
|
|
r5 += 4; \
|
|
r5 += r6; \
|
|
r4 = r5; \
|
|
r4 += 4; \
|
|
if r3 >= r4 goto l2_%=; \
|
|
exit; \
|
|
l2_%=: r4 = *(u32*)(r5 + 0); \
|
|
r0 = 0; \
|
|
exit; \
|
|
" :
|
|
: __imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
|
|
__imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__success __log_level(2)
|
|
__flag(BPF_F_ANY_ALIGNMENT)
|
|
/* Calculated offset in R6 has unknown value, but known
|
|
* alignment of 4.
|
|
*/
|
|
__msg("6: {{.*}} R2=pkt(r=8)")
|
|
__msg("7: {{.*}} R6={{[^)]*}}var_off=(0x0; 0x3fc)")
|
|
/* Adding 14 makes R6 be (4n+2) */
|
|
__msg("8: {{.*}} R6={{[^)]*}}var_off=(0x2; 0x7fc)")
|
|
/* Packet pointer has (4n+2) offset */
|
|
__msg("11: {{.*}} R5={{[^)]*}}var_off=(0x2; 0x7fc)")
|
|
__msg("12: {{.*}} R4={{[^)]*}}var_off=(0x2; 0x7fc)")
|
|
/* At the time the word size load is performed from R5,
|
|
* its total fixed offset is NET_IP_ALIGN + reg->off (0)
|
|
* which is 2. Then the variable offset is (4n+2), so
|
|
* the total offset is 4-byte aligned and meets the
|
|
* load's requirements.
|
|
*/
|
|
__msg("15: {{.*}} R5={{[^)]*}}var_off=(0x2; 0x7fc)")
|
|
/* Newly read value in R6 was shifted left by 2, so has
|
|
* known alignment of 4.
|
|
*/
|
|
__msg("17: {{.*}} R6={{[^)]*}}var_off=(0x0; 0x3fc)")
|
|
/* Added (4n) to packet pointer's (4n+2) {{[^)]*}}var_off, giving
|
|
* another (4n+2).
|
|
*/
|
|
__msg("19: {{.*}} R5={{[^)]*}}var_off=(0x2; 0xffc)")
|
|
__msg("20: {{.*}} R4={{[^)]*}}var_off=(0x2; 0xffc)")
|
|
/* At the time the word size load is performed from R5,
|
|
* its total fixed offset is NET_IP_ALIGN + reg->off (0)
|
|
* which is 2. Then the variable offset is (4n+2), so
|
|
* the total offset is 4-byte aligned and meets the
|
|
* load's requirements.
|
|
*/
|
|
__msg("23: {{.*}} R5={{[^)]*}}var_off=(0x2; 0xffc)")
|
|
__naked void packet_variable_offset_2(void)
|
|
{
|
|
asm volatile (" \
|
|
/* Create an unknown offset, (4n+2)-aligned */ \
|
|
" LOAD_UNKNOWN("r6") " \
|
|
r6 <<= 2; \
|
|
r6 += 14; \
|
|
/* Add it to the packet pointer */ \
|
|
r5 = r2; \
|
|
r5 += r6; \
|
|
/* Check bounds and perform a read */ \
|
|
r4 = r5; \
|
|
r4 += 4; \
|
|
if r3 >= r4 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: r6 = *(u32*)(r5 + 0); \
|
|
/* Make a (4n) offset from the value we just read */\
|
|
r6 &= 0xff; \
|
|
r6 <<= 2; \
|
|
/* Add it to the packet pointer */ \
|
|
r5 += r6; \
|
|
/* Check bounds and perform a read */ \
|
|
r4 = r5; \
|
|
r4 += 4; \
|
|
if r3 >= r4 goto l1_%=; \
|
|
exit; \
|
|
l1_%=: r6 = *(u32*)(r5 + 0); \
|
|
r0 = 0; \
|
|
exit; \
|
|
" :
|
|
: __imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
|
|
__imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__failure __log_level(2)
|
|
__msg("3: {{.*}} R5=pkt_end()")
|
|
/* (ptr - ptr) << 2 == unknown, (4n) */
|
|
__msg("5: {{.*}} R5={{[^)]*}}var_off=(0x0; 0xfffffffffffffffc)")
|
|
/* (4n) + 14 == (4n+2). We blow our bounds, because
|
|
* the add could overflow.
|
|
*/
|
|
__msg("6: {{.*}} R5={{[^)]*}}var_off=(0x2; 0xfffffffffffffffc)")
|
|
/* Checked s>=0 */
|
|
__msg("9: {{.*}} R5={{[^)]*}}var_off=(0x2; 0x7ffffffffffffffc)")
|
|
/* packet pointer + nonnegative (4n+2) */
|
|
__msg("11: {{.*}} R4={{[^)]*}}var_off=(0x2; 0x7ffffffffffffffc){{.*}} R6={{[^)]*}}var_off=(0x2; 0x7ffffffffffffffc)")
|
|
__msg("12: (07) r4 += 4")
|
|
/* packet smax bound overflow */
|
|
__msg("pkt pointer offset -9223372036854775808 is not allowed")
|
|
__naked void dubious_pointer_arithmetic(void)
|
|
{
|
|
asm volatile (" \
|
|
" PREP_PKT_POINTERS " \
|
|
r0 = 0; \
|
|
/* (ptr - ptr) << 2 */ \
|
|
r5 = r3; \
|
|
r5 -= r2; \
|
|
r5 <<= 2; \
|
|
/* We have a (4n) value. Let's make a packet offset\
|
|
* out of it. First add 14, to make it a (4n+2)\
|
|
*/ \
|
|
r5 += 14; \
|
|
/* Then make sure it's nonnegative */ \
|
|
if r5 s>= 0 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: /* Add it to packet pointer */ \
|
|
r6 = r2; \
|
|
r6 += r5; \
|
|
/* Check bounds and perform a read */ \
|
|
r4 = r6; \
|
|
r4 += 4; \
|
|
if r3 >= r4 goto l1_%=; \
|
|
exit; \
|
|
l1_%=: r4 = *(u32*)(r6 + 0); \
|
|
exit; \
|
|
" :
|
|
: __imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
|
|
__imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__success __log_level(2)
|
|
__flag(BPF_F_ANY_ALIGNMENT)
|
|
/* Calculated offset in R6 has unknown value, but known
|
|
* alignment of 4.
|
|
*/
|
|
__msg("6: {{.*}} R2=pkt(r=8)")
|
|
__msg("8: {{.*}} R6={{[^)]*}}var_off=(0x0; 0x3fc)")
|
|
/* Adding 14 makes R6 be (4n+2) */
|
|
__msg("9: {{.*}} R6={{[^)]*}}var_off=(0x2; 0x7fc)")
|
|
/* New unknown value in R7 is (4n) */
|
|
__msg("10: {{.*}} R7={{[^)]*}}var_off=(0x0; 0x3fc)")
|
|
/* Subtracting it from R6 blows our unsigned bounds */
|
|
__msg("11: {{.*}} R6={{[^)]*}}var_off=(0x2; 0xfffffffffffffffc)")
|
|
/* Checked s>= 0 */
|
|
__msg("14: {{.*}} R6={{[^)]*}}var_off=(0x2; 0x7fc)")
|
|
/* At the time the word size load is performed from R5,
|
|
* its total fixed offset is NET_IP_ALIGN + reg->off (0)
|
|
* which is 2. Then the variable offset is (4n+2), so
|
|
* the total offset is 4-byte aligned and meets the
|
|
* load's requirements.
|
|
*/
|
|
__msg("20: {{.*}} R5={{[^)]*}}var_off=(0x2; 0x7fc)")
|
|
__naked void variable_subtraction(void)
|
|
{
|
|
asm volatile (" \
|
|
/* Create an unknown offset, (4n+2)-aligned */ \
|
|
" LOAD_UNKNOWN("r6") " \
|
|
r7 = r6; \
|
|
r6 <<= 2; \
|
|
r6 += 14; \
|
|
/* Create another unknown, (4n)-aligned, and subtract\
|
|
* it from the first one \
|
|
*/ \
|
|
r7 <<= 2; \
|
|
r6 -= r7; \
|
|
/* Bounds-check the result */ \
|
|
if r6 s>= 0 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: /* Add it to the packet pointer */ \
|
|
r5 = r2; \
|
|
r5 += r6; \
|
|
/* Check bounds and perform a read */ \
|
|
r4 = r5; \
|
|
r4 += 4; \
|
|
if r3 >= r4 goto l1_%=; \
|
|
exit; \
|
|
l1_%=: r6 = *(u32*)(r5 + 0); \
|
|
exit; \
|
|
" :
|
|
: __imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
|
|
__imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__success __log_level(2)
|
|
__flag(BPF_F_ANY_ALIGNMENT)
|
|
/* Calculated offset in R6 has unknown value, but known
|
|
* alignment of 4.
|
|
*/
|
|
__msg("6: {{.*}} R2=pkt(r=8)")
|
|
__msg("9: {{.*}} R6={{[^)]*}}var_off=(0x0; 0x3c)")
|
|
/* Adding 14 makes R6 be (4n+2) */
|
|
__msg("10: {{.*}} R6={{[^)]*}}var_off=(0x2; 0x7c)")
|
|
/* Subtracting from packet pointer overflows ubounds */
|
|
__msg("13: R5={{[^)]*}}var_off=(0xffffffffffffff82; 0x7c)")
|
|
/* New unknown value in R7 is (4n), >= 76 */
|
|
__msg("14: {{.*}} R7={{[^)]*}}var_off=(0x0; 0x7fc)")
|
|
/* Adding it to packet pointer gives nice bounds again */
|
|
__msg("16: {{.*}} R5={{[^)]*}}var_off=(0x2; 0x7fc)")
|
|
/* At the time the word size load is performed from R5,
|
|
* its total fixed offset is NET_IP_ALIGN + reg->off (0)
|
|
* which is 2. Then the variable offset is (4n+2), so
|
|
* the total offset is 4-byte aligned and meets the
|
|
* load's requirements.
|
|
*/
|
|
__msg("20: {{.*}} R5={{[^)]*}}var_off=(0x2; 0x7fc)")
|
|
__naked void pointer_variable_subtraction(void)
|
|
{
|
|
asm volatile (" \
|
|
/* Create an unknown offset, (4n+2)-aligned and bounded\
|
|
* to [14,74] \
|
|
*/ \
|
|
" LOAD_UNKNOWN("r6") " \
|
|
r7 = r6; \
|
|
r6 &= 0xf; \
|
|
r6 <<= 2; \
|
|
r6 += 14; \
|
|
/* Subtract it from the packet pointer */ \
|
|
r5 = r2; \
|
|
r5 -= r6; \
|
|
/* Create another unknown, (4n)-aligned and >= 74.\
|
|
* That in fact means >= 76, since 74 mod 4 == 2\
|
|
*/ \
|
|
r7 <<= 2; \
|
|
r7 += 76; \
|
|
/* Add it to the packet pointer */ \
|
|
r5 += r7; \
|
|
/* Check bounds and perform a read */ \
|
|
r4 = r5; \
|
|
r4 += 4; \
|
|
if r3 >= r4 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: r6 = *(u32*)(r5 + 0); \
|
|
exit; \
|
|
" :
|
|
: __imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
|
|
__imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end))
|
|
: __clobber_all);
|
|
}
|
|
|
|
char _license[] SEC("license") = "GPL";
|