tracing: Fixes for 6.1
Subsystems (5)
Steven Rostedt
maintainercover tracing: Fixes for 6.1
Li Huafei (1):
ftrace: Fix use-after-free for dynamic ftrace_ops
Li Qiang (1):
kprobe: reverse kp->flags when arm_kprobe failed
Masami Hiramatsu (Google) (1):
tracing/fprobe: Fix to check whether fprobe is registered correctly
Rafael Mendonca (1):
fprobe: Check rethook_alloc() return in rethook initialization
Shang XiaoJing (1):
tracing: kprobe: Fix memory leak in test_gen_kprobe/kretprobe_cmd()
Steven Rostedt (Google) (1):
ring-buffer: Check for NULL cpu_buffer in ring_buffer_wake_waiters()
----
kernel/kprobes.c | 5 ++++- kernel/trace/fprobe.c | 5 ++++- kernel/trace/ftrace.c | 16 +++------------- kernel/trace/kprobe_event_gen_test.c | 18 +++++++----------- kernel/trace/ring_buffer.c | 11 +++++++++++ 5 files changed, 29 insertions(+), 26 deletions(-)
Steven Rostedt
maintainerPATCH 1/6 ring-buffer: Check for NULL cpu_buffer in ring_buffer_wake_waiters()
From: "Steven Rostedt (Google)" <[email protected]>
On some machines the number of listed CPUs may be bigger than the actual CPUs that exist. The tracing subsystem allocates a per_cpu directory with access to the per CPU ring buffer via a cpuX file. But to save space, the ring buffer will only allocate buffers for online CPUs, even though the CPU array will be as big as the nr_cpu_ids.
With the addition of waking waiters on the ring buffer when closing the file, the ring_buffer_wake_waiters() now needs to make sure that the buffer is allocated (with the irq_work allocated with it) before trying to wake waiters, as it will cause a NULL pointer dereference.
While debugging this, I added a NULL check for the buffer itself (which is OK to do), and also NULL pointer checks against buffer->buffers (which is not fine, and will WARN) as well as making sure the CPU number passed in is within the nr_cpu_ids (which is also not fine if it isn't).
Cc: [email protected]
Cc: Steven Noonan <[email protected]>
Bugzilla: https://bugzilla.opensuse.org/show_bug.cgi?id=1204705
--- kernel/trace/ring_buffer.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
kernel/trace/ring_buffer.c +11 -0
@@ -937,6 +937,9 @@ void ring_buffer_wake_waiters(struct trace_buffer *buffer, int cpu)struct ring_buffer_per_cpu *cpu_buffer;struct rb_irq_work *rbwork; +if (!buffer) +return ; +if (cpu ==RING_BUFFER_ALL_CPUS ) {/* Wake up individual ones too. One level recursion */
@@ -945,7 +948,15 @@ void ring_buffer_wake_waiters(struct trace_buffer *buffer, int cpu) rbwork = &buffer->irq_work; }else { +if (WARN_ON_ONCE (!buffer->buffers)) +return ; +if (WARN_ON_ONCE (cpu >= nr_cpu_ids)) +return ; + cpu_buffer = buffer->buffers[cpu]; +/* The CPU buffer may not have been initialized yet */ +if (!cpu_buffer) +return ; rbwork = &cpu_buffer->irq_work; }
Signature · 1 line
2.35.1
Steven Rostedt
maintainerPATCH 2/6 ftrace: Fix use-after-free for dynamic ftrace_ops
In reply to Steven RostedtFrom: Li Huafei <[email protected]>
KASAN reported a use-after-free with ftrace ops [1]. It was found from vmcore that perf had registered two ops with the same content successively, both dynamic. After unregistering the second ops, a use-after-free occurred.
In ftrace_shutdown(), when the second ops is unregistered, the FTRACE_UPDATE_CALLS command is not set because there is another enabled ops with the same content. Also, both ops are dynamic and the ftrace callback function is ftrace_ops_list_func, so the FTRACE_UPDATE_TRACE_FUNC command will not be set. Eventually the value of 'command' will be 0 and ftrace_shutdown() will skip the rcu synchronization.
However, ftrace may be activated. When the ops is released, another CPU may be accessing the ops. Add the missing synchronization to fix this problem.
[1]
BUG: KASAN: use-after-free in __ftrace_ops_list_func kernel/trace/ftrace.c:7020 [inline] BUG: KASAN: use-after-free in ftrace_ops_list_func+0x2b0/0x31c kernel/trace/ftrace.c:7049 Read of size 8 at addr ffff56551965bbc8 by task syz-executor.2/14468 CPU: 1 PID: 14468 Comm: syz-executor.2 Not tainted 5.10.0 #7 Hardware name: linux,dummy-virt (DT) Call trace: dump_backtrace+0x0/0x40c arch/arm64/kernel/stacktrace.c:132 show_stack+0x30/0x40 arch/arm64/kernel/stacktrace.c:196 __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0x1b4/0x248 lib/dump_stack.c:118 print_address_description.constprop.0+0x28/0x48c mm/kasan/report.c:387 __kasan_report mm/kasan/report.c:547 [inline] kasan_report+0x118/0x210 mm/kasan/report.c:564 check_memory_region_inline mm/kasan/generic.c:187 [inline] __asan_load8+0x98/0xc0 mm/kasan/generic.c:253 __ftrace_ops_list_func kernel/trace/ftrace.c:7020 [inline] ftrace_ops_list_func+0x2b0/0x31c kernel/trace/ftrace.c:7049 ftrace_graph_call+0x0/0x4 __might_sleep+0x8/0x100 include/linux/perf_event.h:1170 __might_fault mm/memory.c:5183 [inline] __might_fault+0x58/0x70 mm/memory.c:5171 do_strncpy_from_user lib/strncpy_from_user.c:41 [inline] strncpy_from_user+0x1f4/0x4b0 lib/strncpy_from_user.c:139 getname_flags+0xb0/0x31c fs/namei.c:149 getname+0x2c/0x40 fs/namei.c:209 [...] Allocated by task 14445: kasan_save_stack+0x24/0x50 mm/kasan/common.c:48 kasan_set_track mm/kasan/common.c:56 [inline] __kasan_kmalloc mm/kasan/common.c:479 [inline] __kasan_kmalloc.constprop.0+0x110/0x13c mm/kasan/common.c:449 kasan_kmalloc+0xc/0x14 mm/kasan/common.c:493 kmem_cache_alloc_trace+0x440/0x924 mm/slub.c:2950 kmalloc include/linux/slab.h:563 [inline] kzalloc include/linux/slab.h:675 [inline] perf_event_alloc.part.0+0xb4/0x1350 kernel/events/core.c:11230 perf_event_alloc kernel/events/core.c:11733 [inline] __do_sys_perf_event_open kernel/events/core.c:11831 [inline] __se_sys_perf_event_open+0x550/0x15f4 kernel/events/core.c:11723 __arm64_sys_perf_event_open+0x6c/0x80 kernel/events/core.c:11723 [...] Freed by task 14445: kasan_save_stack+0x24/0x50 mm/kasan/common.c:48 kasan_set_track+0x24/0x34 mm/kasan/common.c:56 kasan_set_free_info+0x20/0x40 mm/kasan/generic.c:358 __kasan_slab_free.part.0+0x11c/0x1b0 mm/kasan/common.c:437 __kasan_slab_free mm/kasan/common.c:445 [inline] kasan_slab_free+0x2c/0x40 mm/kasan/common.c:446 slab_free_hook mm/slub.c:1569 [inline] slab_free_freelist_hook mm/slub.c:1608 [inline] slab_free mm/slub.c:3179 [inline] kfree+0x12c/0xc10 mm/slub.c:4176 perf_event_alloc.part.0+0xa0c/0x1350 kernel/events/core.c:11434 perf_event_alloc kernel/events/core.c:11733 [inline] __do_sys_perf_event_open kernel/events/core.c:11831 [inline] __se_sys_perf_event_open+0x550/0x15f4 kernel/events/core.c:11723 [...]
--- kernel/trace/ftrace.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-)
kernel/trace/ftrace.c +3 -13
@@ -3028,18 +3028,8 @@ int ftrace_shutdown(struct ftrace_ops *ops, int command) command |=FTRACE_UPDATE_TRACE_FUNC ; } -if (!command || !ftrace_enabled) { -/* -* If these are dynamic or per_cpu ops, they still -* need their data freed. Since, function tracing is -* not currently active, we can just free them -* without synchronizing all CPUs. -*/ -if (ops->flags &FTRACE_OPS_FL_DYNAMIC ) - goto free_ops; - -return 0 ; - } +if (!command || !ftrace_enabled) + goto out;/* * If the ops uses a trampoline, then it needs to be
@@ -3076,6 +3066,7 @@ int ftrace_shutdown(struct ftrace_ops *ops, int command) removed_ops =NULL ; ops->flags &= ~FTRACE_OPS_FL_REMOVING ; +out:/* * Dynamic ops may be freed, we must make sure that all * callers are done before leaving this function.
@@ -3103,7 +3094,6 @@ int ftrace_shutdown(struct ftrace_ops *ops, int command)if (IS_ENABLED (CONFIG_PREEMPTION ))synchronize_rcu_tasks (); - free_ops:ftrace_trampoline_free (ops); }
Signature · 1 line
2.35.1
Steven Rostedt
maintainerPATCH 3/6 kprobe: reverse kp->flags when arm_kprobe failed
In reply to Steven RostedtFrom: Li Qiang <[email protected]>
In aggregate kprobe case, when arm_kprobe failed, we need set the kp->flags with KPROBE_FLAG_DISABLED again. If not, the 'kp' kprobe will been considered as enabled but it actually not enabled.
--- kernel/kprobes.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
kernel/kprobes.c +4 -1
@@ -2429,8 +2429,11 @@ int enable_kprobe(struct kprobe *kp)if (!kprobes_all_disarmed &&kprobe_disabled (p)) { p->flags &= ~KPROBE_FLAG_DISABLED ; ret =arm_kprobe (p); -if (ret) +if (ret) { p->flags |=KPROBE_FLAG_DISABLED ; +if (p != kp) + kp->flags |=KPROBE_FLAG_DISABLED ; + } } out:mutex_unlock (&kprobe_mutex);
Signature · 1 line
2.35.1
Steven Rostedt
maintainerPATCH 4/6 fprobe: Check rethook_alloc() return in rethook initialization
In reply to Steven RostedtFrom: Rafael Mendonca <[email protected]>
Check if fp->rethook succeeded to be allocated. Otherwise, if rethook_alloc() fails, then we end up dereferencing a NULL pointer in rethook_add_node().
--- kernel/trace/fprobe.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/trace/fprobe.c +2 -0
@@ -141,6 +141,8 @@ static int fprobe_init_rethook(struct fprobe *fp, int num)return -E2BIG ; fp->rethook =rethook_alloc ((void *)fp, fprobe_exit_handler); +if (!fp->rethook) +return -ENOMEM ;for (i =0 ; i < size; i++) {struct fprobe_rethook_node *node;
Signature · 1 line
2.35.1
Steven Rostedt
maintainerPATCH 5/6 tracing/fprobe: Fix to check whether fprobe is registered correctly
In reply to Steven RostedtFrom: "Masami Hiramatsu (Google)" <[email protected]>
Since commit ab51e15d535e ("fprobe: Introduce FPROBE_FL_KPROBE_SHARED flag for fprobe") introduced fprobe_kprobe_handler() for fprobe::ops::func, unregister_fprobe() fails to unregister the registered if user specifies FPROBE_FL_KPROBE_SHARED flag.
Moreover, __register_ftrace_function() is possible to change the ftrace_ops::func, thus we have to check fprobe::ops::saved_func instead.
To check it correctly, it should confirm the fprobe::ops::saved_func is either fprobe_handler() or fprobe_kprobe_handler().
--- kernel/trace/fprobe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/trace/fprobe.c +2 -1
@@ -303,7 +303,8 @@ int unregister_fprobe(struct fprobe *fp) {int ret; -if (!fp || fp->ops.func != fprobe_handler) +if (!fp || (fp->ops.saved_func != fprobe_handler && + fp->ops.saved_func != fprobe_kprobe_handler))return -EINVAL ;/*
Signature · 1 line
2.35.1
Steven Rostedt
maintainerPATCH 6/6 tracing: kprobe: Fix memory leak in test_gen_kprobe/kretprobe_cmd()
In reply to Steven RostedtFrom: Shang XiaoJing <[email protected]>
test_gen_kprobe_cmd() only free buf in fail path, hence buf will leak when there is no failure. Move kfree(buf) from fail path to common path to prevent the memleak. The same reason and solution in test_gen_kretprobe_cmd().
unreferenced object 0xffff888143b14000 (size 2048):
comm "insmod", pid 52490, jiffies 4301890980 (age 40.553s) hex dump (first 32 bytes):
70 3a 6b 70 72 6f 62 65 73 2f 67 65 6e 5f 6b 70 p:kprobes/gen_kp
72 6f 62 65 5f 74 65 73 74 20 64 6f 5f 73 79 73 robe_test do_sys
backtrace:
[<000000006d7b836b>] kmalloc_trace+0x27/0xa0
[<0000000009528b5b>] 0xffffffffa059006f
[<000000008408b580>] do_one_initcall+0x87/0x2a0
[<00000000c4980a7e>] do_init_module+0xdf/0x320
[<00000000d775aad0>] load_module+0x3006/0x3390
[<00000000e9a74b80>] __do_sys_finit_module+0x113/0x1b0
[<000000003726480d>] do_syscall_64+0x35/0x80
[<000000003441e93b>] entry_SYSCALL_64_after_hwframe+0x46/0xb0--- kernel/trace/kprobe_event_gen_test.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-)
kernel/trace/kprobe_event_gen_test.c +7 -11
@@ -100,20 +100,20 @@ static int __init test_gen_kprobe_cmd(void)KPROBE_GEN_TEST_FUNC , KPROBE_GEN_TEST_ARG0, KPROBE_GEN_TEST_ARG1);if (ret) - goto free; + goto out;/* Use kprobe_event_add_fields to add the rest of the fields */ ret =kprobe_event_add_fields (&cmd,KPROBE_GEN_TEST_ARG2 ,KPROBE_GEN_TEST_ARG3 );if (ret) - goto free; + goto out;/* * This actually creates the event. */ ret =kprobe_event_gen_cmd_end (&cmd);if (ret) - goto free; + goto out;/* * Now get the gen_kprobe_test event file. We need to prevent
@@ -136,13 +136,11 @@ static int __init test_gen_kprobe_cmd(void) goto delete; } out: +kfree (buf);return ret; delete:/* We got an error after creating the event, delete it */ ret =kprobe_event_delete ("gen_kprobe_test" ); - free: -kfree (buf); - goto out; }
@@ -170,14 +168,14 @@ static int __init test_gen_kretprobe_cmd(void)KPROBE_GEN_TEST_FUNC , "$retval");if (ret) - goto free; + goto out;/* * This actually creates the event. */ ret =kretprobe_event_gen_cmd_end (&cmd);if (ret) - goto free; + goto out;/* * Now get the gen_kretprobe_test event file. We need to
@@ -201,13 +199,11 @@ static int __init test_gen_kretprobe_cmd(void) goto delete; } out: +kfree (buf);return ret; delete:/* We got an error after creating the event, delete it */ ret =kprobe_event_delete ("gen_kretprobe_test" ); - free: -kfree (buf); - goto out; }
Signature · 1 line
2.35.1