diff options
Diffstat (limited to 'kernel/trace/bpf_trace.c')
| -rw-r--r-- | kernel/trace/bpf_trace.c | 63 | 
1 files changed, 61 insertions, 2 deletions
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 88a041adee90..0fe96c7c8803 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -81,13 +81,16 @@ static const struct bpf_func_proto bpf_probe_read_proto = {  /*   * limited trace_printk() - * only %d %u %x %ld %lu %lx %lld %llu %llx %p conversion specifiers allowed + * only %d %u %x %ld %lu %lx %lld %llu %llx %p %s conversion specifiers allowed   */  static u64 bpf_trace_printk(u64 r1, u64 fmt_size, u64 r3, u64 r4, u64 r5)  {  	char *fmt = (char *) (long) r1; +	bool str_seen = false;  	int mod[3] = {};  	int fmt_cnt = 0; +	u64 unsafe_addr; +	char buf[64];  	int i;  	/* @@ -114,12 +117,37 @@ static u64 bpf_trace_printk(u64 r1, u64 fmt_size, u64 r3, u64 r4, u64 r5)  		if (fmt[i] == 'l') {  			mod[fmt_cnt]++;  			i++; -		} else if (fmt[i] == 'p') { +		} else if (fmt[i] == 'p' || fmt[i] == 's') {  			mod[fmt_cnt]++;  			i++;  			if (!isspace(fmt[i]) && !ispunct(fmt[i]) && fmt[i] != 0)  				return -EINVAL;  			fmt_cnt++; +			if (fmt[i - 1] == 's') { +				if (str_seen) +					/* allow only one '%s' per fmt string */ +					return -EINVAL; +				str_seen = true; + +				switch (fmt_cnt) { +				case 1: +					unsafe_addr = r3; +					r3 = (long) buf; +					break; +				case 2: +					unsafe_addr = r4; +					r4 = (long) buf; +					break; +				case 3: +					unsafe_addr = r5; +					r5 = (long) buf; +					break; +				} +				buf[0] = 0; +				strncpy_from_unsafe(buf, +						    (void *) (long) unsafe_addr, +						    sizeof(buf)); +			}  			continue;  		} @@ -158,6 +186,35 @@ const struct bpf_func_proto *bpf_get_trace_printk_proto(void)  	return &bpf_trace_printk_proto;  } +static u64 bpf_perf_event_read(u64 r1, u64 index, u64 r3, u64 r4, u64 r5) +{ +	struct bpf_map *map = (struct bpf_map *) (unsigned long) r1; +	struct bpf_array *array = container_of(map, struct bpf_array, map); +	struct perf_event *event; + +	if (unlikely(index >= array->map.max_entries)) +		return -E2BIG; + +	event = (struct perf_event *)array->ptrs[index]; +	if (!event) +		return -ENOENT; + +	/* +	 * we don't know if the function is run successfully by the +	 * return value. It can be judged in other places, such as +	 * eBPF programs. +	 */ +	return perf_event_read_local(event); +} + +const struct bpf_func_proto bpf_perf_event_read_proto = { +	.func		= bpf_perf_event_read, +	.gpl_only	= false, +	.ret_type	= RET_INTEGER, +	.arg1_type	= ARG_CONST_MAP_PTR, +	.arg2_type	= ARG_ANYTHING, +}; +  static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func_id)  {  	switch (func_id) { @@ -183,6 +240,8 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func  		return bpf_get_trace_printk_proto();  	case BPF_FUNC_get_smp_processor_id:  		return &bpf_get_smp_processor_id_proto; +	case BPF_FUNC_perf_event_read: +		return &bpf_perf_event_read_proto;  	default:  		return NULL;  	}  |