Daniel Borkmann says: ==================== pull-request: bpf 2024-06-24 We've added 12 non-merge commits during the last 10 day(s) which contain a total of 10 files changed, 412 insertions(+), 16 deletions(-). The main changes are: 1) Fix a BPF verifier issue validating may_goto with a negative offset, from Alexei Starovoitov. 2) Fix a BPF verifier validation bug with may_goto combined with jump to the first instruction, also from Alexei Starovoitov. 3) Fix a bug with overrunning reservations in BPF ring buffer, from Daniel Borkmann. 4) Fix a bug in BPF verifier due to missing proper var_off setting related to movsx instruction, from Yonghong Song. 5) Silence unnecessary syzkaller-triggered warning in __xdp_reg_mem_model(), from Daniil Dulov. * tag 'for-netdev' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/bpf/bpf: xdp: Remove WARN() from __xdp_reg_mem_model() selftests/bpf: Add tests for may_goto with negative offset. bpf: Fix may_goto with negative offset. selftests/bpf: Add more ring buffer test coverage bpf: Fix overrunning reservations in ringbuf selftests/bpf: Tests with may_goto and jumps to the 1st insn bpf: Fix the corner case with may_goto and jump to the 1st insn. bpf: Update BPF LSM maintainer list bpf: Fix remap of arena. selftests/bpf: Add a few tests to cover bpf: Add missed var_off setting in coerce_subreg_to_size_sx() bpf: Add missed var_off setting in set_sext32_default_val() ====================
Unified · Split
| @@ -4083,12 +4083,13 @@ F: kernel/bpf/ringbuf.c · 4082 unmodified lines | ||
| 4083 | 4083 | |
| 4084 | 4084 | BPF [SECURITY & LSM] (Security Audit and Enforcement using BPF) |
| 4085 | 4085 | M: KP Singh <kpsingh@kernel.org> |
| 4086 | −R: Matt Bobrowski <mattbobrowski@google.com> | |
| 4086 | +M: Matt Bobrowski <mattbobrowski@google.com> | |
| 4087 | 4087 | L: bpf@vger.kernel.org |
| 4088 | 4088 | S: Maintained |
| 4089 | 4089 | F: Documentation/bpf/prog_lsm.rst |
| 4090 | 4090 | F: include/linux/bpf_lsm.h |
| 4091 | 4091 | F: kernel/bpf/bpf_lsm.c |
| 4092 | +F: kernel/trace/bpf_trace.c | |
| 4092 | 4093 | F: security/bpf/ |
| 4093 | 4094 | |
| 4094 | 4095 | BPF [SELFTESTS] (Test Runners & Infrastructure) |
| @@ -212,6 +212,7 @@ static u64 arena_map_mem_usage(const struct bpf_map *map) · 211 unmodified lines | ||
| 212 | 212 | struct vma_list { |
| 213 | 213 | struct vm_area_struct *vma; |
| 214 | 214 | struct list_head head; |
| 215 | + atomic_t mmap_count; | |
| 215 | 216 | }; |
| 216 | 217 | |
| 217 | 218 | static int remember_vma(struct bpf_arena *arena, struct vm_area_struct *vma) |
| @@ -221,20 +222,30 @@ static int remember_vma(struct bpf_arena *arena, struct vm_area_struct *vma) · 3 unmodified lines | ||
| 221 | 222 | vml = kmalloc(sizeof(*vml), GFP_KERNEL); |
| 222 | 223 | if (!vml) |
| 223 | 224 | return -ENOMEM; |
| 225 | + atomic_set(&vml->mmap_count, 1); | |
| 224 | 226 | vma->vm_private_data = vml; |
| 225 | 227 | vml->vma = vma; |
| 226 | 228 | list_add(&vml->head, &arena->vma_list); |
| 227 | 229 | return 0; |
| 228 | 230 | } |
| 229 | 231 | |
| 232 | +static void arena_vm_open(struct vm_area_struct *vma) | |
| 233 | +{ | |
| 234 | + struct vma_list *vml = vma->vm_private_data; | |
| 235 | + | |
| 236 | + atomic_inc(&vml->mmap_count); | |
| 237 | +} | |
| 238 | + | |
| 230 | 239 | static void arena_vm_close(struct vm_area_struct *vma) |
| 231 | 240 | { |
| 232 | 241 | struct bpf_map *map = vma->vm_file->private_data; |
| 233 | 242 | struct bpf_arena *arena = container_of(map, struct bpf_arena, map); |
| 234 | − struct vma_list *vml; | |
| 243 | + struct vma_list *vml = vma->vm_private_data; | |
| 235 | 244 | |
| 245 | + if (!atomic_dec_and_test(&vml->mmap_count)) | |
| 246 | + return; | |
| 236 | 247 | guard(mutex)(&arena->lock); |
| 237 | − vml = vma->vm_private_data; | |
| 248 | + /* update link list under lock */ | |
| 238 | 249 | list_del(&vml->head); |
| 239 | 250 | vma->vm_private_data = NULL; |
| 240 | 251 | kfree(vml); |
| @@ -287,6 +298,7 @@ out: · 46 unmodified lines | ||
| 287 | 298 | } |
| 288 | 299 | |
| 289 | 300 | static const struct vm_operations_struct arena_vm_ops = { |
| 301 | + .open = arena_vm_open, | |
| 290 | 302 | .close = arena_vm_close, |
| 291 | 303 | .fault = arena_vm_fault, |
| 292 | 304 | }; |
| @@ -51,7 +51,8 @@ struct bpf_ringbuf { · 50 unmodified lines | ||
| 51 | 51 | * This prevents a user-space application from modifying the |
| 52 | 52 | * position and ruining in-kernel tracking. The permissions of the |
| 53 | 53 | * pages depend on who is producing samples: user-space or the |
| 54 | − * kernel. | |
| 54 | + * kernel. Note that the pending counter is placed in the same | |
| 55 | + * page as the producer, so that it shares the same cache line. | |
| 55 | 56 | * |
| 56 | 57 | * Kernel-producer |
| 57 | 58 | * --------------- |
| @@ -70,6 +71,7 @@ struct bpf_ringbuf { · 12 unmodified lines | ||
| 70 | 71 | */ |
| 71 | 72 | unsigned long consumer_pos __aligned(PAGE_SIZE); |
| 72 | 73 | unsigned long producer_pos __aligned(PAGE_SIZE); |
| 74 | + unsigned long pending_pos; | |
| 73 | 75 | char data[] __aligned(PAGE_SIZE); |
| 74 | 76 | }; |
| 75 | 77 | |
| @@ -179,6 +181,7 @@ static struct bpf_ringbuf *bpf_ringbuf_alloc(size_t data_sz, int numa_node) · 103 unmodified lines | ||
| 179 | 181 | rb->mask = data_sz - 1; |
| 180 | 182 | rb->consumer_pos = 0; |
| 181 | 183 | rb->producer_pos = 0; |
| 184 | + rb->pending_pos = 0; | |
| 182 | 185 | |
| 183 | 186 | return rb; |
| 184 | 187 | } |
| @@ -404,9 +407,9 @@ bpf_ringbuf_restore_from_rec(struct bpf_ringbuf_hdr *hdr) · 219 unmodified lines | ||
| 404 | 407 | |
| 405 | 408 | static void *__bpf_ringbuf_reserve(struct bpf_ringbuf *rb, u64 size) |
| 406 | 409 | { |
| 407 | − unsigned long cons_pos, prod_pos, new_prod_pos, flags; | |
| 408 | − u32 len, pg_off; | |
| 410 | + unsigned long cons_pos, prod_pos, new_prod_pos, pend_pos, flags; | |
| 409 | 411 | struct bpf_ringbuf_hdr *hdr; |
| 412 | + u32 len, pg_off, tmp_size, hdr_len; | |
| 410 | 413 | |
| 411 | 414 | if (unlikely(size > RINGBUF_MAX_RECORD_SZ)) |
| 412 | 415 | return NULL; |
| @@ -424,13 +427,29 @@ static void *__bpf_ringbuf_reserve(struct bpf_ringbuf *rb, u64 size) · 11 unmodified lines | ||
| 424 | 427 | spin_lock_irqsave(&rb->spinlock, flags); |
| 425 | 428 | } |
| 426 | 429 | |
| 430 | + pend_pos = rb->pending_pos; | |
| 427 | 431 | prod_pos = rb->producer_pos; |
| 428 | 432 | new_prod_pos = prod_pos + len; |
| 429 | 433 | |
| 430 | − /* check for out of ringbuf space by ensuring producer position | |
| 431 | − * doesn't advance more than (ringbuf_size - 1) ahead | |
| 434 | + while (pend_pos < prod_pos) { | |
| 435 | + hdr = (void *)rb->data + (pend_pos & rb->mask); | |
| 436 | + hdr_len = READ_ONCE(hdr->len); | |
| 437 | + if (hdr_len & BPF_RINGBUF_BUSY_BIT) | |
| 438 | + break; | |
| 439 | + tmp_size = hdr_len & ~BPF_RINGBUF_DISCARD_BIT; | |
| 440 | + tmp_size = round_up(tmp_size + BPF_RINGBUF_HDR_SZ, 8); | |
| 441 | + pend_pos += tmp_size; | |
| 442 | + } | |
| 443 | + rb->pending_pos = pend_pos; | |
| 444 | + | |
| 445 | + /* check for out of ringbuf space: | |
| 446 | + * - by ensuring producer position doesn't advance more than | |
| 447 | + * (ringbuf_size - 1) ahead | |
| 448 | + * - by ensuring oldest not yet committed record until newest | |
| 449 | + * record does not span more than (ringbuf_size - 1) | |
| 432 | 450 | */ |
| 433 | − if (new_prod_pos - cons_pos > rb->mask) { | |
| 451 | + if (new_prod_pos - cons_pos > rb->mask || | |
| 452 | + new_prod_pos - pend_pos > rb->mask) { | |
| 434 | 453 | spin_unlock_irqrestore(&rb->spinlock, flags); |
| 435 | 454 | return NULL; |
| 436 | 455 | } |
| @@ -6236,6 +6236,7 @@ static void set_sext32_default_val(struct bpf_reg_state *reg, int size) · 6235 unmodified lines | ||
| 6236 | 6236 | } |
| 6237 | 6237 | reg->u32_min_value = 0; |
| 6238 | 6238 | reg->u32_max_value = U32_MAX; |
| 6239 | + reg->var_off = tnum_subreg(tnum_unknown); | |
| 6239 | 6240 | } |
| 6240 | 6241 | |
| 6241 | 6242 | static void coerce_subreg_to_size_sx(struct bpf_reg_state *reg, int size) |
| @@ -6280,6 +6281,7 @@ static void coerce_subreg_to_size_sx(struct bpf_reg_state *reg, int size) · 38 unmodified lines | ||
| 6280 | 6281 | reg->s32_max_value = s32_max; |
| 6281 | 6282 | reg->u32_min_value = (u32)s32_min; |
| 6282 | 6283 | reg->u32_max_value = (u32)s32_max; |
| 6284 | + reg->var_off = tnum_subreg(tnum_range(s32_min, s32_max)); | |
| 6283 | 6285 | return; |
| 6284 | 6286 | } |
| 6285 | 6287 | |
| @@ -12719,6 +12721,16 @@ static bool signed_add32_overflows(s32 a, s32 b) · 6433 unmodified lines | ||
| 12719 | 12721 | return res < a; |
| 12720 | 12722 | } |
| 12721 | 12723 | |
| 12724 | +static bool signed_add16_overflows(s16 a, s16 b) | |
| 12725 | +{ | |
| 12726 | + /* Do the add in u16, where overflow is well-defined */ | |
| 12727 | + s16 res = (s16)((u16)a + (u16)b); | |
| 12728 | + | |
| 12729 | + if (b < 0) | |
| 12730 | + return res > a; | |
| 12731 | + return res < a; | |
| 12732 | +} | |
| 12733 | + | |
| 12722 | 12734 | static bool signed_sub_overflows(s64 a, s64 b) |
| 12723 | 12735 | { |
| 12724 | 12736 | /* Do the sub in u64, where overflow is well-defined */ |
| @@ -17448,11 +17460,11 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) · 4723 unmodified lines | ||
| 17448 | 17460 | goto skip_inf_loop_check; |
| 17449 | 17461 | } |
| 17450 | 17462 | if (is_may_goto_insn_at(env, insn_idx)) { |
| 17451 | − if (states_equal(env, &sl->state, cur, RANGE_WITHIN)) { | |
| 17463 | + if (sl->state.may_goto_depth != cur->may_goto_depth && | |
| 17464 | + states_equal(env, &sl->state, cur, RANGE_WITHIN)) { | |
| 17452 | 17465 | update_loop_entry(cur, &sl->state); |
| 17453 | 17466 | goto hit; |
| 17454 | 17467 | } |
| 17455 | − goto skip_inf_loop_check; | |
| 17456 | 17468 | } |
| 17457 | 17469 | if (calls_callback(env, insn_idx)) { |
| 17458 | 17470 | if (states_equal(env, &sl->state, cur, RANGE_WITHIN)) |
| @@ -18730,6 +18742,39 @@ static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 of · 1271 unmodified lines | ||
| 18730 | 18742 | return new_prog; |
| 18731 | 18743 | } |
| 18732 | 18744 | |
| 18745 | +/* | |
| 18746 | + * For all jmp insns in a given 'prog' that point to 'tgt_idx' insn adjust the | |
| 18747 | + * jump offset by 'delta'. | |
| 18748 | + */ | |
| 18749 | +static int adjust_jmp_off(struct bpf_prog *prog, u32 tgt_idx, u32 delta) | |
| 18750 | +{ | |
| 18751 | + struct bpf_insn *insn = prog->insnsi; | |
| 18752 | + u32 insn_cnt = prog->len, i; | |
| 18753 | + | |
| 18754 | + for (i = 0; i < insn_cnt; i++, insn++) { | |
| 18755 | + u8 code = insn->code; | |
| 18756 | + | |
| 18757 | + if ((BPF_CLASS(code) != BPF_JMP && BPF_CLASS(code) != BPF_JMP32) || | |
| 18758 | + BPF_OP(code) == BPF_CALL || BPF_OP(code) == BPF_EXIT) | |
| 18759 | + continue; | |
| 18760 | + | |
| 18761 | + if (insn->code == (BPF_JMP32 | BPF_JA)) { | |
| 18762 | + if (i + 1 + insn->imm != tgt_idx) | |
| 18763 | + continue; | |
| 18764 | + if (signed_add32_overflows(insn->imm, delta)) | |
| 18765 | + return -ERANGE; | |
| 18766 | + insn->imm += delta; | |
| 18767 | + } else { | |
| 18768 | + if (i + 1 + insn->off != tgt_idx) | |
| 18769 | + continue; | |
| 18770 | + if (signed_add16_overflows(insn->imm, delta)) | |
| 18771 | + return -ERANGE; | |
| 18772 | + insn->off += delta; | |
| 18773 | + } | |
| 18774 | + } | |
| 18775 | + return 0; | |
| 18776 | +} | |
| 18777 | + | |
| 18733 | 18778 | static int adjust_subprog_starts_after_remove(struct bpf_verifier_env *env, |
| 18734 | 18779 | u32 off, u32 cnt) |
| 18735 | 18780 | { |
| @@ -20004,7 +20049,10 @@ static int do_misc_fixups(struct bpf_verifier_env *env) · 1268 unmodified lines | ||
| 20004 | 20049 | |
| 20005 | 20050 | stack_depth_extra = 8; |
| 20006 | 20051 | insn_buf[0] = BPF_LDX_MEM(BPF_DW, BPF_REG_AX, BPF_REG_10, stack_off); |
| 20007 | − insn_buf[1] = BPF_JMP_IMM(BPF_JEQ, BPF_REG_AX, 0, insn->off + 2); | |
| 20052 | + if (insn->off >= 0) | |
| 20053 | + insn_buf[1] = BPF_JMP_IMM(BPF_JEQ, BPF_REG_AX, 0, insn->off + 2); | |
| 20054 | + else | |
| 20055 | + insn_buf[1] = BPF_JMP_IMM(BPF_JEQ, BPF_REG_AX, 0, insn->off - 1); | |
| 20008 | 20056 | insn_buf[2] = BPF_ALU64_IMM(BPF_SUB, BPF_REG_AX, 1); |
| 20009 | 20057 | insn_buf[3] = BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_AX, stack_off); |
| 20010 | 20058 | cnt = 4; |
| @@ -20546,6 +20594,13 @@ next_insn: · 535 unmodified lines | ||
| 20546 | 20594 | if (!new_prog) |
| 20547 | 20595 | return -ENOMEM; |
| 20548 | 20596 | env->prog = prog = new_prog; |
| 20597 | + /* | |
| 20598 | + * If may_goto is a first insn of a prog there could be a jmp | |
| 20599 | + * insn that points to it, hence adjust all such jmps to point | |
| 20600 | + * to insn after BPF_ST that inits may_goto count. | |
| 20601 | + * Adjustment will succeed because bpf_patch_insn_data() didn't fail. | |
| 20602 | + */ | |
| 20603 | + WARN_ON(adjust_jmp_off(env->prog, subprog_start, 1)); | |
| 20549 | 20604 | } |
| 20550 | 20605 | |
| 20551 | 20606 | /* Since poke tab is now finalized, publish aux to tracker. */ |
| @@ -295,10 +295,8 @@ static struct xdp_mem_allocator *__xdp_reg_mem_model(struct xdp_mem_info *mem, · 294 unmodified lines | ||
| 295 | 295 | mutex_lock(&mem_id_lock); |
| 296 | 296 | ret = __mem_id_init_hash_table(); |
| 297 | 297 | mutex_unlock(&mem_id_lock); |
| 298 | − if (ret < 0) { | |
| 299 | − WARN_ON(1); | |
| 298 | + if (ret < 0) | |
| 300 | 299 | return ERR_PTR(ret); |
| 301 | − } | |
| 302 | 300 | } |
| 303 | 301 | |
| 304 | 302 | xdp_alloc = kzalloc(sizeof(*xdp_alloc), gfp); |
| @@ -457,7 +457,7 @@ LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h \ · 456 unmodified lines | ||
| 457 | 457 | LSKELS := fentry_test.c fexit_test.c fexit_sleep.c atomics.c \ |
| 458 | 458 | trace_printk.c trace_vprintk.c map_ptr_kern.c \ |
| 459 | 459 | core_kern.c core_kern_overflow.c test_ringbuf.c \ |
| 460 | − test_ringbuf_n.c test_ringbuf_map_key.c | |
| 460 | + test_ringbuf_n.c test_ringbuf_map_key.c test_ringbuf_write.c | |
| 461 | 461 | |
| 462 | 462 | # Generate both light skeleton and libbpf skeleton for these |
| 463 | 463 | LSKELS_EXTRA := test_ksyms_module.c test_ksyms_weak.c kfunc_call_test.c \ |
| @@ -12,9 +12,11 @@ · 11 unmodified lines | ||
| 12 | 12 | #include <sys/sysinfo.h> |
| 13 | 13 | #include <linux/perf_event.h> |
| 14 | 14 | #include <linux/ring_buffer.h> |
| 15 | + | |
| 15 | 16 | #include "test_ringbuf.lskel.h" |
| 16 | 17 | #include "test_ringbuf_n.lskel.h" |
| 17 | 18 | #include "test_ringbuf_map_key.lskel.h" |
| 19 | +#include "test_ringbuf_write.lskel.h" | |
| 18 | 20 | |
| 19 | 21 | #define EDONE 7777 |
| 20 | 22 | |
| @@ -84,6 +86,58 @@ static void *poll_thread(void *input) · 63 unmodified lines | ||
| 84 | 86 | return (void *)(long)ring_buffer__poll(ringbuf, timeout); |
| 85 | 87 | } |
| 86 | 88 | |
| 89 | +static void ringbuf_write_subtest(void) | |
| 90 | +{ | |
| 91 | + struct test_ringbuf_write_lskel *skel; | |
| 92 | + int page_size = getpagesize(); | |
| 93 | + size_t *mmap_ptr; | |
| 94 | + int err, rb_fd; | |
| 95 | + | |
| 96 | + skel = test_ringbuf_write_lskel__open(); | |
| 97 | + if (!ASSERT_OK_PTR(skel, "skel_open")) | |
| 98 | + return; | |
| 99 | + | |
| 100 | + skel->maps.ringbuf.max_entries = 0x4000; | |
| 101 | + | |
| 102 | + err = test_ringbuf_write_lskel__load(skel); | |
| 103 | + if (!ASSERT_OK(err, "skel_load")) | |
| 104 | + goto cleanup; | |
| 105 | + | |
| 106 | + rb_fd = skel->maps.ringbuf.map_fd; | |
| 107 | + | |
| 108 | + mmap_ptr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, rb_fd, 0); | |
| 109 | + if (!ASSERT_OK_PTR(mmap_ptr, "rw_cons_pos")) | |
| 110 | + goto cleanup; | |
| 111 | + *mmap_ptr = 0x3000; | |
| 112 | + ASSERT_OK(munmap(mmap_ptr, page_size), "unmap_rw"); | |
| 113 | + | |
| 114 | + skel->bss->pid = getpid(); | |
| 115 | + | |
| 116 | + ringbuf = ring_buffer__new(rb_fd, process_sample, NULL, NULL); | |
| 117 | + if (!ASSERT_OK_PTR(ringbuf, "ringbuf_new")) | |
| 118 | + goto cleanup; | |
| 119 | + | |
| 120 | + err = test_ringbuf_write_lskel__attach(skel); | |
| 121 | + if (!ASSERT_OK(err, "skel_attach")) | |
| 122 | + goto cleanup_ringbuf; | |
| 123 | + | |
| 124 | + skel->bss->discarded = 0; | |
| 125 | + skel->bss->passed = 0; | |
| 126 | + | |
| 127 | + /* trigger exactly two samples */ | |
| 128 | + syscall(__NR_getpgid); | |
| 129 | + syscall(__NR_getpgid); | |
| 130 | + | |
| 131 | + ASSERT_EQ(skel->bss->discarded, 2, "discarded"); | |
| 132 | + ASSERT_EQ(skel->bss->passed, 0, "passed"); | |
| 133 | + | |
| 134 | + test_ringbuf_write_lskel__detach(skel); | |
| 135 | +cleanup_ringbuf: | |
| 136 | + ring_buffer__free(ringbuf); | |
| 137 | +cleanup: | |
| 138 | + test_ringbuf_write_lskel__destroy(skel); | |
| 139 | +} | |
| 140 | + | |
| 87 | 141 | static void ringbuf_subtest(void) |
| 88 | 142 | { |
| 89 | 143 | const size_t rec_sz = BPF_RINGBUF_HDR_SZ + sizeof(struct sample); |
| @@ -451,4 +505,6 @@ void test_ringbuf(void) · 361 unmodified lines | ||
| 451 | 505 | ringbuf_n_subtest(); |
| 452 | 506 | if (test__start_subtest("ringbuf_map_key")) |
| 453 | 507 | ringbuf_map_key_subtest(); |
| 508 | + if (test__start_subtest("ringbuf_write")) | |
| 509 | + ringbuf_write_subtest(); | |
| 454 | 510 | } |
| @@ -0,0 +1,46 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +#include <linux/bpf.h> | |
| 4 | +#include <bpf/bpf_helpers.h> | |
| 5 | +#include "bpf_misc.h" | |
| 6 | + | |
| 7 | +char _license[] SEC("license") = "GPL"; | |
| 8 | + | |
| 9 | +struct { | |
| 10 | + __uint(type, BPF_MAP_TYPE_RINGBUF); | |
| 11 | +} ringbuf SEC(".maps"); | |
| 12 | + | |
| 13 | +/* inputs */ | |
| 14 | +int pid = 0; | |
| 15 | + | |
| 16 | +/* outputs */ | |
| 17 | +long passed = 0; | |
| 18 | +long discarded = 0; | |
| 19 | + | |
| 20 | +SEC("fentry/" SYS_PREFIX "sys_getpgid") | |
| 21 | +int test_ringbuf_write(void *ctx) | |
| 22 | +{ | |
| 23 | + int *foo, cur_pid = bpf_get_current_pid_tgid() >> 32; | |
| 24 | + void *sample1, *sample2; | |
| 25 | + | |
| 26 | + if (cur_pid != pid) | |
| 27 | + return 0; | |
| 28 | + | |
| 29 | + sample1 = bpf_ringbuf_reserve(&ringbuf, 0x3000, 0); | |
| 30 | + if (!sample1) | |
| 31 | + return 0; | |
| 32 | + /* first one can pass */ | |
| 33 | + sample2 = bpf_ringbuf_reserve(&ringbuf, 0x3000, 0); | |
| 34 | + if (!sample2) { | |
| 35 | + bpf_ringbuf_discard(sample1, 0); | |
| 36 | + __sync_fetch_and_add(&discarded, 1); | |
| 37 | + return 0; | |
| 38 | + } | |
| 39 | + /* second one must not */ | |
| 40 | + __sync_fetch_and_add(&passed, 1); | |
| 41 | + foo = sample2 + 4084; | |
| 42 | + *foo = 256; | |
| 43 | + bpf_ringbuf_discard(sample1, 0); | |
| 44 | + bpf_ringbuf_discard(sample2, 0); | |
| 45 | + return 0; | |
| 46 | +} | |
| @@ -274,6 +274,58 @@ static __naked void iter_limit_bug_cb(void) · 273 unmodified lines | ||
| 274 | 274 | ); |
| 275 | 275 | } |
| 276 | 276 | |
| 277 | +int tmp_var; | |
| 278 | +SEC("socket") | |
| 279 | +__failure __msg("infinite loop detected at insn 2") | |
| 280 | +__naked void jgt_imm64_and_may_goto(void) | |
| 281 | +{ | |
| 282 | + asm volatile (" \ | |
| 283 | + r0 = %[tmp_var] ll; \ | |
| 284 | +l0_%=: .byte 0xe5; /* may_goto */ \ | |
| 285 | + .byte 0; /* regs */ \ | |
| 286 | + .short -3; /* off -3 */ \ | |
| 287 | + .long 0; /* imm */ \ | |
| 288 | + if r0 > 10 goto l0_%=; \ | |
| 289 | + r0 = 0; \ | |
| 290 | + exit; \ | |
| 291 | +" :: __imm_addr(tmp_var) | |
| 292 | + : __clobber_all); | |
| 293 | +} | |
| 294 | + | |
| 295 | +SEC("socket") | |
| 296 | +__failure __msg("infinite loop detected at insn 1") | |
| 297 | +__naked void may_goto_self(void) | |
| 298 | +{ | |
| 299 | + asm volatile (" \ | |
| 300 | + r0 = *(u32 *)(r10 - 4); \ | |
| 301 | +l0_%=: .byte 0xe5; /* may_goto */ \ | |
| 302 | + .byte 0; /* regs */ \ | |
| 303 | + .short -1; /* off -1 */ \ | |
| 304 | + .long 0; /* imm */ \ | |
| 305 | + if r0 > 10 goto l0_%=; \ | |
| 306 | + r0 = 0; \ | |
| 307 | + exit; \ | |
| 308 | +" ::: __clobber_all); | |
| 309 | +} | |
| 310 | + | |
| 311 | +SEC("socket") | |
| 312 | +__success __retval(0) | |
| 313 | +__naked void may_goto_neg_off(void) | |
| 314 | +{ | |
| 315 | + asm volatile (" \ | |
| 316 | + r0 = *(u32 *)(r10 - 4); \ | |
| 317 | + goto l0_%=; \ | |
| 318 | + goto l1_%=; \ | |
| 319 | +l0_%=: .byte 0xe5; /* may_goto */ \ | |
| 320 | + .byte 0; /* regs */ \ | |
| 321 | + .short -2; /* off -2 */ \ | |
| 322 | + .long 0; /* imm */ \ | |
| 323 | + if r0 > 10 goto l0_%=; \ | |
| 324 | +l1_%=: r0 = 0; \ | |
| 325 | + exit; \ | |
| 326 | +" ::: __clobber_all); | |
| 327 | +} | |
| 328 | + | |
| 277 | 329 | SEC("tc") |
| 278 | 330 | __failure |
| 279 | 331 | __flag(BPF_F_TEST_STATE_FREQ) |
| @@ -307,6 +359,100 @@ int iter_limit_bug(struct __sk_buff *skb) · 27 unmodified lines | ||
| 307 | 359 | return 0; |
| 308 | 360 | } |
| 309 | 361 | |
| 362 | +SEC("socket") | |
| 363 | +__success __retval(0) | |
| 364 | +__naked void ja_and_may_goto(void) | |
| 365 | +{ | |
| 366 | + asm volatile (" \ | |
| 367 | +l0_%=: .byte 0xe5; /* may_goto */ \ | |
| 368 | + .byte 0; /* regs */ \ | |
| 369 | + .short 1; /* off 1 */ \ | |
| 370 | + .long 0; /* imm */ \ | |
| 371 | + goto l0_%=; \ | |
| 372 | + r0 = 0; \ | |
| 373 | + exit; \ | |
| 374 | +" ::: __clobber_common); | |
| 375 | +} | |
| 376 | + | |
| 377 | +SEC("socket") | |
| 378 | +__success __retval(0) | |
| 379 | +__naked void ja_and_may_goto2(void) | |
| 380 | +{ | |
| 381 | + asm volatile (" \ | |
| 382 | +l0_%=: r0 = 0; \ | |
| 383 | + .byte 0xe5; /* may_goto */ \ | |
| 384 | + .byte 0; /* regs */ \ | |
| 385 | + .short 1; /* off 1 */ \ | |
| 386 | + .long 0; /* imm */ \ | |
| 387 | + goto l0_%=; \ | |
| 388 | + r0 = 0; \ | |
| 389 | + exit; \ | |
| 390 | +" ::: __clobber_common); | |
| 391 | +} | |
| 392 | + | |
| 393 | +SEC("socket") | |
| 394 | +__success __retval(0) | |
| 395 | +__naked void jlt_and_may_goto(void) | |
| 396 | +{ | |
| 397 | + asm volatile (" \ | |
| 398 | +l0_%=: call %[bpf_jiffies64]; \ | |
| 399 | + .byte 0xe5; /* may_goto */ \ | |
| 400 | + .byte 0; /* regs */ \ | |
| 401 | + .short 1; /* off 1 */ \ | |
| 402 | + .long 0; /* imm */ \ | |
| 403 | + if r0 < 10 goto l0_%=; \ | |
| 404 | + r0 = 0; \ | |
| 405 | + exit; \ | |
| 406 | +" :: __imm(bpf_jiffies64) | |
| 407 | + : __clobber_all); | |
| 408 | +} | |
| 409 | + | |
| 410 | +#if (defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86) || \ | |
| 411 | + (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64) || \ | |
| 412 | + defined(__TARGET_ARCH_arm) || defined(__TARGET_ARCH_s390) || \ | |
| 413 | + defined(__TARGET_ARCH_loongarch)) && \ | |
| 414 | + __clang_major__ >= 18 | |
| 415 | +SEC("socket") | |
| 416 | +__success __retval(0) | |
| 417 | +__naked void gotol_and_may_goto(void) | |
| 418 | +{ | |
| 419 | + asm volatile (" \ | |
| 420 | +l0_%=: r0 = 0; \ | |
| 421 | + .byte 0xe5; /* may_goto */ \ | |
| 422 | + .byte 0; /* regs */ \ | |
| 423 | + .short 1; /* off 1 */ \ | |
| 424 | + .long 0; /* imm */ \ | |
| 425 | + gotol l0_%=; \ | |
| 426 | + r0 = 0; \ | |
| 427 | + exit; \ | |
| 428 | +" ::: __clobber_common); | |
| 429 | +} | |
| 430 | +#endif | |
| 431 | + | |
| 432 | +SEC("socket") | |
| 433 | +__success __retval(0) | |
| 434 | +__naked void ja_and_may_goto_subprog(void) | |
| 435 | +{ | |
| 436 | + asm volatile (" \ | |
| 437 | + call subprog_with_may_goto; \ | |
| 438 | + exit; \ | |
| 439 | +" ::: __clobber_all); | |
| 440 | +} | |
| 441 | + | |
| 442 | +static __naked __noinline __used | |
| 443 | +void subprog_with_may_goto(void) | |
| 444 | +{ | |
| 445 | + asm volatile (" \ | |
| 446 | +l0_%=: .byte 0xe5; /* may_goto */ \ | |
| 447 | + .byte 0; /* regs */ \ | |
| 448 | + .short 1; /* off 1 */ \ | |
| 449 | + .long 0; /* imm */ \ | |
| 450 | + goto l0_%=; \ | |
| 451 | + r0 = 0; \ | |
| 452 | + exit; \ | |
| 453 | +" ::: __clobber_all); | |
| 454 | +} | |
| 455 | + | |
| 310 | 456 | #define ARR_SZ 1000000 |
| 311 | 457 | int zero; |
| 312 | 458 | char arr[ARR_SZ]; |
| @@ -224,6 +224,69 @@ l0_%=: \ · 223 unmodified lines | ||
| 224 | 224 | : __clobber_all); |
| 225 | 225 | } |
| 226 | 226 | |
| 227 | +SEC("socket") | |
| 228 | +__description("MOV32SX, S8, var_off u32_max") | |
| 229 | +__failure __msg("infinite loop detected") | |
| 230 | +__failure_unpriv __msg_unpriv("back-edge from insn 2 to 0") | |
| 231 | +__naked void mov64sx_s32_varoff_1(void) | |
| 232 | +{ | |
| 233 | + asm volatile (" \ | |
| 234 | +l0_%=: \ | |
| 235 | + r3 = *(u8 *)(r10 -387); \ | |
| 236 | + w7 = (s8)w3; \ | |
| 237 | + if w7 >= 0x2533823b goto l0_%=; \ | |
| 238 | + w0 = 0; \ | |
| 239 | + exit; \ | |
| 240 | +" : | |
| 241 | + : | |
| 242 | + : __clobber_all); | |
| 243 | +} | |
| 244 | + | |
| 245 | +SEC("socket") | |
| 246 | +__description("MOV32SX, S8, var_off not u32_max, positive after s8 extension") | |
| 247 | +__success __retval(0) | |
| 248 | +__failure_unpriv __msg_unpriv("frame pointer is read only") | |
| 249 | +__naked void mov64sx_s32_varoff_2(void) | |
| 250 | +{ | |
| 251 | + asm volatile (" \ | |
| 252 | + call %[bpf_get_prandom_u32]; \ | |
| 253 | + r3 = r0; \ | |
| 254 | + r3 &= 0xf; \ | |
| 255 | + w7 = (s8)w3; \ | |
| 256 | + if w7 s>= 16 goto l0_%=; \ | |
| 257 | + w0 = 0; \ | |
| 258 | + exit; \ | |
| 259 | +l0_%=: \ | |
| 260 | + r10 = 1; \ | |
| 261 | + exit; \ | |
| 262 | +" : | |
| 263 | + : __imm(bpf_get_prandom_u32) | |
| 264 | + : __clobber_all); | |
| 265 | +} | |
| 266 | + | |
| 267 | +SEC("socket") | |
| 268 | +__description("MOV32SX, S8, var_off not u32_max, negative after s8 extension") | |
| 269 | +__success __retval(0) | |
| 270 | +__failure_unpriv __msg_unpriv("frame pointer is read only") | |
| 271 | +__naked void mov64sx_s32_varoff_3(void) | |
| 272 | +{ | |
| 273 | + asm volatile (" \ | |
| 274 | + call %[bpf_get_prandom_u32]; \ | |
| 275 | + r3 = r0; \ | |
| 276 | + r3 &= 0xf; \ | |
| 277 | + r3 |= 0x80; \ | |
| 278 | + w7 = (s8)w3; \ | |
| 279 | + if w7 s>= -5 goto l0_%=; \ | |
| 280 | + w0 = 0; \ | |
| 281 | + exit; \ | |
| 282 | +l0_%=: \ | |
| 283 | + r10 = 1; \ | |
| 284 | + exit; \ | |
| 285 | +" : | |
| 286 | + : __imm(bpf_get_prandom_u32) | |
| 287 | + : __clobber_all); | |
| 288 | +} | |
| 289 | + | |
| 227 | 290 | #else |
| 228 | 291 | |
| 229 | 292 | SEC("socket") |