Skip to content

BPF Standard Streams #8905

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
22 changes: 18 additions & 4 deletions arch/x86/net/bpf_jit_comp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1384,15 +1384,27 @@ static int emit_atomic_ld_st_index(u8 **pprog, u32 atomic_op, u32 size,
}

#define DONT_CLEAR 1
#define ARENA_FAULT (1 << 8)

bool ex_handler_bpf(const struct exception_table_entry *x, struct pt_regs *regs)
{
u32 reg = x->fixup >> 8;
u32 arena_reg = (x->fixup >> 8) & 0xff;
bool is_arena = !!arena_reg;
u32 reg = x->fixup >> 16;
unsigned long addr;

/* Read here, if src_reg is dst_reg for load, we'll write 0 to it. */
if (is_arena)
addr = *(unsigned long *)((void *)regs + arena_reg);

/* jump over faulting load and clear dest register */
if (reg != DONT_CLEAR)
*(unsigned long *)((void *)regs + reg) = 0;
regs->ip += x->fixup & 0xff;

if (is_arena)
bpf_prog_report_arena_violation(reg == DONT_CLEAR, addr);

return true;
}

Expand Down Expand Up @@ -2043,7 +2055,10 @@ st: if (is_imm8(insn->off))
ex->data = EX_TYPE_BPF;

ex->fixup = (prog - start_of_ldx) |
((BPF_CLASS(insn->code) == BPF_LDX ? reg2pt_regs[dst_reg] : DONT_CLEAR) << 8);
((BPF_CLASS(insn->code) == BPF_LDX ? reg2pt_regs[dst_reg] : DONT_CLEAR) << 16)
| ((BPF_CLASS(insn->code) == BPF_LDX ? reg2pt_regs[src_reg] : reg2pt_regs[dst_reg])<< 8);
/* Ensure src_reg offset fits in 1 byte. */
BUILD_BUG_ON(sizeof(struct pt_regs) > U8_MAX);
}
break;

Expand Down Expand Up @@ -2161,7 +2176,7 @@ st: if (is_imm8(insn->off))
* End result: x86 insn "mov rbx, qword ptr [rax+0x14]"
* of 4 bytes will be ignored and rbx will be zero inited.
*/
ex->fixup = (prog - start_of_ldx) | (reg2pt_regs[dst_reg] << 8);
ex->fixup = (prog - start_of_ldx) | (reg2pt_regs[dst_reg] << 16);
}
break;

Expand Down Expand Up @@ -3791,7 +3806,6 @@ void arch_bpf_stack_walk(bool (*consume_fn)(void *cookie, u64 ip, u64 sp, u64 bp
}
return;
#endif
WARN(1, "verification of programs using bpf_throw should have failed\n");
}

