1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00
linux/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c
Paolo Abeni 7b769adc26 bpf-next-for-netdev
-----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQTFp0I1jqZrAX+hPRXbK58LschIgwUCZoxN0AAKCRDbK58LschI
 g0c5AQDa3ZV9gfbN42y1zSDoM1uOgO60fb+ydxyOYh8l3+OiQQD/fLfpTY3gBFSY
 9yi/pZhw/QdNzQskHNIBrHFGtJbMxgs=
 =p1Zz
 -----END PGP SIGNATURE-----

Merge tag 'for-netdev' of https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next

Daniel Borkmann says:

====================
pull-request: bpf-next 2024-07-08

The following pull-request contains BPF updates for your *net-next* tree.

We've added 102 non-merge commits during the last 28 day(s) which contain
a total of 127 files changed, 4606 insertions(+), 980 deletions(-).

The main changes are:

1) Support resilient split BTF which cuts down on duplication and makes BTF
   as compact as possible wrt BTF from modules, from Alan Maguire & Eduard Zingerman.

2) Add support for dumping kfunc prototypes from BTF which enables both detecting
   as well as dumping compilable prototypes for kfuncs, from Daniel Xu.

3) Batch of s390x BPF JIT improvements to add support for BPF arena and to implement
   support for BPF exceptions, from Ilya Leoshkevich.

4) Batch of riscv64 BPF JIT improvements in particular to add 12-argument support
   for BPF trampolines and to utilize bpf_prog_pack for the latter, from Pu Lehui.

5) Extend BPF test infrastructure to add a CHECKSUM_COMPLETE validation option
   for skbs and add coverage along with it, from Vadim Fedorenko.

6) Inline bpf_get_current_task/_btf() helpers in the arm64 BPF JIT which gives
   a small 1% performance improvement in micro-benchmarks, from Puranjay Mohan.

7) Extend the BPF verifier to track the delta between linked registers in order
   to better deal with recent LLVM code optimizations, from Alexei Starovoitov.

8) Fix bpf_wq_set_callback_impl() kfunc signature where the third argument should
   have been a pointer to the map value, from Benjamin Tissoires.

9) Extend BPF selftests to add regular expression support for test output matching
   and adjust some of the selftest when compiled under gcc, from Cupertino Miranda.

10) Simplify task_file_seq_get_next() and remove an unnecessary loop which always
    iterates exactly once anyway, from Dan Carpenter.

11) Add the capability to offload the netfilter flowtable in XDP layer through
    kfuncs, from Florian Westphal & Lorenzo Bianconi.

12) Various cleanups in networking helpers in BPF selftests to shave off a few
    lines of open-coded functions on client/server handling, from Geliang Tang.

13) Properly propagate prog->aux->tail_call_reachable out of BPF verifier, so
    that x86 JIT does not need to implement detection, from Leon Hwang.

14) Fix BPF verifier to add a missing check_func_arg_reg_off() to prevent an
    out-of-bounds memory access for dynpointers, from Matt Bobrowski.

15) Fix bpf_session_cookie() kfunc to return __u64 instead of long pointer as
    it might lead to problems on 32-bit archs, from Jiri Olsa.

16) Enhance traffic validation and dynamic batch size support in xsk selftests,
    from Tushar Vyavahare.

bpf-next-for-netdev

* tag 'for-netdev' of https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next: (102 commits)
  selftests/bpf: DENYLIST.aarch64: Remove fexit_sleep
  selftests/bpf: amend for wrong bpf_wq_set_callback_impl signature
  bpf: helpers: fix bpf_wq_set_callback_impl signature
  libbpf: Add NULL checks to bpf_object__{prev_map,next_map}
  selftests/bpf: Remove exceptions tests from DENYLIST.s390x
  s390/bpf: Implement exceptions
  s390/bpf: Change seen_reg to a mask
  bpf: Remove unnecessary loop in task_file_seq_get_next()
  riscv, bpf: Optimize stack usage of trampoline
  bpf, devmap: Add .map_alloc_check
  selftests/bpf: Remove arena tests from DENYLIST.s390x
  selftests/bpf: Add UAF tests for arena atomics
  selftests/bpf: Introduce __arena_global
  s390/bpf: Support arena atomics
  s390/bpf: Enable arena
  s390/bpf: Support address space cast instruction
  s390/bpf: Support BPF_PROBE_MEM32
  s390/bpf: Land on the next JITed instruction after exception
  s390/bpf: Introduce pre- and post- probe functions
  s390/bpf: Get rid of get_probe_mem_regno()
  ...
