mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+soXsSLHKoYyzcli6rmadz2vbToFAmXvm7IACgkQ6rmadz2v bTqdMA//VMHNHVLb4oROoXyQD9fw2mCmIUEKzP88RXfqcxsfEX7HF+k8B5ZTk0ro CHXTAnc79+Qqg0j24bkQKxup/fKBQVw9D+Ia4b3ytlm1I2MtyU/16xNEzVhAPU2D iKk6mVBsEdCbt/GjpWORy/VVnZlZpC7BOpZLxsbbxgXOndnCegyjXzSnLGJGxdvi zkrQTn2SrFzLi6aNpVLqrv6Nks6HJusfCKsIrtlbkQ85dulasHOtwK9s6GF60nte aaho+MPx3L+lWEgapsm8rR779pHaYIB/GbZUgEPxE/xUJ/V8BzDgFNLMzEiIBRMN a0zZam11BkBzCfcO9gkvDRByaei/dZz2jdqfU4GlHklFj1WFfz8Q7fRLEPINksvj WXLgJADGY5mtGbjG21FScThxzj+Ruqwx0a13ddlyI/W+P3y5yzSWsLwJG5F9p0oU 6nlkJ4U8yg+9E1ie5ae0TibqvRJzXPjfOERZGwYDSVvfQGzv1z+DGSOPMmgNcWYM dIaO+A/+NS3zdbk8+1PP2SBbhHPk6kWyCUByWc7wMzCPTiwriFGY/DD2sN+Fsufo zorzfikUQOlTfzzD5jbmT49U8hUQUf6QIWsu7BijSiHaaC7am4S8QB2O6ibJMqdv yNiwvuX+ThgVIY3QKrLLqL0KPGeKMR5mtfq6rrwSpfp/b4g27FE= =eFgA -----END PGP SIGNATURE----- Merge tag 'for-netdev' of https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next Alexei Starovoitov says: ==================== pull-request: bpf-next 2024-03-11 We've added 59 non-merge commits during the last 9 day(s) which contain a total of 88 files changed, 4181 insertions(+), 590 deletions(-). The main changes are: 1) Enforce VM_IOREMAP flag and range in ioremap_page_range and introduce VM_SPARSE kind and vm_area_[un]map_pages to be used in bpf_arena, from Alexei. 2) Introduce bpf_arena which is sparse shared memory region between bpf program and user space where structures inside the arena can have pointers to other areas of the arena, and pointers work seamlessly for both user-space programs and bpf programs, from Alexei and Andrii. 3) Introduce may_goto instruction that is a contract between the verifier and the program. The verifier allows the program to loop assuming it's behaving well, but reserves the right to terminate it, from Alexei. 4) Use IETF format for field definitions in the BPF standard document, from Dave. 5) Extend struct_ops libbpf APIs to allow specify version suffixes for stuct_ops map types, share the same BPF program between several map definitions, and other improvements, from Eduard. 6) Enable struct_ops support for more than one page in trampolines, from Kui-Feng. 7) Support kCFI + BPF on riscv64, from Puranjay. 8) Use bpf_prog_pack for arm64 bpf trampoline, from Puranjay. 9) Fix roundup_pow_of_two undefined behavior on 32-bit archs, from Toke. ==================== Link: https://lore.kernel.org/r/20240312003646.8692-1-alexei.starovoitov@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
409 lines
7.8 KiB
C
409 lines
7.8 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
|
|
);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
#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; cond_break, 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; cond_break, 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; i++, cond_break)
|
|
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;
|
|
}
|
|
|
|
char _license[] SEC("license") = "GPL";
|