Skip to content

stream v3 #9148

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

Open
wants to merge 12 commits into
base: bpf-next_base
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion arch/x86/net/bpf_jit_comp.c
Original file line number Diff line number Diff line change
Expand Up @@ -3845,7 +3845,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
80 changes: 79 additions & 1 deletion include/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -1538,6 +1538,36 @@ 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;
int total_len;
int consumed_len;
char str[];
};

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

struct bpf_stream {
atomic_t capacity;
struct llist_head log; /* list of in-flight stream elements in LIFO order */

struct mutex lock; /* lock protecting backlog_{head,tail} */
struct llist_node *backlog_head; /* list of in-flight stream elements in FIFO order */
struct llist_node *backlog_tail; /* tail of the list above */
};

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 @@ -1646,6 +1676,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 @@ -2408,6 +2440,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 @@ -3550,16 +3584,56 @@ 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);
int bpf_prog_stream_read(struct bpf_prog *prog, enum bpf_stream_id stream_id, void __user *buf, int len);
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);

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

#define bpf_stream_stage(ss, prog, stream_id, expr) \
({ \
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 @@ -3595,4 +3669,8 @@ 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, int *nump);
struct bpf_prog *bpf_prog_find_from_stack(void);

#endif /* _LINUX_BPF_H */
24 changes: 24 additions & 0 deletions include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,17 @@ union bpf_iter_link_info {
* A new file descriptor (a nonnegative integer), or -1 if an
* error occurred (in which case, *errno* is set appropriately).
*
* BPF_PROG_STREAM_READ_BY_FD
* Description
* Read data of a program's BPF stream. The program is identified
* by *prog_fd*, and the stream is identified by the *stream_id*.
* The data is copied to a buffer pointed to by *stream_buf*, and
* filled less than or equal to *stream_buf_len* bytes.
*
* Return
* Number of bytes read from the stream on success, or -1 if an
* error occurred (in which case, *errno* is set appropriately).
*
* NOTES
* eBPF objects (maps and programs) can be shared between processes.
*
Expand Down Expand Up @@ -961,6 +972,7 @@ enum bpf_cmd {
BPF_LINK_DETACH,
BPF_PROG_BIND_MAP,
BPF_TOKEN_CREATE,
BPF_PROG_STREAM_READ_BY_FD,
__MAX_BPF_CMD,
};

Expand Down Expand Up @@ -1463,6 +1475,11 @@ struct bpf_stack_build_id {

#define BPF_OBJ_NAME_LEN 16U

enum {
BPF_STREAM_STDOUT = 1,
BPF_STREAM_STDERR = 2,
};

union bpf_attr {
struct { /* anonymous struct used by BPF_MAP_CREATE command */
__u32 map_type; /* one of enum bpf_map_type */
Expand Down Expand Up @@ -1849,6 +1866,13 @@ union bpf_attr {
__u32 bpffs_fd;
} token_create;

struct {
__aligned_u64 stream_buf;
__u32 stream_buf_len;
__u32 stream_id;
__u32 prog_fd;
} prog_stream_read;

} __attribute__((aligned(8)));

/* The description below is an attempt at providing documentation to eBPF
Expand Down
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
105 changes: 103 additions & 2 deletions 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 @@ -778,7 +782,10 @@ bool is_bpf_text_address(unsigned long addr)

struct bpf_prog *bpf_prog_ksym_find(unsigned long addr)
{
struct bpf_ksym *ksym = bpf_ksym_find(addr);
struct bpf_ksym *ksym;

WARN_ON_ONCE(!rcu_read_lock_held());
ksym = bpf_ksym_find(addr);

return ksym && ksym->prog ?
container_of(ksym, struct bpf_prog_aux, ksym)->prog :
Expand Down Expand Up @@ -2862,6 +2869,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 Down Expand Up @@ -3160,6 +3168,22 @@ u64 __weak arch_bpf_timed_may_goto(void)
return 0;
}

static noinline void bpf_prog_report_may_goto_violation(void)
{
#ifdef CONFIG_BPF_SYSCALL
struct bpf_stream_stage ss;
struct bpf_prog *prog;

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

u64 bpf_check_timed_may_goto(struct bpf_timed_may_goto *p)
{
u64 time = ktime_get_mono_fast_ns();
Expand All @@ -3170,8 +3194,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 @@ -3208,3 +3234,78 @@ EXPORT_SYMBOL(bpf_stats_enabled_key);

EXPORT_TRACEPOINT_SYMBOL_GPL(xdp_exception);
EXPORT_TRACEPOINT_SYMBOL_GPL(xdp_bulk_tx);

#ifdef CONFIG_BPF_SYSCALL

int bpf_prog_get_file_line(struct bpf_prog *prog, unsigned long ip, const char **filep,
const char **linep, int *nump)
{
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; i < prog->aux->nr_linfo &&
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;
*nump = BPF_LINE_INFO_LINE_NUM(linfo[idx].line_col);
return 0;
}

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;

rcu_read_lock();
prog = bpf_prog_ksym_find(ip);
rcu_read_unlock();
if (!prog)
return true;
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;
}

#endif
Loading
Loading