====================

Link: https://patch.msgid.link/20240708221438.10974-1-daniel@iogearbox.net
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2024-07-09 17:01:46 +02:00

790 lines
16 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include "bpf_misc.h"
#include "bpf_experimental.h"
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 8);
__type(key, __u32);
__type(value, __u64);
} map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_USER_RINGBUF);
__uint(max_entries, 8);
} ringbuf SEC(".maps");
struct vm_area_struct;
struct bpf_map;
struct buf_context {
char *buf;
};
struct num_context {
__u64 i;
__u64 j;
};
__u8 choice_arr[2] = { 0, 1 };
static int unsafe_on_2nd_iter_cb(__u32 idx, struct buf_context *ctx)
{
if (idx == 0) {
ctx->buf = (char *)(0xDEAD);
return 0;
}
if (bpf_probe_read_user(ctx->buf, 8, (void *)(0xBADC0FFEE)))
return 1;
return 0;
}
SEC("?raw_tp")
__failure __msg("R1 type=scalar expected=fp")
int unsafe_on_2nd_iter(void *unused)
{
char buf[4];
struct buf_context loop_ctx = { .buf = buf };
bpf_loop(100, unsafe_on_2nd_iter_cb, &loop_ctx, 0);
return 0;
}
static int unsafe_on_zero_iter_cb(__u32 idx, struct num_context *ctx)
{
ctx->i = 0;
return 0;
}
SEC("?raw_tp")
__failure __msg("invalid access to map value, value_size=2 off=32 size=1")
int unsafe_on_zero_iter(void *unused)
{
struct num_context loop_ctx = { .i = 32 };
bpf_loop(100, unsafe_on_zero_iter_cb, &loop_ctx, 0);
return choice_arr[loop_ctx.i];
}
static int widening_cb(__u32 idx, struct num_context *ctx)
{
++ctx->i;
return 0;
}
SEC("?raw_tp")
__success
int widening(void *unused)
{
struct num_context loop_ctx = { .i = 0, .j = 1 };
bpf_loop(100, widening_cb, &loop_ctx, 0);
/* loop_ctx.j is not changed during callback iteration,
* verifier should not apply widening to it.
*/
return choice_arr[loop_ctx.j];
}
static int loop_detection_cb(__u32 idx, struct num_context *ctx)
{
for (;;) {}
return 0;
}
SEC("?raw_tp")
__failure __msg("infinite loop detected")
int loop_detection(void *unused)
{
struct num_context loop_ctx = { .i = 0 };
bpf_loop(100, loop_detection_cb, &loop_ctx, 0);
return 0;
}
static __always_inline __u64 oob_state_machine(struct num_context *ctx)
{
switch (ctx->i) {
case 0:
ctx->i = 1;
break;
case 1:
ctx->i = 32;
break;
}
return 0;
}
static __u64 for_each_map_elem_cb(struct bpf_map *map, __u32 *key, __u64 *val, void *data)
{
return oob_state_machine(data);
}
SEC("?raw_tp")
__failure __msg("invalid access to map value, value_size=2 off=32 size=1")
int unsafe_for_each_map_elem(void *unused)
{
struct num_context loop_ctx = { .i = 0 };
bpf_for_each_map_elem(&map, for_each_map_elem_cb, &loop_ctx, 0);
return choice_arr[loop_ctx.i];
}
static __u64 ringbuf_drain_cb(struct bpf_dynptr *dynptr, void *data)
{
return oob_state_machine(data);
}
SEC("?raw_tp")
__failure __msg("invalid access to map value, value_size=2 off=32 size=1")
int unsafe_ringbuf_drain(void *unused)
{
struct num_context loop_ctx = { .i = 0 };
bpf_user_ringbuf_drain(&ringbuf, ringbuf_drain_cb, &loop_ctx, 0);
return choice_arr[loop_ctx.i];
}
static __u64 find_vma_cb(struct task_struct *task, struct vm_area_struct *vma, void *data)
{
return oob_state_machine(data);
}
SEC("?raw_tp")
__failure __msg("invalid access to map value, value_size=2 off=32 size=1")
int unsafe_find_vma(void *unused)
{
struct task_struct *task = bpf_get_current_task_btf();
struct num_context loop_ctx = { .i = 0 };
bpf_find_vma(task, 0, find_vma_cb, &loop_ctx, 0);
return choice_arr[loop_ctx.i];
}
static int iter_limit_cb(__u32 idx, struct num_context *ctx)
{
ctx->i++;
return 0;
}
SEC("?raw_tp")
__success
int bpf_loop_iter_limit_ok(void *unused)
{
struct num_context ctx = { .i = 0 };
bpf_loop(1, iter_limit_cb, &ctx, 0);
return choice_arr[ctx.i];
}
SEC("?raw_tp")
__failure __msg("invalid access to map value, value_size=2 off=2 size=1")
int bpf_loop_iter_limit_overflow(void *unused)
{
struct num_context ctx = { .i = 0 };
bpf_loop(2, iter_limit_cb, &ctx, 0);
return choice_arr[ctx.i];
}
static int iter_limit_level2a_cb(__u32 idx, struct num_context *ctx)
{
ctx->i += 100;
return 0;
}
static int iter_limit_level2b_cb(__u32 idx, struct num_context *ctx)
{
ctx->i += 10;
return 0;
}
static int iter_limit_level1_cb(__u32 idx, struct num_context *ctx)
{
ctx->i += 1;
bpf_loop(1, iter_limit_level2a_cb, ctx, 0);
bpf_loop(1, iter_limit_level2b_cb, ctx, 0);
return 0;
}
/* Check that path visiting every callback function once had been
* reached by verifier. Variables 'ctx{1,2}i' below serve as flags,
* with each decimal digit corresponding to a callback visit marker.
*/
SEC("socket")
__success __retval(111111)
int bpf_loop_iter_limit_nested(void *unused)
{
struct num_context ctx1 = { .i = 0 };
struct num_context ctx2 = { .i = 0 };
__u64 a, b, c;
bpf_loop(1, iter_limit_level1_cb, &ctx1, 0);
bpf_loop(1, iter_limit_level1_cb, &ctx2, 0);
a = ctx1.i;
b = ctx2.i;
/* Force 'ctx1.i' and 'ctx2.i' precise. */
c = choice_arr[(a + b) % 2];
/* This makes 'c' zero, but neither clang nor verifier know it. */
c /= 10;
/* Make sure that verifier does not visit 'impossible' states:
* enumerate all possible callback visit masks.
*/
if (a != 0 && a != 1 && a != 11 && a != 101 && a != 111 &&
b != 0 && b != 1 && b != 11 && b != 101 && b != 111)
asm volatile ("r0 /= 0;" ::: "r0");
return 1000 * a + b + c;
}
struct iter_limit_bug_ctx {
__u64 a;
__u64 b;
__u64 c;
};
static __naked void iter_limit_bug_cb(void)
{
/* This is the same as C code below, but written
* in assembly to control which branches are fall-through.
*
* switch (bpf_get_prandom_u32()) {
* case 1: ctx->a = 42; break;
* case 2: ctx->b = 42; break;
* default: ctx->c = 42; break;
* }
*/
asm volatile (
"r9 = r2;"
"call %[bpf_get_prandom_u32];"
"r1 = r0;"
"r2 = 42;"
"r0 = 0;"
"if r1 == 0x1 goto 1f;"
"if r1 == 0x2 goto 2f;"
"*(u64 *)(r9 + 16) = r2;"
"exit;"
"1: *(u64 *)(r9 + 0) = r2;"
"exit;"
"2: *(u64 *)(r9 + 8) = r2;"
"exit;"
:
: __imm(bpf_get_prandom_u32)
: __clobber_all
);
}
int tmp_var;
SEC("socket")
__failure __msg("infinite loop detected at insn 2")
__naked void jgt_imm64_and_may_goto(void)
{
asm volatile (" \
r0 = %[tmp_var] ll; \
l0_%=: .byte 0xe5; /* may_goto */ \
.byte 0; /* regs */ \
.short -3; /* off -3 */ \
.long 0; /* imm */ \
if r0 > 10 goto l0_%=; \
r0 = 0; \
exit; \
" :: __imm_addr(tmp_var)
: __clobber_all);
}
SEC("socket")
__failure __msg("infinite loop detected at insn 1")
__naked void may_goto_self(void)
{
asm volatile (" \
r0 = *(u32 *)(r10 - 4); \
l0_%=: .byte 0xe5; /* may_goto */ \
.byte 0; /* regs */ \
.short -1; /* off -1 */ \
.long 0; /* imm */ \
if r0 > 10 goto l0_%=; \
r0 = 0; \
exit; \
" ::: __clobber_all);
}
SEC("socket")
__success __retval(0)
__naked void may_goto_neg_off(void)
{
asm volatile (" \
r0 = *(u32 *)(r10 - 4); \
goto l0_%=; \
goto l1_%=; \
l0_%=: .byte 0xe5; /* may_goto */ \
.byte 0; /* regs */ \
.short -2; /* off -2 */ \
.long 0; /* imm */ \
if r0 > 10 goto l0_%=; \
l1_%=: r0 = 0; \
exit; \
" ::: __clobber_all);
}
SEC("tc")
__failure
__flag(BPF_F_TEST_STATE_FREQ)
int iter_limit_bug(struct __sk_buff *skb)
{
struct iter_limit_bug_ctx ctx = { 7, 7, 7 };
bpf_loop(2, iter_limit_bug_cb, &ctx, 0);
/* This is the same as C code below,
* written in assembly to guarantee checks order.
*
* if (ctx.a == 42 && ctx.b == 42 && ctx.c == 7)
* asm volatile("r1 /= 0;":::"r1");
*/
asm volatile (
"r1 = *(u64 *)%[ctx_a];"
"if r1 != 42 goto 1f;"
"r1 = *(u64 *)%[ctx_b];"
"if r1 != 42 goto 1f;"
"r1 = *(u64 *)%[ctx_c];"
"if r1 != 7 goto 1f;"
"r1 /= 0;"
"1:"
:
: [ctx_a]"m"(ctx.a),
[ctx_b]"m"(ctx.b),
[ctx_c]"m"(ctx.c)
: "r1"
);
return 0;
}
SEC("socket")
__success __retval(0)
__naked void ja_and_may_goto(void)
{
asm volatile (" \
l0_%=: .byte 0xe5; /* may_goto */ \
.byte 0; /* regs */ \
.short 1; /* off 1 */ \
.long 0; /* imm */ \
goto l0_%=; \
r0 = 0; \
exit; \
" ::: __clobber_common);
}
SEC("socket")
__success __retval(0)
__naked void ja_and_may_goto2(void)
{
asm volatile (" \
l0_%=: r0 = 0; \
.byte 0xe5; /* may_goto */ \
.byte 0; /* regs */ \
.short 1; /* off 1 */ \
.long 0; /* imm */ \
goto l0_%=; \
r0 = 0; \
exit; \
" ::: __clobber_common);
}
SEC("socket")
__success __retval(0)
__naked void jlt_and_may_goto(void)
{
asm volatile (" \
l0_%=: call %[bpf_jiffies64]; \
.byte 0xe5; /* may_goto */ \
.byte 0; /* regs */ \
.short 1; /* off 1 */ \
.long 0; /* imm */ \
if r0 < 10 goto l0_%=; \
r0 = 0; \
exit; \
" :: __imm(bpf_jiffies64)
: __clobber_all);
}
#if (defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86) || \
(defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64) || \
defined(__TARGET_ARCH_arm) || defined(__TARGET_ARCH_s390) || \
defined(__TARGET_ARCH_loongarch)) && \
__clang_major__ >= 18
SEC("socket")
__success __retval(0)
__naked void gotol_and_may_goto(void)
{
asm volatile (" \
l0_%=: r0 = 0; \
.byte 0xe5; /* may_goto */ \
.byte 0; /* regs */ \
.short 1; /* off 1 */ \
.long 0; /* imm */ \
gotol l0_%=; \
r0 = 0; \
exit; \
" ::: __clobber_common);
}
#endif
SEC("socket")
__success __retval(0)
__naked void ja_and_may_goto_subprog(void)
{
asm volatile (" \
call subprog_with_may_goto; \
exit; \
" ::: __clobber_all);
}
static __naked __noinline __used
void subprog_with_may_goto(void)
{
asm volatile (" \
l0_%=: .byte 0xe5; /* may_goto */ \
.byte 0; /* regs */ \
.short 1; /* off 1 */ \
.long 0; /* imm */ \
goto l0_%=; \
r0 = 0; \
exit; \
" ::: __clobber_all);
}
#define ARR_SZ 1000000
int zero;
char arr[ARR_SZ];
SEC("socket")
__success __retval(0xd495cdc0)
int cond_break1(const void *ctx)
{
unsigned long i;
unsigned int sum = 0;
for (i = zero; i < ARR_SZ && can_loop; i++)
sum += i;
for (i = zero; i < ARR_SZ; i++) {
barrier_var(i);
sum += i + arr[i];
cond_break;
}
return sum;
}
SEC("socket")
__success __retval(999000000)
int cond_break2(const void *ctx)
{
int i, j;
int sum = 0;
for (i = zero; i < 1000 && can_loop; i++)
for (j = zero; j < 1000; j++) {
sum += i + j;
cond_break;
}
return sum;
}
static __noinline int loop(void)
{
int i, sum = 0;
for (i = zero; i <= 1000000 && can_loop; i++)
sum += i;
return sum;
}
SEC("socket")
__success __retval(0x6a5a2920)
int cond_break3(const void *ctx)
{
return loop();
}
SEC("socket")
__success __retval(1)
int cond_break4(const void *ctx)
{
int cnt = zero;
for (;;) {
/* should eventually break out of the loop */
cond_break;
cnt++;
}
/* if we looped a bit, it's a success */
return cnt > 1 ? 1 : 0;
}
static __noinline int static_subprog(void)
{
int cnt = zero;
for (;;) {
cond_break;
cnt++;
}
return cnt;
}
SEC("socket")
__success __retval(1)
int cond_break5(const void *ctx)
{
int cnt1 = zero, cnt2;
for (;;) {
cond_break;
cnt1++;
}
cnt2 = static_subprog();
/* main and subprog have to loop a bit */
return cnt1 > 1 && cnt2 > 1 ? 1 : 0;
}
#define ARR2_SZ 1000
SEC(".data.arr2")
char arr2[ARR2_SZ];
SEC("socket")
__success __flag(BPF_F_TEST_STATE_FREQ)
int loop_inside_iter(const void *ctx)
{
struct bpf_iter_num it;
int *v, sum = 0;
__u64 i = 0;
bpf_iter_num_new(&it, 0, ARR2_SZ);
while ((v = bpf_iter_num_next(&it))) {
if (i < ARR2_SZ)
sum += arr2[i++];
}
bpf_iter_num_destroy(&it);
return sum;
}
SEC("socket")
__success __flag(BPF_F_TEST_STATE_FREQ)
int loop_inside_iter_signed(const void *ctx)
{
struct bpf_iter_num it;
int *v, sum = 0;
long i = 0;
bpf_iter_num_new(&it, 0, ARR2_SZ);
while ((v = bpf_iter_num_next(&it))) {
if (i < ARR2_SZ && i >= 0)
sum += arr2[i++];
}
bpf_iter_num_destroy(&it);
return sum;
}
volatile const int limit = ARR2_SZ;
SEC("socket")
__success __flag(BPF_F_TEST_STATE_FREQ)
int loop_inside_iter_volatile_limit(const void *ctx)
{
struct bpf_iter_num it;
int *v, sum = 0;
__u64 i = 0;
bpf_iter_num_new(&it, 0, ARR2_SZ);
while ((v = bpf_iter_num_next(&it))) {
if (i < limit)
sum += arr2[i++];
}
bpf_iter_num_destroy(&it);
return sum;
}
#define ARR_LONG_SZ 1000
SEC(".data.arr_long")
long arr_long[ARR_LONG_SZ];
SEC("socket")
__success
int test1(const void *ctx)
{
long i;
for (i = 0; i < ARR_LONG_SZ && can_loop; i++)
arr_long[i] = i;
return 0;
}
SEC("socket")
__success
int test2(const void *ctx)
{
__u64 i;
for (i = zero; i < ARR_LONG_SZ && can_loop; i++) {
barrier_var(i);
arr_long[i] = i;
}
return 0;
}
SEC(".data.arr_foo")
struct {
int a;
int b;
} arr_foo[ARR_LONG_SZ];
SEC("socket")
__success
int test3(const void *ctx)
{
__u64 i;
for (i = zero; i < ARR_LONG_SZ && can_loop; i++) {
barrier_var(i);
arr_foo[i].a = i;
arr_foo[i].b = i;
}
return 0;
}
SEC("socket")
__success
int test4(const void *ctx)
{
long i;
for (i = zero + ARR_LONG_SZ - 1; i < ARR_LONG_SZ && i >= 0 && can_loop; i--) {
barrier_var(i);
arr_foo[i].a = i;
arr_foo[i].b = i;
}
return 0;
}
char buf[10] SEC(".data.buf");
SEC("socket")
__description("check add const")
__success
__naked void check_add_const(void)
{
/* typical LLVM generated loop with may_goto */
asm volatile (" \
call %[bpf_ktime_get_ns]; \
if r0 > 9 goto l1_%=; \
l0_%=: r1 = %[buf]; \
r2 = r0; \
r1 += r2; \
r3 = *(u8 *)(r1 +0); \
.byte 0xe5; /* may_goto */ \
.byte 0; /* regs */ \
.short 4; /* off of l1_%=: */ \
.long 0; /* imm */ \
r0 = r2; \
r0 += 1; \
if r2 < 9 goto l0_%=; \
exit; \
l1_%=: r0 = 0; \
exit; \
" :
: __imm(bpf_ktime_get_ns),
__imm_ptr(buf)
: __clobber_common);
}
SEC("socket")
__failure
__msg("*(u8 *)(r7 +0) = r0")
__msg("invalid access to map value, value_size=10 off=10 size=1")
__naked void check_add_const_3regs(void)
{
asm volatile (
"r6 = %[buf];"
"r7 = %[buf];"
"call %[bpf_ktime_get_ns];"
"r1 = r0;" /* link r0.id == r1.id == r2.id */
"r2 = r0;"
"r1 += 1;" /* r1 == r0+1 */
"r2 += 2;" /* r2 == r0+2 */
"if r0 > 8 goto 1f;" /* r0 range [0, 8] */
"r6 += r1;" /* r1 range [1, 9] */
"r7 += r2;" /* r2 range [2, 10] */
"*(u8 *)(r6 +0) = r0;" /* safe, within bounds */
"*(u8 *)(r7 +0) = r0;" /* unsafe, out of bounds */
"1: exit;"
:
: __imm(bpf_ktime_get_ns),
__imm_ptr(buf)
: __clobber_common);
}
SEC("socket")
__failure
__msg("*(u8 *)(r8 -1) = r0")
__msg("invalid access to map value, value_size=10 off=10 size=1")
__naked void check_add_const_3regs_2if(void)
{
asm volatile (
"r6 = %[buf];"
"r7 = %[buf];"
"r8 = %[buf];"
"call %[bpf_ktime_get_ns];"
"if r0 < 2 goto 1f;"
"r1 = r0;" /* link r0.id == r1.id == r2.id */
"r2 = r0;"
"r1 += 1;" /* r1 == r0+1 */
"r2 += 2;" /* r2 == r0+2 */
"if r2 > 11 goto 1f;" /* r2 range [0, 11] -> r0 range [-2, 9]; r1 range [-1, 10] */
"if r0 s< 0 goto 1f;" /* r0 range [0, 9] -> r1 range [1, 10]; r2 range [2, 11]; */
"r6 += r0;" /* r0 range [0, 9] */
"r7 += r1;" /* r1 range [1, 10] */
"r8 += r2;" /* r2 range [2, 11] */
"*(u8 *)(r6 +0) = r0;" /* safe, within bounds */
"*(u8 *)(r7 -1) = r0;" /* safe */
"*(u8 *)(r8 -1) = r0;" /* unsafe */
"1: exit;"
:
: __imm(bpf_ktime_get_ns),
__imm_ptr(buf)
: __clobber_common);
}
SEC("socket")
__failure
__flag(BPF_F_TEST_STATE_FREQ)
__naked void check_add_const_regsafe_off(void)
{
asm volatile (
"r8 = %[buf];"
"call %[bpf_ktime_get_ns];"
"r6 = r0;"
"call %[bpf_ktime_get_ns];"
"r7 = r0;"
"call %[bpf_ktime_get_ns];"
"r1 = r0;" /* same ids for r1 and r0 */
"if r6 > r7 goto 1f;" /* this jump can't be predicted */
"r1 += 1;" /* r1.off == +1 */
"goto 2f;"
"1: r1 += 100;" /* r1.off == +100 */
"goto +0;" /* verify r1.off in regsafe() after this insn */
"2: if r0 > 8 goto 3f;" /* r0 range [0,8], r1 range either [1,9] or [100,108]*/
"r8 += r1;"
"*(u8 *)(r8 +0) = r0;" /* potentially unsafe, buf size is 10 */
"3: exit;"
:
: __imm(bpf_ktime_get_ns),
__imm_ptr(buf)
: __clobber_common);
}
char _license[] SEC("license") = "GPL";