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 -----BEGIN PGP SIGNATURE----- iIoEABYIADIWIQRRSw7ePDh/lE+zeZMp5XQQmuv6qgUCYwKRrhQccm9zdGVkdEBn b29kbWlzLm9yZwAKCRAp5XQQmuv6qosDAP9WySmPxjoMfR0hbjmnepLy2zJtBbIq ABWR3LDrjvLlYwD9H/wrD+6ctOZtXh5XJc0Vn5z6XEyNtqrVGSse7Lm+sg4= =qb/R -----END PGP SIGNATURE----- Merge tag 'trace-v6.0-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace 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()
This commit is contained in:
commit
7fb312d225
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
val = get_event_field(code, rec);
|
||||
return process_fetch_insn_bottom(code + 1, val, dest, base);
|
||||
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:
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue