summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/bpf/progs/verifier_align.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/testing/selftests/bpf/progs/verifier_align.c')
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_align.c581
1 files changed, 581 insertions, 0 deletions
diff --git a/tools/testing/selftests/bpf/progs/verifier_align.c b/tools/testing/selftests/bpf/progs/verifier_align.c
new file mode 100644
index 000000000000..3e52686515ca
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_align.c
@@ -0,0 +1,581 @@
+// 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";