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() ====================
Showing the changes to kernel (3 of 10 files). Show all
Unified · Split
| @@ -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. */ |