Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

tracing: Fix WARN_ON in tracing_buffers_mmap_close

When a process forks, the child process copies the parent's VMAs but the
user_mapped reference count is not incremented. As a result, when both the
parent and child processes exit, tracing_buffers_mmap_close() is called
twice. On the second call, user_mapped is already 0, causing the function to
return -ENODEV and triggering a WARN_ON.

Normally, this isn't an issue as the memory is mapped with VM_DONTCOPY set.
But this is only a hint, and the application can call
madvise(MADVISE_DOFORK) which resets the VM_DONTCOPY flag. When the
application does that, it can trigger this issue on fork.

Fix it by incrementing the user_mapped reference count without re-mapping
the pages in the VMA's open callback.

Cc: stable@vger.kernel.org
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Vincent Donnefort <vdonnefort@google.com>
Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Link: https://patch.msgid.link/20260227025842.1085206-1-wangqing7171@gmail.com
Fixes: cf9f0f7c4c5bb ("tracing: Allow user-space mapping of the ring-buffer")
Reported-by: syzbot+3b5dd2030fe08afdf65d@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=3b5dd2030fe08afdf65d
Tested-by: syzbot+3b5dd2030fe08afdf65d@syzkaller.appspotmail.com
Signed-off-by: Qing Wang <wangqing7171@gmail.com>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>

authored by

Qing Wang and committed by
Steven Rostedt (Google)
e39bb9e0 a5dd6f58

+35
+1
include/linux/ring_buffer.h
··· 248 248 249 249 int ring_buffer_map(struct trace_buffer *buffer, int cpu, 250 250 struct vm_area_struct *vma); 251 + void ring_buffer_map_dup(struct trace_buffer *buffer, int cpu); 251 252 int ring_buffer_unmap(struct trace_buffer *buffer, int cpu); 252 253 int ring_buffer_map_get_reader(struct trace_buffer *buffer, int cpu); 253 254 #endif /* _LINUX_RING_BUFFER_H */
+21
kernel/trace/ring_buffer.c
··· 7310 7310 return err; 7311 7311 } 7312 7312 7313 + /* 7314 + * This is called when a VMA is duplicated (e.g., on fork()) to increment 7315 + * the user_mapped counter without remapping pages. 7316 + */ 7317 + void ring_buffer_map_dup(struct trace_buffer *buffer, int cpu) 7318 + { 7319 + struct ring_buffer_per_cpu *cpu_buffer; 7320 + 7321 + if (WARN_ON(!cpumask_test_cpu(cpu, buffer->cpumask))) 7322 + return; 7323 + 7324 + cpu_buffer = buffer->buffers[cpu]; 7325 + 7326 + guard(mutex)(&cpu_buffer->mapping_lock); 7327 + 7328 + if (cpu_buffer->user_mapped) 7329 + __rb_inc_dec_mapped(cpu_buffer, true); 7330 + else 7331 + WARN(1, "Unexpected buffer stat, it should be mapped"); 7332 + } 7333 + 7313 7334 int ring_buffer_unmap(struct trace_buffer *buffer, int cpu) 7314 7335 { 7315 7336 struct ring_buffer_per_cpu *cpu_buffer;
+13
kernel/trace/trace.c
··· 8213 8213 static inline void put_snapshot_map(struct trace_array *tr) { } 8214 8214 #endif 8215 8215 8216 + /* 8217 + * This is called when a VMA is duplicated (e.g., on fork()) to increment 8218 + * the user_mapped counter without remapping pages. 8219 + */ 8220 + static void tracing_buffers_mmap_open(struct vm_area_struct *vma) 8221 + { 8222 + struct ftrace_buffer_info *info = vma->vm_file->private_data; 8223 + struct trace_iterator *iter = &info->iter; 8224 + 8225 + ring_buffer_map_dup(iter->array_buffer->buffer, iter->cpu_file); 8226 + } 8227 + 8216 8228 static void tracing_buffers_mmap_close(struct vm_area_struct *vma) 8217 8229 { 8218 8230 struct ftrace_buffer_info *info = vma->vm_file->private_data; ··· 8244 8232 } 8245 8233 8246 8234 static const struct vm_operations_struct tracing_buffers_vmops = { 8235 + .open = tracing_buffers_mmap_open, 8247 8236 .close = tracing_buffers_mmap_close, 8248 8237 .may_split = tracing_buffers_may_split, 8249 8238 };