diff options
Diffstat (limited to 'kernel/trace/bpf_trace.c')
| -rw-r--r-- | kernel/trace/bpf_trace.c | 113 | 
1 files changed, 107 insertions, 6 deletions
| diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 1ed08967fb97..3bbd3f0c810c 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -6,6 +6,7 @@  #include <linux/types.h>  #include <linux/slab.h>  #include <linux/bpf.h> +#include <linux/bpf_verifier.h>  #include <linux/bpf_perf_event.h>  #include <linux/btf.h>  #include <linux/filter.h> @@ -773,7 +774,7 @@ BPF_CALL_0(bpf_get_current_task_btf)  const struct bpf_func_proto bpf_get_current_task_btf_proto = {  	.func		= bpf_get_current_task_btf,  	.gpl_only	= true, -	.ret_type	= RET_PTR_TO_BTF_ID, +	.ret_type	= RET_PTR_TO_BTF_ID_TRUSTED,  	.ret_btf_id	= &btf_tracing_ids[BTF_TRACING_TYPE_TASK],  }; @@ -1456,6 +1457,10 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)  		return &bpf_get_current_cgroup_id_proto;  	case BPF_FUNC_get_current_ancestor_cgroup_id:  		return &bpf_get_current_ancestor_cgroup_id_proto; +	case BPF_FUNC_cgrp_storage_get: +		return &bpf_cgrp_storage_get_proto; +	case BPF_FUNC_cgrp_storage_delete: +		return &bpf_cgrp_storage_delete_proto;  #endif  	case BPF_FUNC_send_signal:  		return &bpf_send_signal_proto; @@ -1480,9 +1485,9 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)  	case BPF_FUNC_get_task_stack:  		return &bpf_get_task_stack_proto;  	case BPF_FUNC_copy_from_user: -		return prog->aux->sleepable ? &bpf_copy_from_user_proto : NULL; +		return &bpf_copy_from_user_proto;  	case BPF_FUNC_copy_from_user_task: -		return prog->aux->sleepable ? &bpf_copy_from_user_task_proto : NULL; +		return &bpf_copy_from_user_task_proto;  	case BPF_FUNC_snprintf_btf:  		return &bpf_snprintf_btf_proto;  	case BPF_FUNC_per_cpu_ptr: @@ -1490,8 +1495,12 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)  	case BPF_FUNC_this_cpu_ptr:  		return &bpf_this_cpu_ptr_proto;  	case BPF_FUNC_task_storage_get: +		if (bpf_prog_check_recur(prog)) +			return &bpf_task_storage_get_recur_proto;  		return &bpf_task_storage_get_proto;  	case BPF_FUNC_task_storage_delete: +		if (bpf_prog_check_recur(prog)) +			return &bpf_task_storage_delete_recur_proto;  		return &bpf_task_storage_delete_proto;  	case BPF_FUNC_for_each_map_elem:  		return &bpf_for_each_map_elem_proto; @@ -2452,6 +2461,8 @@ struct bpf_kprobe_multi_link {  	unsigned long *addrs;  	u64 *cookies;  	u32 cnt; +	u32 mods_cnt; +	struct module **mods;  };  struct bpf_kprobe_multi_run_ctx { @@ -2507,6 +2518,14 @@ error:  	return err;  } +static void kprobe_multi_put_modules(struct module **mods, u32 cnt) +{ +	u32 i; + +	for (i = 0; i < cnt; i++) +		module_put(mods[i]); +} +  static void free_user_syms(struct user_syms *us)  {  	kvfree(us->syms); @@ -2519,6 +2538,7 @@ static void bpf_kprobe_multi_link_release(struct bpf_link *link)  	kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link);  	unregister_fprobe(&kmulti_link->fp); +	kprobe_multi_put_modules(kmulti_link->mods, kmulti_link->mods_cnt);  }  static void bpf_kprobe_multi_link_dealloc(struct bpf_link *link) @@ -2528,6 +2548,7 @@ static void bpf_kprobe_multi_link_dealloc(struct bpf_link *link)  	kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link);  	kvfree(kmulti_link->addrs);  	kvfree(kmulti_link->cookies); +	kfree(kmulti_link->mods);  	kfree(kmulti_link);  } @@ -2550,7 +2571,7 @@ static void bpf_kprobe_multi_cookie_swap(void *a, void *b, int size, const void  	swap(*cookie_a, *cookie_b);  } -static int __bpf_kprobe_multi_cookie_cmp(const void *a, const void *b) +static int bpf_kprobe_multi_addrs_cmp(const void *a, const void *b)  {  	const unsigned long *addr_a = a, *addr_b = b; @@ -2561,7 +2582,7 @@ static int __bpf_kprobe_multi_cookie_cmp(const void *a, const void *b)  static int bpf_kprobe_multi_cookie_cmp(const void *a, const void *b, const void *priv)  { -	return __bpf_kprobe_multi_cookie_cmp(a, b); +	return bpf_kprobe_multi_addrs_cmp(a, b);  }  static u64 bpf_kprobe_multi_cookie(struct bpf_run_ctx *ctx) @@ -2579,7 +2600,7 @@ static u64 bpf_kprobe_multi_cookie(struct bpf_run_ctx *ctx)  		return 0;  	entry_ip = run_ctx->entry_ip;  	addr = bsearch(&entry_ip, link->addrs, link->cnt, sizeof(entry_ip), -		       __bpf_kprobe_multi_cookie_cmp); +		       bpf_kprobe_multi_addrs_cmp);  	if (!addr)  		return 0;  	cookie = link->cookies + (addr - link->addrs); @@ -2663,6 +2684,71 @@ static void symbols_swap_r(void *a, void *b, int size, const void *priv)  	}  } +struct module_addr_args { +	unsigned long *addrs; +	u32 addrs_cnt; +	struct module **mods; +	int mods_cnt; +	int mods_cap; +}; + +static int module_callback(void *data, const char *name, +			   struct module *mod, unsigned long addr) +{ +	struct module_addr_args *args = data; +	struct module **mods; + +	/* We iterate all modules symbols and for each we: +	 * - search for it in provided addresses array +	 * - if found we check if we already have the module pointer stored +	 *   (we iterate modules sequentially, so we can check just the last +	 *   module pointer) +	 * - take module reference and store it +	 */ +	if (!bsearch(&addr, args->addrs, args->addrs_cnt, sizeof(addr), +		       bpf_kprobe_multi_addrs_cmp)) +		return 0; + +	if (args->mods && args->mods[args->mods_cnt - 1] == mod) +		return 0; + +	if (args->mods_cnt == args->mods_cap) { +		args->mods_cap = max(16, args->mods_cap * 3 / 2); +		mods = krealloc_array(args->mods, args->mods_cap, sizeof(*mods), GFP_KERNEL); +		if (!mods) +			return -ENOMEM; +		args->mods = mods; +	} + +	if (!try_module_get(mod)) +		return -EINVAL; + +	args->mods[args->mods_cnt] = mod; +	args->mods_cnt++; +	return 0; +} + +static int get_modules_for_addrs(struct module ***mods, unsigned long *addrs, u32 addrs_cnt) +{ +	struct module_addr_args args = { +		.addrs     = addrs, +		.addrs_cnt = addrs_cnt, +	}; +	int err; + +	/* We return either err < 0 in case of error, ... */ +	err = module_kallsyms_on_each_symbol(module_callback, &args); +	if (err) { +		kprobe_multi_put_modules(args.mods, args.mods_cnt); +		kfree(args.mods); +		return err; +	} + +	/* or number of modules found if everything is ok. */ +	*mods = args.mods; +	return args.mods_cnt; +} +  int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)  {  	struct bpf_kprobe_multi_link *link = NULL; @@ -2773,10 +2859,25 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr  		       bpf_kprobe_multi_cookie_cmp,  		       bpf_kprobe_multi_cookie_swap,  		       link); +	} else { +		/* +		 * We need to sort addrs array even if there are no cookies +		 * provided, to allow bsearch in get_modules_for_addrs. +		 */ +		sort(addrs, cnt, sizeof(*addrs), +		       bpf_kprobe_multi_addrs_cmp, NULL); +	} + +	err = get_modules_for_addrs(&link->mods, addrs, cnt); +	if (err < 0) { +		bpf_link_cleanup(&link_primer); +		return err;  	} +	link->mods_cnt = err;  	err = register_fprobe_ips(&link->fp, addrs, cnt);  	if (err) { +		kprobe_multi_put_modules(link->mods, link->mods_cnt);  		bpf_link_cleanup(&link_primer);  		return err;  	} |