Commit 7fb312d2 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull tracing fixes from Steven Rostedt:
 "Various fixes for tracing:

   - Fix a return value of traceprobe_parse_event_name()

   - Fix NULL pointer dereference from failed ftrace enabling

   - Fix NULL pointer dereference when asking for registers from eprobes

   - Make eprobes consistent with kprobes/uprobes, filters and
     histograms"

* tag 'trace-v6.0-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace:
  tracing: Have filter accept "common_cpu" to be consistent
  tracing/probes: Have kprobes and uprobes use $COMM too
  tracing/eprobes: Have event probes be consistent with kprobes and uprobes
  tracing/eprobes: Fix reading of string fields
  tracing/eprobes: Do not hardcode $comm as a string
  tracing/eprobes: Do not allow eprobes to use $stack, or % for regs
  ftrace: Fix NULL pointer dereference in is_ftrace_trampoline when ftrace is dead
  tracing/perf: Fix double put of trace event when init fails
  tracing: React to error return from traceprobe_parse_event_name()
parents e3f259d3 b2380577
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -2974,6 +2974,16 @@ int ftrace_startup(struct ftrace_ops *ops, int command)

	ftrace_startup_enable(command);

	/*
	 * If ftrace is in an undefined state, we just remove ops from list
	 * to prevent the NULL pointer, instead of totally rolling it back and
	 * free trampoline, because those actions could cause further damage.
	 */
	if (unlikely(ftrace_disabled)) {
		__unregister_ftrace_function(ops);
		return -ENODEV;
	}

	ops->flags &= ~FTRACE_OPS_FL_ADDING;

	return 0;
+86 −7
Original line number Diff line number Diff line
@@ -227,6 +227,7 @@ static int trace_eprobe_tp_arg_update(struct trace_eprobe *ep, int i)
	struct probe_arg *parg = &ep->tp.args[i];
	struct ftrace_event_field *field;
	struct list_head *head;
	int ret = -ENOENT;

	head = trace_get_fields(ep->event);
	list_for_each_entry(field, head, link) {
@@ -236,9 +237,20 @@ static int trace_eprobe_tp_arg_update(struct trace_eprobe *ep, int i)
			return 0;
		}
	}

	/*
	 * Argument not found on event. But allow for comm and COMM
	 * to be used to get the current->comm.
	 */
	if (strcmp(parg->code->data, "COMM") == 0 ||
	    strcmp(parg->code->data, "comm") == 0) {
		parg->code->op = FETCH_OP_COMM;
		ret = 0;
	}

	kfree(parg->code->data);
	parg->code->data = NULL;
	return -ENOENT;
	return ret;
}

static int eprobe_event_define_fields(struct trace_event_call *event_call)
@@ -311,6 +323,27 @@ static unsigned long get_event_field(struct fetch_insn *code, void *rec)

	addr = rec + field->offset;

	if (is_string_field(field)) {
		switch (field->filter_type) {
		case FILTER_DYN_STRING:
			val = (unsigned long)(rec + (*(unsigned int *)addr & 0xffff));
			break;
		case FILTER_RDYN_STRING:
			val = (unsigned long)(addr + (*(unsigned int *)addr & 0xffff));
			break;
		case FILTER_STATIC_STRING:
			val = (unsigned long)addr;
			break;
		case FILTER_PTR_STRING:
			val = (unsigned long)(*(char *)addr);
			break;
		default:
			WARN_ON_ONCE(1);
			return 0;
		}
		return val;
	}

	switch (field->size) {
	case 1:
		if (field->is_signed)
@@ -342,16 +375,38 @@ static unsigned long get_event_field(struct fetch_insn *code, void *rec)

static int get_eprobe_size(struct trace_probe *tp, void *rec)
{
	struct fetch_insn *code;
	struct probe_arg *arg;
	int i, len, ret = 0;

	for (i = 0; i < tp->nr_args; i++) {
		arg = tp->args + i;
		if (unlikely(arg->dynamic)) {
		if (arg->dynamic) {
			unsigned long val;

			val = get_event_field(arg->code, rec);
			len = process_fetch_insn_bottom(arg->code + 1, val, NULL, NULL);
			code = arg->code;
 retry:
			switch (code->op) {
			case FETCH_OP_TP_ARG:
				val = get_event_field(code, rec);
				break;
			case FETCH_OP_IMM:
				val = code->immediate;
				break;
			case FETCH_OP_COMM:
				val = (unsigned long)current->comm;
				break;
			case FETCH_OP_DATA:
				val = (unsigned long)code->data;
				break;
			case FETCH_NOP_SYMBOL:	/* Ignore a place holder */
				code++;
				goto retry;
			default:
				continue;
			}
			code++;
			len = process_fetch_insn_bottom(code, val, NULL, NULL);
			if (len > 0)
				ret += len;
		}
@@ -369,8 +424,28 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest,
{
	unsigned long val;

 retry:
	switch (code->op) {
	case FETCH_OP_TP_ARG:
		val = get_event_field(code, rec);
	return process_fetch_insn_bottom(code + 1, val, dest, base);
		break;
	case FETCH_OP_IMM:
		val = code->immediate;
		break;
	case FETCH_OP_COMM:
		val = (unsigned long)current->comm;
		break;
	case FETCH_OP_DATA:
		val = (unsigned long)code->data;
		break;
	case FETCH_NOP_SYMBOL:	/* Ignore a place holder */
		code++;
		goto retry;
	default:
		return -EILSEQ;
	}
	code++;
	return process_fetch_insn_bottom(code, val, dest, base);
}
NOKPROBE_SYMBOL(process_fetch_insn)

@@ -845,6 +920,10 @@ static int trace_eprobe_tp_update_arg(struct trace_eprobe *ep, const char *argv[
			trace_probe_log_err(0, BAD_ATTACH_ARG);
	}

	/* Handle symbols "@" */
	if (!ret)
		ret = traceprobe_update_arg(&ep->tp.args[i]);

	return ret;
}

@@ -883,7 +962,7 @@ static int __trace_eprobe_create(int argc, const char *argv[])
	trace_probe_log_set_index(1);
	sys_event = argv[1];
	ret = traceprobe_parse_event_name(&sys_event, &sys_name, buf2, 0);
	if (!sys_event || !sys_name) {
	if (ret || !sys_event || !sys_name) {
		trace_probe_log_err(0, NO_EVENT_INFO);
		goto parse_error;
	}
+4 −3
Original line number Diff line number Diff line
@@ -157,7 +157,7 @@ static void perf_trace_event_unreg(struct perf_event *p_event)
	int i;

	if (--tp_event->perf_refcount > 0)
		goto out;
		return;

	tp_event->class->reg(tp_event, TRACE_REG_PERF_UNREGISTER, NULL);

@@ -176,8 +176,6 @@ static void perf_trace_event_unreg(struct perf_event *p_event)
			perf_trace_buf[i] = NULL;
		}
	}
out:
	trace_event_put_ref(tp_event);
}

static int perf_trace_event_open(struct perf_event *p_event)
@@ -241,6 +239,7 @@ void perf_trace_destroy(struct perf_event *p_event)
	mutex_lock(&event_mutex);
	perf_trace_event_close(p_event);
	perf_trace_event_unreg(p_event);
	trace_event_put_ref(p_event->tp_event);
	mutex_unlock(&event_mutex);
}

@@ -292,6 +291,7 @@ void perf_kprobe_destroy(struct perf_event *p_event)
	mutex_lock(&event_mutex);
	perf_trace_event_close(p_event);
	perf_trace_event_unreg(p_event);
	trace_event_put_ref(p_event->tp_event);
	mutex_unlock(&event_mutex);

	destroy_local_trace_kprobe(p_event->tp_event);
@@ -347,6 +347,7 @@ void perf_uprobe_destroy(struct perf_event *p_event)
	mutex_lock(&event_mutex);
	perf_trace_event_close(p_event);
	perf_trace_event_unreg(p_event);
	trace_event_put_ref(p_event->tp_event);
	mutex_unlock(&event_mutex);
	destroy_local_trace_uprobe(p_event->tp_event);
}
+1 −0
Original line number Diff line number Diff line
@@ -176,6 +176,7 @@ static int trace_define_generic_fields(void)

	__generic_field(int, CPU, FILTER_CPU);
	__generic_field(int, cpu, FILTER_CPU);
	__generic_field(int, common_cpu, FILTER_CPU);
	__generic_field(char *, COMM, FILTER_COMM);
	__generic_field(char *, comm, FILTER_COMM);

+18 −11
Original line number Diff line number Diff line
@@ -283,7 +283,14 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
	int ret = 0;
	int len;

	if (strcmp(arg, "retval") == 0) {
	if (flags & TPARG_FL_TPOINT) {
		if (code->data)
			return -EFAULT;
		code->data = kstrdup(arg, GFP_KERNEL);
		if (!code->data)
			return -ENOMEM;
		code->op = FETCH_OP_TP_ARG;
	} else if (strcmp(arg, "retval") == 0) {
		if (flags & TPARG_FL_RETURN) {
			code->op = FETCH_OP_RETVAL;
		} else {
@@ -307,7 +314,7 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
			}
		} else
			goto inval_var;
	} else if (strcmp(arg, "comm") == 0) {
	} else if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) {
		code->op = FETCH_OP_COMM;
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
	} else if (((flags & TPARG_FL_MASK) ==
@@ -323,13 +330,6 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
		code->op = FETCH_OP_ARG;
		code->param = (unsigned int)param - 1;
#endif
	} else if (flags & TPARG_FL_TPOINT) {
		if (code->data)
			return -EFAULT;
		code->data = kstrdup(arg, GFP_KERNEL);
		if (!code->data)
			return -ENOMEM;
		code->op = FETCH_OP_TP_ARG;
	} else
		goto inval_var;

@@ -384,6 +384,11 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
		break;

	case '%':	/* named register */
		if (flags & TPARG_FL_TPOINT) {
			/* eprobes do not handle registers */
			trace_probe_log_err(offs, BAD_VAR);
			break;
		}
		ret = regs_query_register_offset(arg + 1);
		if (ret >= 0) {
			code->op = FETCH_OP_REG;
@@ -617,9 +622,11 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,

	/*
	 * Since $comm and immediate string can not be dereferenced,
	 * we can find those by strcmp.
	 * we can find those by strcmp. But ignore for eprobes.
	 */
	if (strcmp(arg, "$comm") == 0 || strncmp(arg, "\\\"", 2) == 0) {
	if (!(flags & TPARG_FL_TPOINT) &&
	    (strcmp(arg, "$comm") == 0 || strcmp(arg, "$COMM") == 0 ||
	     strncmp(arg, "\\\"", 2) == 0)) {
		/* The type of $comm must be "string", and not an array. */
		if (parg->count || (t && strcmp(t, "string")))
			goto out;