Skip to content

Commit 795301d

Browse files
committed
tracing: Have type enum modifications copy the strings
When an enum is used in the visible parts of a trace event that is exported to user space, the user space applications like perf and trace-cmd do not have a way to know what the value of the enum is. To solve this, at boot up (or module load) the printk formats are modified to replace the enum with their numeric value in the string output. Array fields of the event are defined by [<nr-elements>] in the type portion of the format file so that the user space parsers can correctly parse the array into the appropriate size chunks. But in some trace events, an enum is used in defining the size of the array, which once again breaks the parsing of user space tooling. This was solved the same way as the print formats were, but it modified the type strings of the trace event. This caused crashes in some architectures because, as supposed to the print string, is a const string value. This was not detected on x86, as it appears that const strings are still writable (at least in boot up), but other architectures this is not the case, and writing to a const string will cause a kernel fault. To fix this, use kstrdup() to copy the type before modifying it. If the trace event is for the core kernel there's no need to free it because the string will be in use for the life of the machine being on line. For modules, create a link list to store all the strings being allocated for modules and when the module is removed, free them. Link: https://lore.kernel.org/all/yt9dr1706b4i.fsf@linux.ibm.com/ Link: https://lkml.kernel.org/r/20220318153432.3984b871@gandalf.local.home Tested-by: Marc Zyngier <maz@kernel.org> Tested-by: Sven Schnelle <svens@linux.ibm.com> Reported-by: Sven Schnelle <svens@linux.ibm.com> Fixes: b3bc854 ("tracing: Have TRACE_DEFINE_ENUM affect trace event types as well") Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
1 parent 089331d commit 795301d

1 file changed

Lines changed: 61 additions & 1 deletion

File tree

kernel/trace/trace_events.c

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ static LIST_HEAD(ftrace_generic_fields);
4040
static LIST_HEAD(ftrace_common_fields);
4141
static bool eventdir_initialized;
4242

43+
static LIST_HEAD(module_strings);
44+
45+
struct module_string {
46+
struct list_head next;
47+
struct module *module;
48+
char *str;
49+
};
50+
4351
#define GFP_TRACE (GFP_KERNEL | __GFP_ZERO)
4452

4553
static struct kmem_cache *field_cachep;
@@ -2633,14 +2641,40 @@ static void update_event_printk(struct trace_event_call *call,
26332641
}
26342642
}
26352643

2644+
static void add_str_to_module(struct module *module, char *str)
2645+
{
2646+
struct module_string *modstr;
2647+
2648+
modstr = kmalloc(sizeof(*modstr), GFP_KERNEL);
2649+
2650+
/*
2651+
* If we failed to allocate memory here, then we'll just
2652+
* let the str memory leak when the module is removed.
2653+
* If this fails to allocate, there's worse problems than
2654+
* a leaked string on module removal.
2655+
*/
2656+
if (WARN_ON_ONCE(!modstr))
2657+
return;
2658+
2659+
modstr->module = module;
2660+
modstr->str = str;
2661+
2662+
list_add(&modstr->next, &module_strings);
2663+
}
2664+
26362665
static void update_event_fields(struct trace_event_call *call,
26372666
struct trace_eval_map *map)
26382667
{
26392668
struct ftrace_event_field *field;
26402669
struct list_head *head;
26412670
char *ptr;
2671+
char *str;
26422672
int len = strlen(map->eval_string);
26432673

2674+
/* Dynamic events should never have field maps */
2675+
if (WARN_ON_ONCE(call->flags & TRACE_EVENT_FL_DYNAMIC))
2676+
return;
2677+
26442678
head = trace_get_fields(call);
26452679
list_for_each_entry(field, head, link) {
26462680
ptr = strchr(field->type, '[');
@@ -2654,9 +2688,26 @@ static void update_event_fields(struct trace_event_call *call,
26542688
if (strncmp(map->eval_string, ptr, len) != 0)
26552689
continue;
26562690

2691+
str = kstrdup(field->type, GFP_KERNEL);
2692+
if (WARN_ON_ONCE(!str))
2693+
return;
2694+
ptr = str + (ptr - field->type);
26572695
ptr = eval_replace(ptr, map, len);
26582696
/* enum/sizeof string smaller than value */
2659-
WARN_ON_ONCE(!ptr);
2697+
if (WARN_ON_ONCE(!ptr)) {
2698+
kfree(str);
2699+
continue;
2700+
}
2701+
2702+
/*
2703+
* If the event is part of a module, then we need to free the string
2704+
* when the module is removed. Otherwise, it will stay allocated
2705+
* until a reboot.
2706+
*/
2707+
if (call->module)
2708+
add_str_to_module(call->module, str);
2709+
2710+
field->type = str;
26602711
}
26612712
}
26622713

@@ -2883,6 +2934,7 @@ static void trace_module_add_events(struct module *mod)
28832934
static void trace_module_remove_events(struct module *mod)
28842935
{
28852936
struct trace_event_call *call, *p;
2937+
struct module_string *modstr, *m;
28862938

28872939
down_write(&trace_event_sem);
28882940
list_for_each_entry_safe(call, p, &ftrace_events, list) {
@@ -2891,6 +2943,14 @@ static void trace_module_remove_events(struct module *mod)
28912943
if (call->module == mod)
28922944
__trace_remove_event_call(call);
28932945
}
2946+
/* Check for any strings allocade for this module */
2947+
list_for_each_entry_safe(modstr, m, &module_strings, next) {
2948+
if (modstr->module != mod)
2949+
continue;
2950+
list_del(&modstr->next);
2951+
kfree(modstr->str);
2952+
kfree(modstr);
2953+
}
28942954
up_write(&trace_event_sem);
28952955

28962956
/*

0 commit comments

Comments
 (0)