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 | BPF [SECURITY & LSM] (Security Audit and Enforcement using BPF) | 4084 | BPF [SECURITY & LSM] (Security Audit and Enforcement using BPF) |
| 4085 | M: KP Singh <kpsingh@kernel.org> | 4085 | M: KP Singh <kpsingh@kernel.org> |
| 4086 | R: Matt Bobrowski <mattbobrowski@google.com> | 4086 | M: Matt Bobrowski <mattbobrowski@google.com> |
| 4087 | L: bpf@vger.kernel.org | 4087 | L: bpf@vger.kernel.org |
| 4088 | S: Maintained | 4088 | S: Maintained |
| 4089 | F: Documentation/bpf/prog_lsm.rst | 4089 | F: Documentation/bpf/prog_lsm.rst |
| 4090 | F: include/linux/bpf_lsm.h | 4090 | F: include/linux/bpf_lsm.h |
| 4091 | F: kernel/bpf/bpf_lsm.c | 4091 | F: kernel/bpf/bpf_lsm.c |
| 4092 | F: kernel/trace/bpf_trace.c | ||
| 4092 | F: security/bpf/ | 4093 | F: security/bpf/ |
| 4093 | 4094 | ||
| 4094 | BPF [SELFTESTS] (Test Runners & Infrastructure) | 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 | struct vma_list { | 212 | struct vma_list { |
| 213 | struct vm_area_struct *vma; | 213 | struct vm_area_struct *vma; |
| 214 | struct list_head head; | 214 | struct list_head head; |
| 215 | atomic_t mmap_count; | ||
| 215 | }; | 216 | }; |
| 216 | 217 | ||
| 217 | static int remember_vma(struct bpf_arena *arena, struct vm_area_struct *vma) | 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 | vml = kmalloc(sizeof(*vml), GFP_KERNEL); | 222 | vml = kmalloc(sizeof(*vml), GFP_KERNEL); |
| 222 | if (!vml) | 223 | if (!vml) |
| 223 | return -ENOMEM; | 224 | return -ENOMEM; |
| 225 | atomic_set(&vml->mmap_count, 1); | ||
| 224 | vma->vm_private_data = vml; | 226 | vma->vm_private_data = vml; |
| 225 | vml->vma = vma; | 227 | vml->vma = vma; |
| 226 | list_add(&vml->head, &arena->vma_list); | 228 | list_add(&vml->head, &arena->vma_list); |
| 227 | return 0; | 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 | static void arena_vm_close(struct vm_area_struct *vma) | 239 | static void arena_vm_close(struct vm_area_struct *vma) |
| 231 | { | 240 | { |
| 232 | struct bpf_map *map = vma->vm_file->private_data; | 241 | struct bpf_map *map = vma->vm_file->private_data; |
| 233 | struct bpf_arena *arena = container_of(map, struct bpf_arena, map); | 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 | guard(mutex)(&arena->lock); | 247 | guard(mutex)(&arena->lock); |
| 237 | vml = vma->vm_private_data; | 248 | /* update link list under lock */ |
| 238 | list_del(&vml->head); | 249 | list_del(&vml->head); |
| 239 | vma->vm_private_data = NULL; | 250 | vma->vm_private_data = NULL; |
| 240 | kfree(vml); | 251 | kfree(vml); |
| @@ -287,6 +298,7 @@ out: · 46 unmodified lines | |||
| 287 | } | 298 | } |
| 288 | 299 | ||
| 289 | static const struct vm_operations_struct arena_vm_ops = { | 300 | static const struct vm_operations_struct arena_vm_ops = { |
| 301 | .open = arena_vm_open, | ||
| 290 | .close = arena_vm_close, | 302 | .close = arena_vm_close, |
| 291 | .fault = arena_vm_fault, | 303 | .fault = arena_vm_fault, |
| 292 | }; | 304 | }; |
| @@ -51,7 +51,8 @@ struct bpf_ringbuf { · 50 unmodified lines | |||
| 51 | * This prevents a user-space application from modifying the | 51 | * This prevents a user-space application from modifying the |
| 52 | * position and ruining in-kernel tracking. The permissions of the | 52 | * position and ruining in-kernel tracking. The permissions of the |
| 53 | * pages depend on who is producing samples: user-space or the | 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 | * Kernel-producer | 57 | * Kernel-producer |
| 57 | * --------------- | 58 | * --------------- |
| @@ -70,6 +71,7 @@ struct bpf_ringbuf { · 12 unmodified lines | |||
| 70 | */ | 71 | */ |
| 71 | unsigned long consumer_pos __aligned(PAGE_SIZE); | 72 | unsigned long consumer_pos __aligned(PAGE_SIZE); |
| 72 | unsigned long producer_pos __aligned(PAGE_SIZE); | 73 | unsigned long producer_pos __aligned(PAGE_SIZE); |
| 74 | unsigned long pending_pos; | ||
| 73 | char data[] __aligned(PAGE_SIZE); | 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 | rb->mask = data_sz - 1; | 181 | rb->mask = data_sz - 1; |
| 180 | rb->consumer_pos = 0; | 182 | rb->consumer_pos = 0; |
| 181 | rb->producer_pos = 0; | 183 | rb->producer_pos = 0; |
| 184 | rb->pending_pos = 0; | ||
| 182 | 185 | ||
| 183 | return rb; | 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 | static void *__bpf_ringbuf_reserve(struct bpf_ringbuf *rb, u64 size) | 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; | 410 | unsigned long cons_pos, prod_pos, new_prod_pos, pend_pos, flags; |
| 408 | u32 len, pg_off; | ||
| 409 | struct bpf_ringbuf_hdr *hdr; | 411 | struct bpf_ringbuf_hdr *hdr; |
| 412 | u32 len, pg_off, tmp_size, hdr_len; | ||
| 410 | 413 | ||
| 411 | if (unlikely(size > RINGBUF_MAX_RECORD_SZ)) | 414 | if (unlikely(size > RINGBUF_MAX_RECORD_SZ)) |
| 412 | return NULL; | 415 | return NULL; |
| @@ -424,13 +427,29 @@ static void *__bpf_ringbuf_reserve(struct bpf_ringbuf *rb, u64 size) · 11 unmodified lines | |||
| 424 | spin_lock_irqsave(&rb->spinlock, flags); | 427 | spin_lock_irqsave(&rb->spinlock, flags); |
| 425 | } | 428 | } |
| 426 | 429 | ||
| 430 | pend_pos = rb->pending_pos; | ||
| 427 | prod_pos = rb->producer_pos; | 431 | prod_pos = rb->producer_pos; |
| 428 | new_prod_pos = prod_pos + len; | 432 | new_prod_pos = prod_pos + len; |
| 429 | 433 | ||
| 430 | /* check for out of ringbuf space by ensuring producer position | 434 | while (pend_pos < prod_pos) { |
| 431 | * doesn't advance more than (ringbuf_size - 1) ahead | 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 | spin_unlock_irqrestore(&rb->spinlock, flags); | 453 | spin_unlock_irqrestore(&rb->spinlock, flags); |
| 435 | return NULL; | 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 | reg->u32_min_value = 0; | 6237 | reg->u32_min_value = 0; |
| 6238 | reg->u32_max_value = U32_MAX; | 6238 | reg->u32_max_value = U32_MAX; |
| 6239 | reg->var_off = tnum_subreg(tnum_unknown); | ||
| 6239 | } | 6240 | } |
| 6240 | 6241 | ||
| 6241 | static void coerce_subreg_to_size_sx(struct bpf_reg_state *reg, int size) | 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 | reg->s32_max_value = s32_max; | 6281 | reg->s32_max_value = s32_max; |
| 6281 | reg->u32_min_value = (u32)s32_min; | 6282 | reg->u32_min_value = (u32)s32_min; |
| 6282 | reg->u32_max_value = (u32)s32_max; | 6283 | reg->u32_max_value = (u32)s32_max; |
| 6284 | reg->var_off = tnum_subreg(tnum_range(s32_min, s32_max)); | ||
| 6283 | return; | 6285 | return; |
| 6284 | } | 6286 | } |
| 6285 | 6287 | ||
| @@ -12719,6 +12721,16 @@ static bool signed_add32_overflows(s32 a, s32 b) · 6433 unmodified lines | |||
| 12719 | return res < a; | 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 | static bool signed_sub_overflows(s64 a, s64 b) | 12734 | static bool signed_sub_overflows(s64 a, s64 b) |
| 12723 | { | 12735 | { |
| 12724 | /* Do the sub in u64, where overflow is well-defined */ | 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 | goto skip_inf_loop_check; | 17460 | goto skip_inf_loop_check; |
| 17449 | } | 17461 | } |
| 17450 | if (is_may_goto_insn_at(env, insn_idx)) { | 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 | update_loop_entry(cur, &sl->state); | 17465 | update_loop_entry(cur, &sl->state); |
| 17453 | goto hit; | 17466 | goto hit; |
| 17454 | } | 17467 | } |
| 17455 | goto skip_inf_loop_check; | ||
| 17456 | } | 17468 | } |
| 17457 | if (calls_callback(env, insn_idx)) { | 17469 | if (calls_callback(env, insn_idx)) { |
| 17458 | if (states_equal(env, &sl->state, cur, RANGE_WITHIN)) | 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 | return new_prog; | 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 | static int adjust_subprog_starts_after_remove(struct bpf_verifier_env *env, | 18778 | static int adjust_subprog_starts_after_remove(struct bpf_verifier_env *env, |
| 18734 | u32 off, u32 cnt) | 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 | stack_depth_extra = 8; | 20050 | stack_depth_extra = 8; |
| 20006 | insn_buf[0] = BPF_LDX_MEM(BPF_DW, BPF_REG_AX, BPF_REG_10, stack_off); | 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 | insn_buf[2] = BPF_ALU64_IMM(BPF_SUB, BPF_REG_AX, 1); | 20056 | insn_buf[2] = BPF_ALU64_IMM(BPF_SUB, BPF_REG_AX, 1); |
| 20009 | insn_buf[3] = BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_AX, stack_off); | 20057 | insn_buf[3] = BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_AX, stack_off); |
| 20010 | cnt = 4; | 20058 | cnt = 4; |
| @@ -20546,6 +20594,13 @@ next_insn: · 535 unmodified lines | |||
| 20546 | if (!new_prog) | 20594 | if (!new_prog) |
| 20547 | return -ENOMEM; | 20595 | return -ENOMEM; |
| 20548 | env->prog = prog = new_prog; | 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 | /* Since poke tab is now finalized, publish aux to tracker. */ | 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 | mutex_lock(&mem_id_lock); | 295 | mutex_lock(&mem_id_lock); |
| 296 | ret = __mem_id_init_hash_table(); | 296 | ret = __mem_id_init_hash_table(); |
| 297 | mutex_unlock(&mem_id_lock); | 297 | mutex_unlock(&mem_id_lock); |
| 298 | if (ret < 0) { | 298 | if (ret < 0) |
| 299 | WARN_ON(1); | ||
| 300 | return ERR_PTR(ret); | 299 | return ERR_PTR(ret); |
| 301 | } | ||
| 302 | } | 300 | } |
| 303 | 301 | ||
| 304 | xdp_alloc = kzalloc(sizeof(*xdp_alloc), gfp); | 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 | LSKELS := fentry_test.c fexit_test.c fexit_sleep.c atomics.c \ | 457 | LSKELS := fentry_test.c fexit_test.c fexit_sleep.c atomics.c \ |
| 458 | trace_printk.c trace_vprintk.c map_ptr_kern.c \ | 458 | trace_printk.c trace_vprintk.c map_ptr_kern.c \ |
| 459 | core_kern.c core_kern_overflow.c test_ringbuf.c \ | 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 | # Generate both light skeleton and libbpf skeleton for these | 462 | # Generate both light skeleton and libbpf skeleton for these |
| 463 | LSKELS_EXTRA := test_ksyms_module.c test_ksyms_weak.c kfunc_call_test.c \ | 463 | LSKELS_EXTRA := test_ksyms_module.c test_ksyms_weak.c kfunc_call_test.c \ |
| @@ -12,9 +12,11 @@ · 11 unmodified lines | |||
| 12 | #include <sys/sysinfo.h> | 12 | #include <sys/sysinfo.h> |
| 13 | #include <linux/perf_event.h> | 13 | #include <linux/perf_event.h> |
| 14 | #include <linux/ring_buffer.h> | 14 | #include <linux/ring_buffer.h> |
| 15 | |||
| 15 | #include "test_ringbuf.lskel.h" | 16 | #include "test_ringbuf.lskel.h" |
| 16 | #include "test_ringbuf_n.lskel.h" | 17 | #include "test_ringbuf_n.lskel.h" |
| 17 | #include "test_ringbuf_map_key.lskel.h" | 18 | #include "test_ringbuf_map_key.lskel.h" |
| 19 | #include "test_ringbuf_write.lskel.h" | ||
| 18 | 20 | ||
| 19 | #define EDONE 7777 | 21 | #define EDONE 7777 |
| 20 | 22 | ||
| @@ -84,6 +86,58 @@ static void *poll_thread(void *input) · 63 unmodified lines | |||
| 84 | return (void *)(long)ring_buffer__poll(ringbuf, timeout); | 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 | static void ringbuf_subtest(void) | 141 | static void ringbuf_subtest(void) |
| 88 | { | 142 | { |
| 89 | const size_t rec_sz = BPF_RINGBUF_HDR_SZ + sizeof(struct sample); | 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 | ringbuf_n_subtest(); | 505 | ringbuf_n_subtest(); |
| 452 | if (test__start_subtest("ringbuf_map_key")) | 506 | if (test__start_subtest("ringbuf_map_key")) |
| 453 | ringbuf_map_key_subtest(); | 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 | SEC("tc") | 329 | SEC("tc") |
| 278 | __failure | 330 | __failure |
| 279 | __flag(BPF_F_TEST_STATE_FREQ) | 331 | __flag(BPF_F_TEST_STATE_FREQ) |
| @@ -307,6 +359,100 @@ int iter_limit_bug(struct __sk_buff *skb) · 27 unmodified lines | |||
| 307 | return 0; | 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 | #define ARR_SZ 1000000 | 456 | #define ARR_SZ 1000000 |
| 311 | int zero; | 457 | int zero; |
| 312 | char arr[ARR_SZ]; | 458 | char arr[ARR_SZ]; |
| @@ -224,6 +224,69 @@ l0_%=: \ · 223 unmodified lines | |||
| 224 | : __clobber_all); | 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 | #else | 290 | #else |
| 228 | 291 | ||
| 229 | SEC("socket") | 292 | SEC("socket") |