void bpf_arch_poke_desc_update(struct bpf_jit_poke_descriptor *poke,
Expand Down
91 changes: 90 additions & 1 deletion include/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -1344,6 +1344,12 @@ enum bpf_dynptr_type {
BPF_DYNPTR_TYPE_XDP,
};

struct bpf_mem_slice {
void *ptr;
u32 len;
u32 reserved;
};

int bpf_dynptr_check_size(u32 size);
u32 __bpf_dynptr_size(const struct bpf_dynptr_kern *ptr);
const void *__bpf_dynptr_data(const struct bpf_dynptr_kern *ptr, u32 len);
Expand Down Expand Up @@ -1518,6 +1524,40 @@ struct btf_mod_pair {

struct bpf_kfunc_desc_tab;

enum bpf_stream_id {
BPF_STDOUT = 1,
BPF_STDERR = 2,
};

struct bpf_stream_elem {
struct llist_node node;
struct bpf_mem_slice mem_slice;
char str[];
};

struct bpf_stream_elem_batch {
struct llist_node *node;
};

enum {
BPF_STREAM_MAX_CAPACITY = (4 * 1024U * 1024U),
};

struct bpf_stream {
enum bpf_stream_id stream_id;
atomic_t capacity;
struct llist_head log;

rqspinlock_t lock;
struct llist_node *backlog_head;
struct llist_node *backlog_tail;
};

struct bpf_stream_stage {
struct llist_head log;
int len;
};

struct bpf_prog_aux {
atomic64_t refcnt;
u32 used_map_cnt;
Expand Down Expand Up @@ -1626,6 +1666,8 @@ struct bpf_prog_aux {
struct work_struct work;
struct rcu_head rcu;
};
struct bpf_stream stream[2];
atomic_t stream_error_cnt;
};

struct bpf_prog {
Expand Down Expand Up @@ -2385,6 +2427,8 @@ int generic_map_delete_batch(struct bpf_map *map,
struct bpf_map *bpf_map_get_curr_or_next(u32 *id);
struct bpf_prog *bpf_prog_get_curr_or_next(u32 *id);


struct page *__bpf_alloc_page(int nid);
int bpf_map_alloc_pages(const struct bpf_map *map, int nid,
unsigned long nr_pages, struct page **page_array);
#ifdef CONFIG_MEMCG
Expand Down Expand Up @@ -3523,16 +3567,58 @@ bool btf_id_set_contains(const struct btf_id_set *set, u32 id);
#define MAX_BPRINTF_VARARGS 12
#define MAX_BPRINTF_BUF 1024

/* Per-cpu temp buffers used by printf-like helpers to store the bprintf binary
* arguments representation.
*/
#define MAX_BPRINTF_BIN_ARGS 512

struct bpf_bprintf_buffers {
char bin_args[MAX_BPRINTF_BIN_ARGS];
char buf[MAX_BPRINTF_BUF];
};

struct bpf_bprintf_data {
u32 *bin_args;
char *buf;
bool get_bin_args;
bool get_buf;
};

int bpf_bprintf_prepare(char *fmt, u32 fmt_size, const u64 *raw_args,
int bpf_bprintf_prepare(const char *fmt, u32 fmt_size, const u64 *raw_args,
u32 num_args, struct bpf_bprintf_data *data);
void bpf_bprintf_cleanup(struct bpf_bprintf_data *data);
int bpf_try_get_buffers(struct bpf_bprintf_buffers **bufs);
void bpf_put_buffers(void);

#define BPF_PROG_STREAM_ERROR_CNT 512

void bpf_prog_stream_init(struct bpf_prog *prog);
void bpf_prog_stream_free(struct bpf_prog *prog);

void bpf_stream_stage_init(struct bpf_stream_stage *ss);
void bpf_stream_stage_free(struct bpf_stream_stage *ss);
__printf(2, 3)
int bpf_stream_stage_printk(struct bpf_stream_stage *ss, const char *fmt, ...);
int bpf_stream_stage_commit(struct bpf_stream_stage *ss, struct bpf_prog *prog,
enum bpf_stream_id stream_id);
int bpf_stream_stage_dump_stack(struct bpf_stream_stage *ss);

bool bpf_prog_stream_error_limit(struct bpf_prog *prog);
void bpf_prog_report_arena_violation(bool write, unsigned long addr);

#define bpf_stream_printk(...) bpf_stream_stage_printk(&__ss, __VA_ARGS__)
#define bpf_stream_dump_stack() bpf_stream_stage_dump_stack(&__ss)

#define bpf_stream_stage(prog, stream_id, expr) \
({ \
struct bpf_stream_stage __ss; \
if (!bpf_prog_stream_error_limit(prog)) { \
bpf_stream_stage_init(&__ss); \
(expr); \
bpf_stream_stage_commit(&__ss, prog, stream_id); \
bpf_stream_stage_free(&__ss); \
} \
})

#ifdef CONFIG_BPF_LSM
void bpf_cgroup_atype_get(u32 attach_btf_id, int cgroup_atype);
Expand Down Expand Up @@ -3568,4 +3654,7 @@ static inline bool bpf_is_subprog(const struct bpf_prog *prog)
return prog->aux->func_idx != 0;
}

int bpf_prog_get_file_line(struct bpf_prog *prog, unsigned long ip, const char **filep, const char **linep);
struct bpf_prog *bpf_prog_find_from_stack(void);

#endif /* _LINUX_BPF_H */
2 changes: 1 addition & 1 deletion kernel/bpf/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ obj-$(CONFIG_BPF_SYSCALL) += bpf_local_storage.o bpf_task_storage.o
obj-${CONFIG_BPF_LSM} += bpf_inode_storage.o
obj-$(CONFIG_BPF_SYSCALL) += disasm.o mprog.o
obj-$(CONFIG_BPF_JIT) += trampoline.o
obj-$(CONFIG_BPF_SYSCALL) += btf.o memalloc.o rqspinlock.o
obj-$(CONFIG_BPF_SYSCALL) += btf.o memalloc.o rqspinlock.o stream.o
ifeq ($(CONFIG_MMU)$(CONFIG_64BIT),yy)
obj-$(CONFIG_BPF_SYSCALL) += arena.o range_tree.o
endif
Expand Down
14 changes: 14 additions & 0 deletions kernel/bpf/arena.c
Original file line number Diff line number Diff line change
Expand Up @@ -590,3 +590,17 @@ static int __init kfunc_init(void)
return register_btf_kfunc_id_set(BPF_PROG_TYPE_UNSPEC, &common_kfunc_set);
}
late_initcall(kfunc_init);

void bpf_prog_report_arena_violation(bool write, unsigned long addr)
{
struct bpf_prog *prog;

prog = bpf_prog_find_from_stack();
if (!prog)
return;
bpf_stream_stage(prog, BPF_STDERR, ({
bpf_stream_printk("ERROR: Arena %s access at unmapped address 0x%lx\n",
write ? "WRITE" : "READ", addr);
bpf_stream_dump_stack();
}));
}
95 changes: 94 additions & 1 deletion kernel/bpf/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ struct bpf_prog *bpf_prog_alloc_no_stats(unsigned int size, gfp_t gfp_extra_flag
mutex_init(&fp->aux->ext_mutex);
mutex_init(&fp->aux->dst_mutex);

#ifdef CONFIG_BPF_SYSCALL
bpf_prog_stream_init(fp);
#endif

return fp;
}

Expand Down Expand Up @@ -2861,6 +2865,7 @@ static void bpf_prog_free_deferred(struct work_struct *work)
aux = container_of(work, struct bpf_prog_aux, work);
#ifdef CONFIG_BPF_SYSCALL
bpf_free_kfunc_btf_tab(aux->kfunc_btf_tab);
bpf_prog_stream_free(aux->prog);
#endif
#ifdef CONFIG_CGROUP_BPF
if (aux->cgroup_atype != CGROUP_BPF_ATTACH_TYPE_INVALID)
Expand All @@ -2877,6 +2882,13 @@ static void bpf_prog_free_deferred(struct work_struct *work)
if (aux->dst_trampoline)
bpf_trampoline_put(aux->dst_trampoline);
for (i = 0; i < aux->real_func_cnt; i++) {
#ifdef CONFIG_BPF_SYSCALL
/* Ensure we don't push to subprog lists. */
if (bpf_is_subprog(aux->func[i])) {
WARN_ON_ONCE(!llist_empty(&aux->func[i]->aux->stream[0].log));
WARN_ON_ONCE(!llist_empty(&aux->func[i]->aux->stream[1].log));
}
#endif
/* We can just unlink the subprog poke descriptor table as
* it was originally linked to the main program and is also
* released along with it.
Expand Down Expand Up @@ -3144,6 +3156,19 @@ u64 __weak arch_bpf_timed_may_goto(void)
return 0;
}

static noinline void bpf_prog_report_may_goto_violation(void)
{
struct bpf_prog *prog;

prog = bpf_prog_find_from_stack();
if (!prog)
return;
bpf_stream_stage(prog, BPF_STDERR, ({
bpf_stream_printk("ERROR: Timeout detected for may_goto instruction\n");
bpf_stream_dump_stack();
}));
}

u64 bpf_check_timed_may_goto(struct bpf_timed_may_goto *p)
{
u64 time = ktime_get_mono_fast_ns();
Expand All @@ -3154,8 +3179,10 @@ u64 bpf_check_timed_may_goto(struct bpf_timed_may_goto *p)
return BPF_MAX_TIMED_LOOPS;
}
/* Check if we've exhausted our time slice, and zero count. */
if (time - p->timestamp >= (NSEC_PER_SEC / 4))
if (unlikely(time - p->timestamp >= (NSEC_PER_SEC / 4))) {
bpf_prog_report_may_goto_violation();
return 0;
}
/* Refresh the count for the stack frame. */
return BPF_MAX_TIMED_LOOPS;
}
Expand Down Expand Up @@ -3192,3 +3219,69 @@ EXPORT_SYMBOL(bpf_stats_enabled_key);

EXPORT_TRACEPOINT_SYMBOL_GPL(xdp_exception);
EXPORT_TRACEPOINT_SYMBOL_GPL(xdp_bulk_tx);

int bpf_prog_get_file_line(struct bpf_prog *prog, unsigned long ip, const char **filep, const char **linep)
{
int idx = -1, insn_start, insn_end, len;
struct bpf_line_info *linfo;
void **jited_linfo;
struct btf *btf;

btf = prog->aux->btf;
linfo = prog->aux->linfo;
jited_linfo = prog->aux->jited_linfo;

if (!btf || !linfo || !prog->aux->jited_linfo)
return -EINVAL;
len = prog->aux->func ? prog->aux->func[prog->aux->func_idx]->len : prog->len;

linfo = &prog->aux->linfo[prog->aux->linfo_idx];
jited_linfo = &prog->aux->jited_linfo[prog->aux->linfo_idx];

insn_start = linfo[0].insn_off;
insn_end = insn_start + len;

for (int i = 0; linfo[i].insn_off >= insn_start && linfo[i].insn_off < insn_end; i++) {
if (jited_linfo[i] >= (void *)ip)
break;
idx = i;
}

if (idx == -1)
return -ENOENT;

/* Get base component of the file path. */
*filep = btf_name_by_offset(btf, linfo[idx].file_name_off);
*filep = kbasename(*filep);
/* Obtain the source line, and strip whitespace in prefix. */
*linep = btf_name_by_offset(btf, linfo[idx].line_off);
while (isspace(**linep))
*linep += 1;
return BPF_LINE_INFO_LINE_NUM(linfo[idx].line_col);
}

struct walk_stack_ctx {
struct bpf_prog *prog;
};

static bool find_from_stack_cb(void *cookie, u64 ip, u64 sp, u64 bp)
{
struct walk_stack_ctx *ctxp = cookie;
struct bpf_prog *prog;

if (!is_bpf_text_address(ip))
return true;
prog = bpf_prog_ksym_find(ip);
if (bpf_is_subprog(prog))
return true;
ctxp->prog = prog;
return false;
}

struct bpf_prog *bpf_prog_find_from_stack(void)
{
struct walk_stack_ctx ctx = {};

arch_bpf_stack_walk(find_from_stack_cb, &ctx);
return ctx.prog;
}
Loading
Loading