mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
Merge tag 'for-netdev' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/bpf/bpf
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() ==================== Link: https://patch.msgid.link/20240624124330.8401-1-daniel@iogearbox.net Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -212,6 +212,7 @@ static u64 arena_map_mem_usage(const struct bpf_map *map)
|
||||
struct vma_list {
|
||||
struct vm_area_struct *vma;
|
||||
struct list_head head;
|
||||
atomic_t mmap_count;
|
||||
};
|
||||
|
||||
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)
|
||||
vml = kmalloc(sizeof(*vml), GFP_KERNEL);
|
||||
if (!vml)
|
||||
return -ENOMEM;
|
||||
atomic_set(&vml->mmap_count, 1);
|
||||
vma->vm_private_data = vml;
|
||||
vml->vma = vma;
|
||||
list_add(&vml->head, &arena->vma_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void arena_vm_open(struct vm_area_struct *vma)
|
||||
{
|
||||
struct vma_list *vml = vma->vm_private_data;
|
||||
|
||||
atomic_inc(&vml->mmap_count);
|
||||
}
|
||||
|
||||
static void arena_vm_close(struct vm_area_struct *vma)
|
||||
{
|
||||
struct bpf_map *map = vma->vm_file->private_data;
|
||||
struct bpf_arena *arena = container_of(map, struct bpf_arena, map);
|
||||
struct vma_list *vml;
|
||||
struct vma_list *vml = vma->vm_private_data;
|
||||
|
||||
if (!atomic_dec_and_test(&vml->mmap_count))
|
||||
return;
|
||||
guard(mutex)(&arena->lock);
|
||||
vml = vma->vm_private_data;
|
||||
/* update link list under lock */
|
||||
list_del(&vml->head);
|
||||
vma->vm_private_data = NULL;
|
||||
kfree(vml);
|
||||
@@ -287,6 +298,7 @@ out:
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct arena_vm_ops = {
|
||||
.open = arena_vm_open,
|
||||
.close = arena_vm_close,
|
||||
.fault = arena_vm_fault,
|
||||
};
|
||||
|
||||
@@ -51,7 +51,8 @@ struct bpf_ringbuf {
|
||||
* This prevents a user-space application from modifying the
|
||||
* position and ruining in-kernel tracking. The permissions of the
|
||||
* pages depend on who is producing samples: user-space or the
|
||||
* kernel.
|
||||
* kernel. Note that the pending counter is placed in the same
|
||||
* page as the producer, so that it shares the same cache line.
|
||||
*
|
||||
* Kernel-producer
|
||||
* ---------------
|
||||
@@ -70,6 +71,7 @@ struct bpf_ringbuf {
|
||||
*/
|
||||
unsigned long consumer_pos __aligned(PAGE_SIZE);
|
||||
unsigned long producer_pos __aligned(PAGE_SIZE);
|
||||
unsigned long pending_pos;
|
||||
char data[] __aligned(PAGE_SIZE);
|
||||
};
|
||||
|
||||
@@ -179,6 +181,7 @@ static struct bpf_ringbuf *bpf_ringbuf_alloc(size_t data_sz, int numa_node)
|
||||
rb->mask = data_sz - 1;
|
||||
rb->consumer_pos = 0;
|
||||
rb->producer_pos = 0;
|
||||
rb->pending_pos = 0;
|
||||
|
||||
return rb;
|
||||
}
|
||||
@@ -404,9 +407,9 @@ bpf_ringbuf_restore_from_rec(struct bpf_ringbuf_hdr *hdr)
|
||||
|
||||
static void *__bpf_ringbuf_reserve(struct bpf_ringbuf *rb, u64 size)
|
||||
{
|
||||
unsigned long cons_pos, prod_pos, new_prod_pos, flags;
|
||||
u32 len, pg_off;
|
||||
unsigned long cons_pos, prod_pos, new_prod_pos, pend_pos, flags;
|
||||
struct bpf_ringbuf_hdr *hdr;
|
||||
u32 len, pg_off, tmp_size, hdr_len;
|
||||
|
||||
if (unlikely(size > RINGBUF_MAX_RECORD_SZ))
|
||||
return NULL;
|
||||
@@ -424,13 +427,29 @@ static void *__bpf_ringbuf_reserve(struct bpf_ringbuf *rb, u64 size)
|
||||
spin_lock_irqsave(&rb->spinlock, flags);
|
||||
}
|
||||
|
||||
pend_pos = rb->pending_pos;
|
||||
prod_pos = rb->producer_pos;
|
||||
new_prod_pos = prod_pos + len;
|
||||
|
||||
/* check for out of ringbuf space by ensuring producer position
|
||||
* doesn't advance more than (ringbuf_size - 1) ahead
|
||||
while (pend_pos < prod_pos) {
|
||||
hdr = (void *)rb->data + (pend_pos & rb->mask);
|
||||
hdr_len = READ_ONCE(hdr->len);
|
||||
if (hdr_len & BPF_RINGBUF_BUSY_BIT)
|
||||
break;
|
||||
tmp_size = hdr_len & ~BPF_RINGBUF_DISCARD_BIT;
|
||||
tmp_size = round_up(tmp_size + BPF_RINGBUF_HDR_SZ, 8);
|
||||
pend_pos += tmp_size;
|
||||
}
|
||||
rb->pending_pos = pend_pos;
|
||||
|
||||
/* check for out of ringbuf space:
|
||||
* - by ensuring producer position doesn't advance more than
|
||||
* (ringbuf_size - 1) ahead
|
||||
* - by ensuring oldest not yet committed record until newest
|
||||
* record does not span more than (ringbuf_size - 1)
|
||||
*/
|
||||
if (new_prod_pos - cons_pos > rb->mask) {
|
||||
if (new_prod_pos - cons_pos > rb->mask ||
|
||||
new_prod_pos - pend_pos > rb->mask) {
|
||||
spin_unlock_irqrestore(&rb->spinlock, flags);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -6236,6 +6236,7 @@ static void set_sext32_default_val(struct bpf_reg_state *reg, int size)
|
||||
}
|
||||
reg->u32_min_value = 0;
|
||||
reg->u32_max_value = U32_MAX;
|
||||
reg->var_off = tnum_subreg(tnum_unknown);
|
||||
}
|
||||
|
||||
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)
|
||||
reg->s32_max_value = s32_max;
|
||||
reg->u32_min_value = (u32)s32_min;
|
||||
reg->u32_max_value = (u32)s32_max;
|
||||
reg->var_off = tnum_subreg(tnum_range(s32_min, s32_max));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -12719,6 +12721,16 @@ static bool signed_add32_overflows(s32 a, s32 b)
|
||||
return res < a;
|
||||
}
|
||||
|
||||
static bool signed_add16_overflows(s16 a, s16 b)
|
||||
{
|
||||
/* Do the add in u16, where overflow is well-defined */
|
||||
s16 res = (s16)((u16)a + (u16)b);
|
||||
|
||||
if (b < 0)
|
||||
return res > a;
|
||||
return res < a;
|
||||
}
|
||||
|
||||
static bool signed_sub_overflows(s64 a, s64 b)
|
||||
{
|
||||
/* 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)
|
||||
goto skip_inf_loop_check;
|
||||
}
|
||||
if (is_may_goto_insn_at(env, insn_idx)) {
|
||||
if (states_equal(env, &sl->state, cur, RANGE_WITHIN)) {
|
||||
if (sl->state.may_goto_depth != cur->may_goto_depth &&
|
||||
states_equal(env, &sl->state, cur, RANGE_WITHIN)) {
|
||||
update_loop_entry(cur, &sl->state);
|
||||
goto hit;
|
||||
}
|
||||
goto skip_inf_loop_check;
|
||||
}
|
||||
if (calls_callback(env, insn_idx)) {
|
||||
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
|
||||
return new_prog;
|
||||
}
|
||||
|
||||
/*
|
||||
* For all jmp insns in a given 'prog' that point to 'tgt_idx' insn adjust the
|
||||
* jump offset by 'delta'.
|
||||
*/
|
||||
static int adjust_jmp_off(struct bpf_prog *prog, u32 tgt_idx, u32 delta)
|
||||
{
|
||||
struct bpf_insn *insn = prog->insnsi;
|
||||
u32 insn_cnt = prog->len, i;
|
||||
|
||||
for (i = 0; i < insn_cnt; i++, insn++) {
|
||||
u8 code = insn->code;
|
||||
|
||||
if ((BPF_CLASS(code) != BPF_JMP && BPF_CLASS(code) != BPF_JMP32) ||
|
||||
BPF_OP(code) == BPF_CALL || BPF_OP(code) == BPF_EXIT)
|
||||
continue;
|
||||
|
||||
if (insn->code == (BPF_JMP32 | BPF_JA)) {
|
||||
if (i + 1 + insn->imm != tgt_idx)
|
||||
continue;
|
||||
if (signed_add32_overflows(insn->imm, delta))
|
||||
return -ERANGE;
|
||||
insn->imm += delta;
|
||||
} else {
|
||||
if (i + 1 + insn->off != tgt_idx)
|
||||
continue;
|
||||
if (signed_add16_overflows(insn->imm, delta))
|
||||
return -ERANGE;
|
||||
insn->off += delta;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adjust_subprog_starts_after_remove(struct bpf_verifier_env *env,
|
||||
u32 off, u32 cnt)
|
||||
{
|
||||
@@ -20004,7 +20049,10 @@ static int do_misc_fixups(struct bpf_verifier_env *env)
|
||||
|
||||
stack_depth_extra = 8;
|
||||
insn_buf[0] = BPF_LDX_MEM(BPF_DW, BPF_REG_AX, BPF_REG_10, stack_off);
|
||||
insn_buf[1] = BPF_JMP_IMM(BPF_JEQ, BPF_REG_AX, 0, insn->off + 2);
|
||||
if (insn->off >= 0)
|
||||
insn_buf[1] = BPF_JMP_IMM(BPF_JEQ, BPF_REG_AX, 0, insn->off + 2);
|
||||
else
|
||||
insn_buf[1] = BPF_JMP_IMM(BPF_JEQ, BPF_REG_AX, 0, insn->off - 1);
|
||||
insn_buf[2] = BPF_ALU64_IMM(BPF_SUB, BPF_REG_AX, 1);
|
||||
insn_buf[3] = BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_AX, stack_off);
|
||||
cnt = 4;
|
||||
@@ -20546,6 +20594,13 @@ next_insn:
|
||||
if (!new_prog)
|
||||
return -ENOMEM;
|
||||
env->prog = prog = new_prog;
|
||||
/*
|
||||
* If may_goto is a first insn of a prog there could be a jmp
|
||||
* insn that points to it, hence adjust all such jmps to point
|
||||
* to insn after BPF_ST that inits may_goto count.
|
||||
* Adjustment will succeed because bpf_patch_insn_data() didn't fail.
|
||||
*/
|
||||
WARN_ON(adjust_jmp_off(env->prog, subprog_start, 1));
|
||||
}
|
||||
|
||||
/* Since poke tab is now finalized, publish aux to tracker. */
|
||||
|
||||
Reference in New Issue
Block a user