tracing: Fixes for v6.17
Subsystems (5)
cover tracing: Fixes for v6.17
tracing fixes for v6.17-rc2:
- Fix rtla and latency tooling pkg-config errors
If libtraceevent and libtracefs is installed, but their corresponding '.pc' files are not installed, it reports that the libraries are missing and confuses the developer. Instead, report that the pkg-config files are missing and should be installed.
- Fix overflow bug of the parser in trace_get_user()
trace_get_user() uses the parsing functions to parse the user space strings. If the parser fails due to incorrect processing, it doesn't terminate the buffer with a nul byte. Add a "failed" flag to the parser that gets set when parsing fails and is used to know if the buffer is fine to use or not.
- Remove a semicolon that was at an end of a comment line
- Fix register_ftrace_graph() to unregister the pm notifier on error
The register_ftrace_graph() registers a pm notifier but there's an error path that can exit the function without unregistering it. Since the function returns an error, it will never be unregistered.
- Allocate and copy ftrace hash for reader of ftrace filter files
When the set_ftrace_filter or set_ftrace_notrace files are open for read, an iterator is created and sets its hash pointer to the associated hash that represents filtering or notrace filtering to it. The issue is that the hash it points to can change while the iteration is happening. All the locking used to access the tracer's hashes are released which means those hashes can change or even be freed. Using the hash pointed to by the iterator can cause UAF bugs or similar.
Have the read of these files allocate and copy the corresponding hashes and use that as that will keep them the same while the iterator is open. This also simplifies the code as opening it for write already does an allocate and copy, and now that the read is doing the same, there's no need to check which why it was opened on the release of the file, and the iterator hash can always be freed.
Liao Yuanhong (1):
ring-buffer: Remove redundant semicolons
Pu Lehui (1):
tracing: Limit access to parser->buffer when trace_get_user failed
Steven Rostedt (1):
ftrace: Also allocate and copy hash for reading of filter files
Tao Chen (2):
tools/latency-collector: Check pkg-config install
rtla: Check pkg-config install
Ye Weihua (1):
trace/fgraph: Fix the warning caused by missing unregister notifier
----
kernel/trace/fgraph.c | 1 + kernel/trace/ftrace.c | 16 +++++++--------- kernel/trace/ring_buffer.c | 2 +- kernel/trace/trace.c | 18 ++++++++++++------ kernel/trace/trace.h | 8 +++++++- tools/tracing/latency/Makefile.config | 8 ++++++++ tools/tracing/rtla/Makefile.config | 8 ++++++++ 7 files changed, 44 insertions(+), 17 deletions(-)
PATCH 1/6 tools/latency-collector: Check pkg-config install
From: Tao Chen <[email protected]>
The tool pkg-config used to check libtraceevent and libtracefs, if not installed, it will report the libs not found, even though they have already been installed.
Before:
libtraceevent is missing. Please install libtraceevent-dev/libtraceevent-devel libtracefs is missing. Please install libtracefs-dev/libtracefs-devel
After:
Makefile.config:10: *** Error: pkg-config needed by libtraceevent/libtracefs is missing on this system, please install it.
--- tools/tracing/latency/Makefile.config | 8 ++++++++ 1 file changed, 8 insertions(+)
tools/tracing/latency/Makefile.config +8 -0
@@ -1,7 +1,15 @@ # SPDX-License-Identifier: GPL-2.0-only +include $(srctree)/tools/scripts/utilities.mak + STOP_ERROR := +ifndef ($(NO_LIBTRACEEVENT),1) + ifeq ($(call get-executable,$(PKG_CONFIG)),) + $(error Error: $(PKG_CONFIG) needed by libtraceevent/libtracefs is missing on this system, please install it) + endif +endif + define lib_setup $(eval LIB_INCLUDES += $(shell sh -c "$(PKG_CONFIG) --cflags lib$(1)")) $(eval LDFLAGS += $(shell sh -c "$(PKG_CONFIG) --libs-only-L lib$(1)"))
Signature · 1 line
2.50.1
PATCH 2/6 rtla: Check pkg-config install
In reply to Steven RostedtFrom: Tao Chen <[email protected]>
The tool pkg-config used to check libtraceevent and libtracefs, if not installed, it will report the libs not found, even though they have already been installed.
Before:
libtraceevent is missing. Please install libtraceevent-dev/libtraceevent-devel libtracefs is missing. Please install libtracefs-dev/libtracefs-devel
After:
Makefile.config:10: *** Error: pkg-config needed by libtraceevent/libtracefs is missing on this system, please install it.
--- tools/tracing/rtla/Makefile.config | 8 ++++++++ 1 file changed, 8 insertions(+)
tools/tracing/rtla/Makefile.config +8 -0
@@ -1,10 +1,18 @@ # SPDX-License-Identifier: GPL-2.0-only +include $(srctree)/tools/scripts/utilities.mak + STOP_ERROR := LIBTRACEEVENT_MIN_VERSION = 1.5 LIBTRACEFS_MIN_VERSION = 1.6 +ifndef ($(NO_LIBTRACEEVENT),1) + ifeq ($(call get-executable,$(PKG_CONFIG)),) + $(error Error: $(PKG_CONFIG) needed by libtraceevent/libtracefs is missing on this system, please install it) + endif +endif + define lib_setup $(eval LIB_INCLUDES += $(shell sh -c "$(PKG_CONFIG) --cflags lib$(1)")) $(eval LDFLAGS += $(shell sh -c "$(PKG_CONFIG) --libs-only-L lib$(1)"))
Signature · 1 line
2.50.1
PATCH 3/6 tracing: Limit access to parser->buffer when trace_get_user failed
In reply to Steven RostedtFrom: Pu Lehui <[email protected]>
When the length of the string written to set_ftrace_filter exceeds FTRACE_BUFF_MAX, the following KASAN alarm will be triggered:
BUG: KASAN: slab-out-of-bounds in strsep+0x18c/0x1b0 Read of size 1 at addr ffff0000d00bd5ba by task ash/165 CPU: 1 UID: 0 PID: 165 Comm: ash Not tainted 6.16.0-g6bcdbd62bd56-dirty Hardware name: linux,dummy-virt (DT) Call trace: show_stack+0x34/0x50 (C) dump_stack_lvl+0xa0/0x158 print_address_description.constprop.0+0x88/0x398 print_report+0xb0/0x280 kasan_report+0xa4/0xf0 __asan_report_load1_noabort+0x20/0x30 strsep+0x18c/0x1b0 ftrace_process_regex.isra.0+0x100/0x2d8 ftrace_regex_release+0x484/0x618 __fput+0x364/0xa58 ____fput+0x28/0x40 task_work_run+0x154/0x278 do_notify_resume+0x1f0/0x220 el0_svc+0xec/0xf0 el0t_64_sync_handler+0xa0/0xe8 el0t_64_sync+0x1ac/0x1b0
The reason is that trace_get_user will fail when processing a string longer than FTRACE_BUFF_MAX, but not set the end of parser->buffer to 0. Then an OOB access will be triggered in ftrace_regex_release-> ftrace_process_regex->strsep->strpbrk. We can solve this problem by limiting access to parser->buffer when trace_get_user failed.
--- kernel/trace/trace.c | 18 ++++++++++++------ kernel/trace/trace.h | 8 +++++++- 2 files changed, 19 insertions(+), 7 deletions(-)
kernel/trace/trace.c +12 -6
@@ -1816,7 +1816,7 @@ int trace_get_user(struct trace_parser *parser, const char __user *ubuf, ret =get_user (ch, ubuf++);if (ret) -return ret; + goto fail; read++; cnt--;
@@ -1830,7 +1830,7 @@ int trace_get_user(struct trace_parser *parser, const char __user *ubuf,while (cnt &&isspace (ch)) { ret =get_user (ch, ubuf++);if (ret) -return ret; + goto fail; read++; cnt--; }
@@ -1848,12 +1848,14 @@ int trace_get_user(struct trace_parser *parser, const char __user *ubuf,while (cnt && !isspace (ch) && ch) {if (parser->idx < parser->size -1 ) parser->buffer[parser->idx++] = ch; -else -return -EINVAL ; +else { + ret = -EINVAL ; + goto fail; + } ret =get_user (ch, ubuf++);if (ret) -return ret; + goto fail; read++; cnt--; }
@@ -1868,11 +1870,15 @@ int trace_get_user(struct trace_parser *parser, const char __user *ubuf,/* Make sure the parsed string always terminates with '\0'. */ parser->buffer[parser->idx] =0 ; }else { -return -EINVAL ; + ret = -EINVAL ; + goto fail; } *ppos += read;return read; +fail: +trace_parser_fail (parser); +return ret; }/* TODO add a seq_buf_to_buffer() */
kernel/trace/trace.h +7 -1
@@ -1292,6 +1292,7 @@ bool ftrace_event_is_function(struct trace_event_call *call);*/ struct trace_parser {bool cont; +bool fail;char *buffer;unsigned idx;unsigned size;
@@ -1299,7 +1300,7 @@ struct trace_parser {static inline bool trace_parser_loaded (struct trace_parser *parser) { -return (parser->idx !=0 ); +return !parser->fail && parser->idx !=0 ; }static inline bool trace_parser_cont (struct trace_parser *parser)
@@ -1313,6 +1314,11 @@ static inline void trace_parser_clear(struct trace_parser *parser) parser->idx =0 ; } +static inline void trace_parser_fail (struct trace_parser *parser) +{ + parser->fail = true; +} +extern int trace_parser_get_init (struct trace_parser *parser,int size);extern void trace_parser_put (struct trace_parser *parser); extern int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
Signature · 1 line
2.50.1
PATCH 4/6 ring-buffer: Remove redundant semicolons
In reply to Steven RostedtFrom: Liao Yuanhong <[email protected]>
Remove unnecessary semicolons.
--- kernel/trace/ring_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/ring_buffer.c +1 -1
@@ -7666,7 +7666,7 @@ static __init int test_ringbuffer(void) rb_test_started = true;set_current_state (TASK_INTERRUPTIBLE ); -/* Just run for 10 seconds */ ; +/* Just run for 10 seconds */ schedule_timeout (10 *HZ );kthread_stop (rb_hammer);
Signature · 1 line
2.50.1
PATCH 5/6 trace/fgraph: Fix the warning caused by missing unregister notifier
In reply to Steven RostedtFrom: Ye Weihua <[email protected]>
This warning was triggered during testing on v6.16:
notifier callback ftrace_suspend_notifier_call already registered
WARNING: CPU: 2 PID: 86 at kernel/notifier.c:23 notifier_chain_register+0x44/0xb0 ... Call Trace: <TASK> blocking_notifier_chain_register+0x34/0x60 register_ftrace_graph+0x330/0x410 ftrace_profile_write+0x1e9/0x340 vfs_write+0xf8/0x420 ? filp_flush+0x8a/0xa0 ? filp_close+0x1f/0x30 ? do_dup2+0xaf/0x160 ksys_write+0x65/0xe0 do_syscall_64+0xa4/0x260 entry_SYSCALL_64_after_hwframe+0x77/0x7f
When writing to the function_profile_enabled interface, the notifier was not unregistered after start_graph_tracing failed, causing a warning the next time function_profile_enabled was written.
Fixed by adding unregister_pm_notifier in the exception path.
--- kernel/trace/fgraph.c | 1 + 1 file changed, 1 insertion(+)
kernel/trace/fgraph.c +1 -0
@@ -1397,6 +1397,7 @@ int register_ftrace_graph(struct fgraph_ops *gops) ftrace_graph_active--; gops->saved_func =NULL ;fgraph_lru_release_index (i); +unregister_pm_notifier (&ftrace_suspend_notifier); }return ret; }
Signature · 1 line
2.50.1
PATCH 6/6 ftrace: Also allocate and copy hash for reading of filter files
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
Currently the reader of set_ftrace_filter and set_ftrace_notrace just adds the pointer to the global tracer hash to its iterator. Unlike the writer that allocates a copy of the hash, the reader keeps the pointer to the filter hashes. This is problematic because this pointer is static across function calls that release the locks that can update the global tracer hashes. This can cause UAF and similar bugs.
Allocate and copy the hash for reading the filter files like it is done for the writers. This not only fixes UAF bugs, but also makes the code a bit simpler as it doesn't have to differentiate when to free the iterator's hash between writers and readers.
--- kernel/trace/ftrace.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-)
kernel/trace/ftrace.c +7 -9
@@ -4661,13 +4661,14 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag, }else { iter->hash =alloc_and_copy_ftrace_hash (size_bits, hash); } + }else { + iter->hash =alloc_and_copy_ftrace_hash (hash->size_bits, hash); + } -if (!iter->hash) { -trace_parser_put (&iter->parser); - goto out_unlock; - } - }else - iter->hash = hash; +if (!iter->hash) { +trace_parser_put (&iter->parser); + goto out_unlock; + } ret =0 ;
@@ -6543,9 +6544,6 @@ int ftrace_regex_release(struct inode *inode, struct file *file)ftrace_hash_move_and_update_ops (iter->ops, orig_hash, iter->hash, filter_hash);mutex_unlock (&ftrace_lock); - }else { -/* For read only, the hash is the ops hash */ - iter->hash =NULL ; }mutex_unlock (&iter->ops->func_hash->regex_lock);
Signature · 1 line
2.50.1