tracing: Show contents of syscall trace event user space fields
Subsystems (3)
v4 cover tracing: Show contents of syscall trace event user space fields
As of commit 654ced4a1377 ("tracing: Introduce tracepoint_is_faultable()") system call trace events allow faulting in user space memory. Have some of the system call trace events take advantage of this.
Also, commit 64cf7d058a00 ("tracing: Have trace_marker use per-cpu data to read user space") added the method to use per CPU buffers to read data from user space in critical sections. Instead of recreating that code, make that code generic so that the system calls can utilize it as well.
Update the system call trace events to read user space for various system calls (like openat, execve, etc).
A new file is created in the tracefs directory (and also per instance) that allows the user to shorten the amount copied from user space. It can be completely disabled if set to zero (it will only display "" or (, ...) but no copying from user space will be performed). The max size to copy is hard coded to 165, which should be enough for this purpose. The default size is 63 bytes.
This allows the output to look like this:
sys_access(filename: 0x7f8c55368470 "/etc/ld.so.preload", mode: 4)
sys_execve(filename: 0x564ebcf5a6b8 "/usr/bin/emacs", argv: 0x7fff357c0300, envp: 0x564ebc4a4820) sys_write(fd: 1, buf: 0x56430f353be0 (2f:72:6f:6f:74:0a) "/root.", count: 6)
sys_sethostname(name: 0x5584310eb2a0 "debian", len: 6)
sys_renameat2(olddfd: 0xffffff9c, oldname: 0x7ffe02facdff "/tmp/x", newdfd: 0xffffff9c, newname: 0x7ffe02face06 "/tmp/y", flags: 1)
Perf system call logic is also updated to take advantage of this work.
The openat system call was updated to show the flags as well:
sys_openat(dfd: 18446744073709551516, filename: 140733603151330 "/tmp/x", flags: O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, mode: 0666)
Changes since v3: https://lore.kernel.org/linux-trace-kernel/[email protected]/
- Fixed return value of perf_sysenter_enable() and perf_sysexit_enable() to not return undefined value (kernel test robot)
- Fix use of ret in perf_sysenter_enable. (kernel test robot)
- Fixed up the code to determine if it's a syscall event or not when CONFIG_FTRACE_SYSCALLS is not defined (kernel test robot)
Steven Rostedt (13):
tracing: Make trace_user_fault_read() exposed to rest of tracing
tracing: Have syscall trace events read user space string
perf: tracing: Simplify perf_sysenter_enable/disable() with guards
perf: tracing: Have perf system calls read user space
tracing: Have system call events record user array data
tracing: Display some syscall arrays as strings
tracing: Allow syscall trace events to read more than one user parameter
tracing: Add a config and syscall_user_buf_size file to limit amount written
tracing: Show printable characters in syscall arrays
tracing: Add trace_seq_pop() and seq_buf_pop()
tracing: Add parsing of flags to the sys_enter_openat trace event
tracing: Check for printable characters when printing field dyn strings
tracing: Have persistent ring buffer print syscalls normally
----
Documentation/trace/ftrace.rst | 8 + include/linux/seq_buf.h | 17 + include/linux/trace_seq.h | 13 + include/trace/syscall.h | 8 +- kernel/trace/Kconfig | 14 + kernel/trace/trace.c | 327 ++++++++++++--- kernel/trace/trace.h | 20 + kernel/trace/trace_output.c | 27 +- kernel/trace/trace_syscalls.c | 889 ++++++++++++++++++++++++++++++++++++++--- 9 files changed, 1204 insertions(+), 119 deletions(-)
PATCH v4 1/13 tracing: Make trace_user_fault_read() exposed to rest of tracing
From: Steven Rostedt <[email protected]>
The write to the trace_marker file is a critical section where it cannot take locks nor allocate memory. To read from user space, it allocates a per CPU buffer when the trace_marker file is opened, and then when the write system call is performed, it uses the following method to read from user space:
preempt_disable();
buffer = per_cpu_ptr(cpu_buffers, cpu);
do {
cnt = nr_context_switches_cpu();
migrate_disable();
preempt_enable();
ret = copy_from_user(buffer, ptr, len);
preempt_disable();
migrate_enable();
} while (!ret && cnt != nr_context_switches_cpu());
if (!ret)
ring_buffer_write(buffer);
preempt_enable();It records the number of context switches for the current CPU, enables preemption, copies from user space, disable preemption and then checks if the number of context switches changed. If it did not, then the buffer is valid, otherwise the buffer may have been corrupted and the read from user space must be tried again.
The system call trace events are now faultable and have the same restrictions as the trace_marker write. For system calls to read the user space buffer (for example to read the file of the openat system call), it needs the same logic. Instead of copying the code over to the system call trace events, make the code generic to allow the system call trace events to use the same code. The following API is added internally to the tracing sub system (these are only exposed within the tracing subsystem and not to be used outside of it):
trace_user_fault_init() - initializes a trace_user_buf_info descriptor
that will allocate the per CPU buffers to copy from user space into.
trace_user_fault_destroy() - used to free the allocations made by
trace_user_fault_init().
trace_user_fault_get() - update the ref count of the info descriptor to
allow more than one user to use the same descriptor.
trace_user_fault_put() - decrement the ref count.
trace_user_fault_read() - performs the above action to read user space
into the per CPU buffer. The preempt_disable() is expected before calling this function and preemption must remain disabled while the buffer returned is in use.
--- kernel/trace/trace.c | 250 ++++++++++++++++++++++++++++++++----------- kernel/trace/trace.h | 17 +++ 2 files changed, 205 insertions(+), 62 deletions(-)
kernel/trace/trace.c +188 -62
@@ -7223,52 +7223,43 @@ struct trace_user_buf {char *buf; }; -struct trace_user_buf_info { -struct trace_user_buf __percpu *tbuf; -int ref; -}; - -static DEFINE_MUTEX (trace_user_buffer_mutex);static struct trace_user_buf_info *trace_user_buffer; -static void trace_user_fault_buffer_free (struct trace_user_buf_info *tinfo) +/** +* trace_user_fault_destroy - free up allocated memory of a trace user buffer +* @tinfo: The descriptor to free up +* +* Frees any data allocated in the trace info dsecriptor. +*/ +void trace_user_fault_destroy (struct trace_user_buf_info *tinfo) {char *buf;int cpu; +if (!tinfo || !tinfo->tbuf) +return ; +for_each_possible_cpu (cpu) { buf =per_cpu_ptr (tinfo->tbuf, cpu)->buf;kfree (buf); }free_percpu (tinfo->tbuf); -kfree (tinfo); } -static int trace_user_fault_buffer_enable (void ) +static int user_fault_buffer_enable (struct trace_user_buf_info *tinfo,size_t size) { -struct trace_user_buf_info *tinfo;char *buf;int cpu; -guard (mutex)(&trace_user_buffer_mutex); - -if (trace_user_buffer) { - trace_user_buffer->ref++; -return 0 ; - } - - tinfo =kmalloc (sizeof (*tinfo),GFP_KERNEL ); -if (!tinfo) -return -ENOMEM ; +lockdep_assert_held (&trace_user_buffer_mutex); tinfo->tbuf = alloc_percpu(struct trace_user_buf); -if (!tinfo->tbuf) { -kfree (tinfo); +if (!tinfo->tbuf)return -ENOMEM ; - } tinfo->ref =1 ; + tinfo->size = size;/* Clear each buffer in case of error */ for_each_possible_cpu (cpu) {
@@ -7276,42 +7267,165 @@ static int trace_user_fault_buffer_enable(void) }for_each_possible_cpu (cpu) { - buf =kmalloc_node (TRACE_MARKER_MAX_SIZE ,GFP_KERNEL , + buf =kmalloc_node (size,GFP_KERNEL ,cpu_to_node (cpu)); -if (!buf) { -trace_user_fault_buffer_free (tinfo); +if (!buf)return -ENOMEM ; - }per_cpu_ptr (tinfo->tbuf, cpu)->buf = buf; } - trace_user_buffer = tinfo; -return 0 ; } -static void trace_user_fault_buffer_disable (void ) +/* For internal use. Free and reinitialize */ +static void user_buffer_free (struct trace_user_buf_info **tinfo) { -struct trace_user_buf_info *tinfo; +lockdep_assert_held (&trace_user_buffer_mutex); -guard (mutex)(&trace_user_buffer_mutex); +trace_user_fault_destroy (*tinfo); +kfree (*tinfo); + *tinfo =NULL ; +} + +/* For internal use. Initialize and allocate */ +static int user_buffer_init (struct trace_user_buf_info **tinfo,size_t size) +{ +bool alloc = false; +int ret; + +lockdep_assert_held (&trace_user_buffer_mutex); + +if (!*tinfo) { + alloc = true; + *tinfo =kzalloc (sizeof (**tinfo),GFP_KERNEL ); +if (!*tinfo) +return -ENOMEM ; + } - tinfo = trace_user_buffer; + ret =user_fault_buffer_enable (*tinfo, size); +if (ret <0 && alloc) +user_buffer_free (tinfo); -if (WARN_ON_ONCE (!tinfo)) +return ret; +} + +/* For internal use, derefrence and free if necessary */ +static void user_buffer_put (struct trace_user_buf_info **tinfo) +{ +guard (mutex)(&trace_user_buffer_mutex); + +if (WARN_ON_ONCE (!*tinfo || !(*tinfo)->ref))return ; -if (--tinfo->ref) +if (--(*tinfo)->ref)return ; -trace_user_fault_buffer_free (tinfo); - trace_user_buffer =NULL ; +user_buffer_free (tinfo); } -/* Must be called with preemption disabled */ -static char *trace_user_fault_read (struct trace_user_buf_info *tinfo, -const char __user *ptr,size_t size, -size_t *read_size) +/** +* trace_user_fault_init - Allocated or reference a per CPU buffer +* @tinfo: A pointer to the trace buffer descriptor +* @size: The size to allocate each per CPU buffer +* +* Create a per CPU buffer that can be used to copy from user space +* in a task context. When calling trace_user_fault_read(), preemption +* must be disabled, and it will enable preemption and copy user +* space data to the buffer. If any schedule switches occur, it will +* retry until it succeeds without a schedule switch knowing the buffer +* is still valid. +* +* Returns 0 on success, negative on failure. +*/ +int trace_user_fault_init (struct trace_user_buf_info *tinfo,size_t size) +{ +int ret; + +if (!tinfo) +return -EINVAL ; + +guard (mutex)(&trace_user_buffer_mutex); + + ret =user_buffer_init (&tinfo, size); +if (ret <0 ) +trace_user_fault_destroy (tinfo); + +return ret; +} + +/** +* trace_user_fault_get - up the ref count for the user buffer +* @tinfo: A pointer to a pointer to the trace buffer descriptor +* +* Ups the ref count of the trace buffer. +* +* Returns the new ref count. +*/ +int trace_user_fault_get (struct trace_user_buf_info *tinfo) +{ +if (!tinfo) +return -1 ; + +guard (mutex)(&trace_user_buffer_mutex); + + tinfo->ref++; +return tinfo->ref; +} + +/** +* trace_user_fault_put - dereference a per cpu trace buffer +* @tinfo: The @tinfo that was passed to trace_user_fault_get() +* +* Decrement the ref count of @tinfo. +* +* Returns the new refcount (negative on error). +*/ +int trace_user_fault_put (struct trace_user_buf_info *tinfo) +{ +guard (mutex)(&trace_user_buffer_mutex); + +if (WARN_ON_ONCE (!tinfo || !tinfo->ref)) +return -1 ; + + --tinfo->ref; +return tinfo->ref; +} + +/** +* trace_user_fault_read - Read user space into a per CPU buffer +* @tinfo: The @tinfo allocated by trace_user_fault_get() +* @ptr: The user space pointer to read +* @size: The size of user space to read. +* @copy_func: Optional function to use to copy from user space +* @data: Data to pass to copy_func if it was supplied +* +* Preemption must be disabled when this is called, and must not +* be enabled while using the returned buffer. +* This does the copying from user space into a per CPU buffer. +* +* The @size must not be greater than the size passed in to +* trace_user_fault_init(). +* +* If @copy_func is NULL, trace_user_fault_read() will use copy_from_user(), +* otherwise it will call @copy_func. It will call @copy_func with: +* +* buffer: the per CPU buffer of the @tinfo. +* ptr: The pointer @ptr to user space to read +* size: The @size of the ptr to read +* data: The @data parameter +* +* It is expected that @copy_func will return 0 on success and non zero +* if there was a fault. +* +* Returns a pointer to the buffer with the content read from @ptr. +* Preemption must remain disabled while the caller accesses the +* buffer returned by this function. +* Returns NULL if there was a fault, or the size passed in is +* greater than the size passed to trace_user_fault_init(). +*/ +char *trace_user_fault_read (struct trace_user_buf_info *tinfo, +const char __user *ptr,size_t size, +trace_user_buf_copy copy_func,void *data) {int cpu =smp_processor_id ();char *buffer =per_cpu_ptr (tinfo->tbuf, cpu)->buf;
@@ -7319,9 +7433,14 @@ static char *trace_user_fault_read(struct trace_user_buf_info *tinfo,int trys =0 ;int ret; -if (size >TRACE_MARKER_MAX_SIZE ) - size =TRACE_MARKER_MAX_SIZE ; - *read_size =0 ; +lockdep_assert_preemption_disabled (); + +/* +* It's up to the caller to not try to copy more than it said +* it would. +*/ +if (size > tinfo->size) +return NULL ;/* * This acts similar to a seqcount. The per CPU context switches are
@@ -7361,7 +7480,14 @@ static char *trace_user_fault_read(struct trace_user_buf_info *tinfo,*/ preempt_enable_notrace (); - ret =__copy_from_user (buffer, ptr, size); +/* Make sure preemption is enabled here */ +lockdep_assert_preemption_enabled (); + +if (copy_func) { + ret =copy_func (buffer, ptr, size, data); + }else { + ret =__copy_from_user (buffer, ptr, size); + }preempt_disable_notrace ();migrate_enable ();
@@ -7378,7 +7504,6 @@ static char *trace_user_fault_read(struct trace_user_buf_info *tinfo,*/ }while (nr_context_switches_cpu (cpu) != cnt); - *read_size = size;return buffer; }
@@ -7389,7 +7514,6 @@ tracing_mark_write(struct file *filp, const char __user *ubuf,struct trace_array *tr = filp->private_data;ssize_t written = -ENODEV ;unsigned long ip; -size_t size;char *buf;if (tracing_disabled)
@@ -7407,13 +7531,10 @@ tracing_mark_write(struct file *filp, const char __user *ubuf,/* Must have preemption disabled while having access to the buffer */ guard (preempt_notrace)(); - buf =trace_user_fault_read (trace_user_buffer, ubuf, cnt, &size); + buf =trace_user_fault_read (trace_user_buffer, ubuf, cnt,NULL ,NULL );if (!buf)return -EFAULT ; -if (cnt > size) - cnt = size; -/* The selftests expect this function to be the IP address */ ip = _THIS_IP_;
@@ -7473,7 +7594,6 @@ tracing_mark_raw_write(struct file *filp, const char __user *ubuf, {struct trace_array *tr = filp->private_data;ssize_t written = -ENODEV ; -size_t size;char *buf;if (tracing_disabled)
@@ -7486,17 +7606,17 @@ tracing_mark_raw_write(struct file *filp, const char __user *ubuf,if (cnt <sizeof (unsigned int ))return -EINVAL ; +/* raw write is all or nothing */ +if (cnt >TRACE_MARKER_MAX_SIZE ) +return -EINVAL ; +/* Must have preemption disabled while having access to the buffer */ guard (preempt_notrace)(); - buf =trace_user_fault_read (trace_user_buffer, ubuf, cnt, &size); + buf =trace_user_fault_read (trace_user_buffer, ubuf, cnt,NULL ,NULL );if (!buf)return -EFAULT ; -/* raw write is all or nothing */ -if (cnt > size) -return -EINVAL ; -/* The global trace_marker_raw can go to multiple instances */ if (tr == &global_trace) {guard (rcu)();
@@ -7516,20 +7636,26 @@ static int tracing_mark_open(struct inode *inode, struct file *filp) {int ret; - ret =trace_user_fault_buffer_enable (); -if (ret <0 ) -return ret; + scoped_guard(mutex, &trace_user_buffer_mutex) { +if (!trace_user_buffer) { + ret =user_buffer_init (&trace_user_buffer,TRACE_MARKER_MAX_SIZE ); +if (ret <0 ) +return ret; + }else { + trace_user_buffer->ref++; + } + }stream_open (inode, filp); ret =tracing_open_generic_tr (inode, filp);if (ret <0 ) -trace_user_fault_buffer_disable (); +user_buffer_put (&trace_user_buffer);return ret; }static int tracing_mark_release (struct inode *inode,struct file *file) { -trace_user_fault_buffer_disable (); +user_buffer_put (&trace_user_buffer);return tracing_release_generic_tr (inode, file); }
kernel/trace/trace.h +17 -0
@@ -1531,6 +1531,23 @@ void trace_buffered_event_enable(void);void early_enable_events (struct trace_array *tr,char *buf,bool disable_first); +struct trace_user_buf ; +struct trace_user_buf_info { +struct trace_user_buf __percpu *tbuf; +size_t size; +int ref; +}; + +typedef int (*trace_user_buf_copy )(char *dst,const char __user *src, +size_t size,void *data); +int trace_user_fault_init (struct trace_user_buf_info *tinfo,size_t size); +int trace_user_fault_get (struct trace_user_buf_info *tinfo); +int trace_user_fault_put (struct trace_user_buf_info *tinfo); +void trace_user_fault_destroy (struct trace_user_buf_info *tinfo); +char *trace_user_fault_read (struct trace_user_buf_info *tinfo, +const char __user *ptr,size_t size, +trace_user_buf_copy copy_func,void *data); +static inline void __trace_event_discard_commit (struct trace_buffer *buffer,struct ring_buffer_event *event)
Signature · 1 line
2.51.0
PATCH v4 2/13 tracing: Have syscall trace events read user space string
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
As of commit 654ced4a1377 ("tracing: Introduce tracepoint_is_faultable()") system call trace events allow faulting in user space memory. Have some of the system call trace events take advantage of this.
Use the trace_user_fault_read() logic to read the user space buffer from user space and instead of just saving the pointer to the buffer in the system call event, also save the string that is passed in.
The syscall event has its nb_args shorten from an int to a short (where even u8 is plenty big enough) and the freed two bytes are used for "user_mask". The new "user_mask" field is used to store the index of the "args" field array that has the address to read from user space. This value is set to 0 if the system call event does not need to read user space for a field. This mask can be used to know if the event may fault or not. Only one bit set in user_mask is supported at this time.
This allows the output to look like this:
sys_access(filename: 0x7f8c55368470 "/etc/ld.so.preload", mode: 4) sys_execve(filename: 0x564ebcf5a6b8 "/usr/bin/emacs", argv: 0x7fff357c0300, envp: 0x564ebc4a4820)
--- include/trace/syscall.h | 4 +- kernel/trace/trace_syscalls.c | 396 ++++++++++++++++++++++++++++++++-- 2 files changed, 380 insertions(+), 20 deletions(-)
include/trace/syscall.h +3 -1
@@ -16,6 +16,7 @@* @name: name of the syscall * @syscall_nr: number of the syscall * @nb_args: number of parameters it takes +* @user_mask: mask of @args that will read user space * @types: list of types as strings * @args: list of args as strings (args[i] matches types[i]) * @enter_fields: list of fields for syscall_enter trace event
@@ -25,7 +26,8 @@struct syscall_metadata {const char *name;int syscall_nr; -int nb_args; +short nb_args; +short user_mask;const char **types;const char **args;struct list_head enter_fields;
kernel/trace/trace_syscalls.c +377 -19
516 lines not on this page · raw message
Signature · 1 line
2.51.0
PATCH v4 3/13 perf: tracing: Simplify perf_sysenter_enable/disable() with guards
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
Use guard(mutex)(&syscall_trace_lock) for perf_sysenter_enable() and perf_sysenter_disable() as well as for the perf_sysexit_enable() and perf_sysexit_disable(). This will make it easier to update these functions with other code that has early exit handling.
---
Changes since v3: https://lore.kernel.org/[email protected]
- Fixed return value of perf_sysenter_enable() and perf_sysexit_enable() to not return undefined value (kernel test robot)
kernel/trace/trace_syscalls.c | 48 ++++++++++++++++------------------- 1 file changed, 22 insertions(+), 26 deletions(-)
kernel/trace/trace_syscalls.c +22 -26
@@ -1009,22 +1009,21 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)static int perf_sysenter_enable (struct trace_event_call *call) { -int ret =0 ;int num; num = ((struct syscall_metadata *)call->data)->syscall_nr; -mutex_lock (&syscall_trace_lock); -if (!sys_perf_refcount_enter) - ret =register_trace_sys_enter (perf_syscall_enter,NULL ); -if (ret) { -pr_info ("event trace: Could not activate syscall entry trace point" ); - }else { -set_bit (num, enabled_perf_enter_syscalls); - sys_perf_refcount_enter++; +guard (mutex)(&syscall_trace_lock); +if (!sys_perf_refcount_enter) { +int ret =register_trace_sys_enter (perf_syscall_enter,NULL ); +if (ret) { +pr_info ("event trace: Could not activate syscall entry trace point" ); +return ret; + } } -mutex_unlock (&syscall_trace_lock); -return ret; +set_bit (num, enabled_perf_enter_syscalls); + sys_perf_refcount_enter++; +return 0 ; }static void perf_sysenter_disable (struct trace_event_call *call)
@@ -1033,12 +1032,11 @@ static void perf_sysenter_disable(struct trace_event_call *call) num = ((struct syscall_metadata *)call->data)->syscall_nr; -mutex_lock (&syscall_trace_lock); +guard (mutex)(&syscall_trace_lock); sys_perf_refcount_enter--;clear_bit (num, enabled_perf_enter_syscalls);if (!sys_perf_refcount_enter)unregister_trace_sys_enter (perf_syscall_enter,NULL ); -mutex_unlock (&syscall_trace_lock); } static int perf_call_bpf_exit(struct trace_event_call *call, struct pt_regs *regs,
@@ -1115,22 +1113,21 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret)static int perf_sysexit_enable (struct trace_event_call *call) { -int ret =0 ;int num; num = ((struct syscall_metadata *)call->data)->syscall_nr; -mutex_lock (&syscall_trace_lock); -if (!sys_perf_refcount_exit) - ret =register_trace_sys_exit (perf_syscall_exit,NULL ); -if (ret) { -pr_info ("event trace: Could not activate syscall exit trace point" ); - }else { -set_bit (num, enabled_perf_exit_syscalls); - sys_perf_refcount_exit++; +guard (mutex)(&syscall_trace_lock); +if (!sys_perf_refcount_exit) { +int ret =register_trace_sys_exit (perf_syscall_exit,NULL ); +if (ret) { +pr_info ("event trace: Could not activate syscall exit trace point" ); +return ret; + } } -mutex_unlock (&syscall_trace_lock); -return ret; +set_bit (num, enabled_perf_exit_syscalls); + sys_perf_refcount_exit++; +return 0 ; }static void perf_sysexit_disable (struct trace_event_call *call)
@@ -1139,12 +1136,11 @@ static void perf_sysexit_disable(struct trace_event_call *call) num = ((struct syscall_metadata *)call->data)->syscall_nr; -mutex_lock (&syscall_trace_lock); +guard (mutex)(&syscall_trace_lock); sys_perf_refcount_exit--;clear_bit (num, enabled_perf_exit_syscalls);if (!sys_perf_refcount_exit) unregister_trace_sys_exit(perf_syscall_exit, NULL); - mutex_unlock(&syscall_trace_lock); }#endif /* CONFIG_PERF_EVENTS */
Signature · 1 line
2.51.0
PATCH v4 4/13 perf: tracing: Have perf system calls read user space
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
Allow some of the system call events to read user space buffers. Instead of just showing the pointer into user space, allow perf events to also record the content of those pointers. For example:
# perf record -e syscalls:sys_enter_openat ls /usr/bin
[..]
# perf script
ls 1024 [005] 52.902721: syscalls:sys_enter_openat: dfd: 0xffffff9c, filename: 0x7fc1dbae321c "/etc/ld.so.cache", flags: 0x00080000, mode: 0x00000000
ls 1024 [005] 52.902899: syscalls:sys_enter_openat: dfd: 0xffffff9c, filename: 0x7fc1dbaae140 "/lib/x86_64-linux-gnu/libselinux.so.1", flags: 0x00080000, mode: 0x00000000
ls 1024 [005] 52.903471: syscalls:sys_enter_openat: dfd: 0xffffff9c, filename: 0x7fc1dbaae690 "/lib/x86_64-linux-gnu/libcap.so.2", flags: 0x00080000, mode: 0x00000000
ls 1024 [005] 52.903946: syscalls:sys_enter_openat: dfd: 0xffffff9c, filename: 0x7fc1dbaaebe0 "/lib/x86_64-linux-gnu/libc.so.6", flags: 0x00080000, mode: 0x00000000
ls 1024 [005] 52.904629: syscalls:sys_enter_openat: dfd: 0xffffff9c, filename: 0x7fc1dbaaf110 "/lib/x86_64-linux-gnu/libpcre2-8.so.0", flags: 0x00080000, mode: 0x00000000
ls 1024 [005] 52.906985: syscalls:sys_enter_openat: dfd: 0xffffffffffffff9c, filename: 0x7fc1dba92904 "/proc/filesystems", flags: 0x00080000, mode: 0x00000000
ls 1024 [005] 52.907323: syscalls:sys_enter_openat: dfd: 0xffffff9c, filename: 0x7fc1dba19490 "/usr/lib/locale/locale-archive", flags: 0x00080000, mode: 0x00000000
ls 1024 [005] 52.907746: syscalls:sys_enter_openat: dfd: 0xffffff9c, filename: 0x556fb888dcd0 "/usr/bin", flags: 0x00090800, mode: 0x00000000---
Changes since v3: https://lore.kernel.org/[email protected]
- Fix use of ret in perf_sysenter_enable. (kernel test robot)
kernel/trace/trace_syscalls.c | 136 ++++++++++++++++++++++------------ 1 file changed, 90 insertions(+), 46 deletions(-)
kernel/trace/trace_syscalls.c +90 -46
@@ -468,6 +468,58 @@ static char *sys_fault_user(struct syscall_metadata *sys_data,return buf; } +static int +syscall_get_data (struct syscall_metadata *sys_data,unsigned long *args, +char **buffer,int *size,int *user_size) +{ +struct syscall_user_buffer *sbuf; + +/* If the syscall_buffer is NULL, tracing is being shutdown */ + sbuf =READ_ONCE (syscall_buffer); +if (!sbuf) +return -1 ; + + *buffer =sys_fault_user (sys_data, sbuf, args, user_size); +/* +* user_size is the amount of data to append. +* Need to add 4 for the meta field that points to +* the user memory at the end of the event and also +* stores its size. +*/ + *size =4 + *user_size; +return 0 ; +} + +static void syscall_put_data (struct syscall_metadata *sys_data, +struct syscall_trace_enter *entry, +char *buffer,int size) +{ +void *ptr; +int val; + +/* +* Set the pointer to point to the meta data of the event +* that has information about the stored user space memory. +*/ + ptr = (void *)entry->args +sizeof (unsigned long ) * sys_data->nb_args; + +/* +* The meta data will store the offset of the user data from +* the beginning of the event. +*/ + val = (ptr - (void *)entry) +4 ; + +/* Store the offset and the size into the meta data */ + *(int *)ptr = val | (size <<16 ); + +/* Nothing to do if the user space was empty or faulted */ +if (size) { +/* Now store the user space data into the event */ + ptr +=4 ; +memcpy (ptr, buffer, size); + } +} +static void ftrace_syscall_enter (void *data,struct pt_regs *regs,long id) {struct trace_array *tr = data;
@@ -511,21 +563,9 @@ static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id)syscall_get_arguments (current, regs, args);if (mayfault) { -struct syscall_user_buffer *sbuf; - -/* If the syscall_buffer is NULL, tracing is being shutdown */ - sbuf =READ_ONCE (syscall_buffer); -if (!sbuf) +if (syscall_get_data (sys_data, args, &user_ptr, + &size, &user_size) <0 )return ; - - user_ptr =sys_fault_user (sys_data, sbuf, args, &user_size); -/* -* user_size is the amount of data to append. -* Need to add 4 for the meta field that points to -* the user memory at the end of the event and also -* stores its size. -*/ - size =4 + user_size; } size +=sizeof (*entry) +sizeof (unsigned long ) * sys_data->nb_args;
@@ -539,32 +579,8 @@ static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id)memcpy (entry->args, args,sizeof (unsigned long ) * sys_data->nb_args); -if (mayfault) { -void *ptr; -int val; - -/* -* Set the pointer to point to the meta data of the event -* that has information about the stored user space memory. -*/ - ptr = (void *)entry->args +sizeof (unsigned long ) * sys_data->nb_args; - -/* -* The meta data will store the offset of the user data from -* the beginning of the event. -*/ - val = (ptr - (void *)entry) +4 ; - -/* Store the offset and the size into the meta data */ - *(int *)ptr = val | (user_size <<16 ); - -/* Nothing to do if the user space was empty or faulted */ -if (user_size) { -/* Now store the user space data into the event */ - ptr +=4 ; -memcpy (ptr, user_ptr, user_size); - } - } +if (mayfault) +syscall_put_data (sys_data, entry, user_ptr, user_size);trace_event_buffer_commit (&fbuffer); }
@@ -956,9 +972,12 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)struct hlist_head *head;unsigned long args[6 ];bool valid_prog_array; +bool mayfault; +char *user_ptr;int syscall_nr; +int user_size;int rctx; -int size; +int size =0 ;/* * Syscall probe called with preemption enabled, but the ring
@@ -977,13 +996,24 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)if (!sys_data)return ; +syscall_get_arguments (current, regs, args); + +/* Check if this syscall event faults in user space memory */ + mayfault = sys_data->user_mask !=0 ; + +if (mayfault) { +if (syscall_get_data (sys_data, args, &user_ptr, + &size, &user_size) <0 ) +return ; + } + head =this_cpu_ptr (sys_data->enter_event->perf_events); valid_prog_array =bpf_prog_array_valid (sys_data->enter_event);if (!valid_prog_array &&hlist_empty (head))return ;/* get the size after alignment with the u32 buffer size field */ - size =sizeof (unsigned long ) * sys_data->nb_args +sizeof (*rec); + size +=sizeof (unsigned long ) * sys_data->nb_args +sizeof (*rec); size =ALIGN (size +sizeof (u32),sizeof (u64)); size -=sizeof (u32);
@@ -992,9 +1022,11 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)return ; rec->nr = syscall_nr; -syscall_get_arguments (current, regs, args);memcpy (&rec->args, args,sizeof (unsigned long ) * sys_data->nb_args); +if (mayfault) +syscall_put_data (sys_data, rec, user_ptr, user_size); +if ((valid_prog_array && !perf_call_bpf_enter (sys_data->enter_event, fake_regs, sys_data, rec)) ||hlist_empty (head)) {
@@ -1009,15 +1041,24 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)static int perf_sysenter_enable (struct trace_event_call *call) { +struct syscall_metadata *sys_data = call->data;int num; +int ret; - num = ((struct syscall_metadata *)call->data)->syscall_nr; + num = sys_data->syscall_nr;guard (mutex)(&syscall_trace_lock); +if (sys_data->user_mask) { + ret =syscall_fault_buffer_enable (); +if (ret <0 ) +return ret; + }if (!sys_perf_refcount_enter) { -int ret =register_trace_sys_enter (perf_syscall_enter,NULL ); + ret =register_trace_sys_enter (perf_syscall_enter,NULL );if (ret) {pr_info ("event trace: Could not activate syscall entry trace point" ); +if (sys_data->user_mask) +syscall_fault_buffer_disable ();return ret; } }
@@ -1028,15 +1069,18 @@ static int perf_sysenter_enable(struct trace_event_call *call)static void perf_sysenter_disable (struct trace_event_call *call) { +struct syscall_metadata *sys_data = call->data;int num; - num = ((struct syscall_metadata *)call->data)->syscall_nr; + num = sys_data->syscall_nr;guard (mutex)(&syscall_trace_lock); sys_perf_refcount_enter--;clear_bit (num, enabled_perf_enter_syscalls);if (!sys_perf_refcount_enter)unregister_trace_sys_enter (perf_syscall_enter,NULL ); +if (sys_data->user_mask) +syscall_fault_buffer_disable (); } static int perf_call_bpf_exit(struct trace_event_call *call, struct pt_regs *regs,
Signature · 1 line
2.51.0
PATCH v4 5/13 tracing: Have system call events record user array data
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
For system call events that have a length field, add a "user_arg_size" parameter to the system call meta data that denotes the index of the args array that holds the size of arg that the user_mask field has a bit set for.
The "user_mask" has a bit set that denotes the arg that points to an array in the user space address space and if a system call event has the user_mask field set and the user_arg_size set, it will then record the content of that address into the trace event, up to the size defined by SYSCALL_FAULT_BUF_SZ - 1.
This allows the output to look like:
sys_write(fd: 0xa, buf: 0x5646978d13c0 (01:00:05:00:00:00:00:00:01:87:55:89:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00), count: 0x20)
--- include/trace/syscall.h | 4 +- kernel/trace/trace_syscalls.c | 121 ++++++++++++++++++++++++---------- 2 files changed, 90 insertions(+), 35 deletions(-)
include/trace/syscall.h +3 -1
@@ -16,6 +16,7 @@* @name: name of the syscall * @syscall_nr: number of the syscall * @nb_args: number of parameters it takes +* @user_arg_size: holds @arg that has size of the user space to read * @user_mask: mask of @args that will read user space * @types: list of types as strings * @args: list of args as strings (args[i] matches types[i])
@@ -26,7 +27,8 @@struct syscall_metadata {const char *name;int syscall_nr; -short nb_args; +u8 nb_args; +s8 user_arg_size;short user_mask;const char **types;const char **args;
kernel/trace/trace_syscalls.c +87 -34
213 lines not on this page · raw message
Signature · 1 line
2.51.0
PATCH v4 6/13 tracing: Display some syscall arrays as strings
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
Some of the system calls that read a fixed length of memory from the user space address are not arrays but strings. Take a bit away from the nb_args field in the syscall meta data to use as a flag to denote that the system call's user_arg_size is being used as a string. The nb_args should never be more than 6, so 7 bits is plenty to hold that number. When the user_arg_is_str flag that, when set, will display the data array from the user space address as a string and not an array.
This will allow the output to look like this:
sys_sethostname(name: 0x5584310eb2a0 "debian", len: 6)
--- include/trace/syscall.h | 4 +++- kernel/trace/trace_syscalls.c | 22 +++++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-)
include/trace/syscall.h +3 -1
@@ -16,6 +16,7 @@* @name: name of the syscall * @syscall_nr: number of the syscall * @nb_args: number of parameters it takes +* @user_arg_is_str: set if the arg for @user_arg_size is a string * @user_arg_size: holds @arg that has size of the user space to read * @user_mask: mask of @args that will read user space * @types: list of types as strings
@@ -27,7 +28,8 @@struct syscall_metadata {const char *name;int syscall_nr; -u8 nb_args; +u8 nb_args:7 ; +u8 user_arg_is_str:1 ;s8 user_arg_size;short user_mask;const char **types;
kernel/trace/trace_syscalls.c +19 -3
@@ -184,7 +184,7 @@ print_syscall_enter(struct trace_iterator *iter, int flags, ptr = (void *)ent + (val &0xffff ); len = val >>16 ; -if (entry->user_arg_size <0 ) { +if (entry->user_arg_size <0 || entry->user_arg_is_str) {trace_seq_printf (s," \"%.*s\"" , len, ptr);continue ; }
@@ -249,6 +249,7 @@ print_syscall_exit(struct trace_iterator *iter, int flags,static int __init __set_enter_print_fmt (struct syscall_metadata *entry,char *buf,int len) { +bool is_string = entry->user_arg_is_str;int i;int pos =0 ;
@@ -266,7 +267,7 @@ __set_enter_print_fmt(struct syscall_metadata *entry, char *buf, int len)continue ;/* Add the format for the user space string or array */ -if (entry->user_arg_size <0 ) +if (entry->user_arg_size <0 || is_string) pos +=snprintf (buf + pos,LEN_OR_ZERO ," \\\"%%s\\\"" );else pos +=snprintf (buf + pos,LEN_OR_ZERO ," (%%s)" );
@@ -279,7 +280,7 @@ __set_enter_print_fmt(struct syscall_metadata *entry, char *buf, int len)if (!(BIT (i) & entry->user_mask))continue ;/* The user space data for arg has name __<arg>_val */ -if (entry->user_arg_size <0 ) { +if (entry->user_arg_size <0 || is_string) { pos +=snprintf (buf + pos,LEN_OR_ZERO ,", __get_str(__%s_val)" , entry->args[i]); }else {
@@ -781,6 +782,21 @@ static void check_faultable_syscall(struct trace_event_call *call, int nr) sys_data->user_mask =BIT (1 ); sys_data->user_arg_size =2 ;break ; +/* user arg 0 with size arg at 1 as string */ +case __NR_setdomainname: +case __NR_sethostname: + sys_data->user_mask =BIT (0 ); + sys_data->user_arg_size =1 ; + sys_data->user_arg_is_str =1 ; +break ; +#if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG !=32 +/* user arg 4 with size arg at 3 as string */ +case __NR_kexec_file_load: + sys_data->user_mask =BIT (4 ); + sys_data->user_arg_size =3 ; + sys_data->user_arg_is_str =1 ; +break ; +#endif /* user arg at position 0 */ case __NR_access:case __NR_acct:
Signature · 1 line
2.51.0
PATCH v4 7/13 tracing: Allow syscall trace events to read more than one user parameter
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
Allow more than one field of a syscall trace event to read user space. Build on top of the user_mask by allowing more than one bit to be set that corresponds to the @args array of the syscall metadata. For each argument in the @args array that is to be read, it will have a dynamic array/string field associated to it.
Note that multiple fields to be read from user space is not supported if the user_arg_size field is set in the syscall metada. That field can only be used if only one field is being read from user space as that field is a number representing the size field of the syscall event that holds the size of the data to read from user space. It becomes ambiguous if the system call reads more than one field. Currently this is not an issue.
If a syscall event happens to enable two events to read user space and sets the user_arg_size field, it will trigger a warning at boot and the user_arg_size field will be cleared.
The per CPU buffer that is used to read the user space addresses is now broken up into 3 sections, each of 168 bytes. The reason for 168 is that it is the biggest portion of 512 bytes divided by 3 that is 8 byte aligned.
The max amount copied into the ring buffer from user space is now only 128 bytes, which is plenty. When reading user space, it still reads 167 (168-1) bytes and uses the remaining to know if it should append the extra "..." to the end or not.
This will allow the event to look like this:
sys_renameat2(olddfd: 0xffffff9c, oldname: 0x7ffe02facdff "/tmp/x", newdfd: 0xffffff9c, newname: 0x7ffe02face06 "/tmp/y", flags: 1)
--- kernel/trace/trace_syscalls.c | 321 +++++++++++++++++++++++----------- 1 file changed, 221 insertions(+), 100 deletions(-)
kernel/trace/trace_syscalls.c +221 -100
@@ -138,6 +138,7 @@ print_syscall_enter(struct trace_iterator *iter, int flags,struct syscall_metadata *entry;int i, syscall, val, len;unsigned char *ptr; +int offset =0 ; trace = (typeof (trace ))ent; syscall = trace->nr;
@@ -177,12 +178,13 @@ print_syscall_enter(struct trace_iterator *iter, int flags,continue ;/* This arg points to a user space string */ - ptr = (void *)trace->args +sizeof (long ) * entry->nb_args; + ptr = (void *)trace->args +sizeof (long ) * entry->nb_args + offset; val = *(int *)ptr;/* The value is a dynamic string (len << 16 | offset) */ ptr = (void *)ent + (val &0xffff ); len = val >>16 ; + offset +=4 ;if (entry->user_arg_size <0 || entry->user_arg_is_str) {trace_seq_printf (s," \"%.*s\"" , len, ptr);
@@ -335,7 +337,6 @@ static int __init syscall_enter_define_fields(struct trace_event_call *call)unsigned long mask;char *arg;int offset = offsetof(typeof (trace ), args); -int idx;int ret =0 ;int len;int i;
@@ -354,31 +355,56 @@ static int __init syscall_enter_define_fields(struct trace_event_call *call)return ret; mask = meta->user_mask; - idx =ffs (mask) -1 ; -/* -* User space data is faulted into a temporary buffer and then -* added as a dynamic string or array to the end of the event. -* The user space data name for the arg pointer is "__<arg>_val". -*/ - len =strlen (meta->args[idx]) +sizeof ("___val" ); - arg =kmalloc (len,GFP_KERNEL ); -if (WARN_ON_ONCE (!arg)) { - meta->user_mask =0 ; -return -ENOMEM ; - } +while (mask) { +int idx =ffs (mask) -1 ; + mask &= ~BIT (idx); + +/* +* User space data is faulted into a temporary buffer and then +* added as a dynamic string or array to the end of the event. +* The user space data name for the arg pointer is +* "__<arg>_val". +*/ + len =strlen (meta->args[idx]) +sizeof ("___val" ); + arg =kmalloc (len,GFP_KERNEL ); +if (WARN_ON_ONCE (!arg)) { + meta->user_mask =0 ; +return -ENOMEM ; + } -snprintf (arg, len,"__%s_val" , meta->args[idx]); +snprintf (arg, len,"__%s_val" , meta->args[idx]); - ret =trace_define_field (call,"__data_loc char[]" , - arg, offset,sizeof (int ),0 , -FILTER_OTHER ); -if (ret) -kfree (arg); + ret =trace_define_field (call,"__data_loc char[]" , + arg, offset,sizeof (int ),0 , +FILTER_OTHER ); +if (ret) { +kfree (arg); +break ; + } + offset +=4 ; + }return ret; } +/* +* Create a per CPU temporary buffer to copy user space pointers into. +* +* SYSCALL_FAULT_BUF_SZ holds the size of the per CPU buffer to use +* to copy memory from user space addresses into. +* +* SYSCALL_FAULT_ARG_SZ is the amount to copy from user space. +* +* SYSCALL_FAULT_USER_MAX is the amount to copy into the ring buffer. +* It's slightly smaller than SYSCALL_FAULT_ARG_SZ to know if it +* needs to append the EXTRA or not. +* +* This only allows up to 3 args from system calls. +*/ #define SYSCALL_FAULT_BUF_SZ 512 +#define SYSCALL_FAULT_ARG_SZ 168 +#define SYSCALL_FAULT_USER_MAX 128 +#define SYSCALL_FAULT_MAX_CNT 3/* Use the tracing per CPU buffer infrastructure to copy from user space */ struct syscall_user_buffer {
@@ -438,34 +464,58 @@ static void syscall_fault_buffer_disable(void)call_rcu_tasks_trace (&sbuf->rcu, rcu_free_syscall_buffer); } +struct syscall_args { +char *ptr_array[SYSCALL_FAULT_MAX_CNT ]; +int read[SYSCALL_FAULT_MAX_CNT ]; +int uargs; +}; +static int syscall_copy_user (char *buf,const char __user *ptr,size_t size,void *data) { -unsigned long *ret_size = data; +struct syscall_args *args = data; +int ret; + +for (int i =0 ; i < args->uargs; i++, buf +=SYSCALL_FAULT_ARG_SZ ) { + ptr = (char __user *)args->ptr_array[i]; + ret =strncpy_from_user (buf, ptr, size); + args->read[i] = ret; + } +return 0 ; +} + +static int syscall_copy_user_array (char *buf,const char __user *ptr, +size_t size,void *data) +{ +struct syscall_args *args = data;int ret; - ret =strncpy_from_user (buf, ptr, size); -if (ret <0 ) -return 1 ; - *ret_size = ret; +for (int i =0 ; i < args->uargs; i++, buf +=SYSCALL_FAULT_ARG_SZ ) { + ptr = (char __user *)args->ptr_array[i]; + ret =__copy_from_user (buf, ptr, size); + args->read[i] = ret ?-1 : size; + }return 0 ; }static char *sys_fault_user (struct syscall_metadata *sys_data,struct syscall_user_buffer *sbuf, -unsigned long *args,unsigned int *data_size) +unsigned long *args, +unsigned int data_size[SYSCALL_FAULT_MAX_CNT ]) {trace_user_buf_copy syscall_copy = syscall_copy_user; -unsigned long size =SYSCALL_FAULT_BUF_SZ -1 ;unsigned long mask = sys_data->user_mask; -int idx =ffs (mask) -1 ; +unsigned long size =SYSCALL_FAULT_ARG_SZ -1 ; +struct syscall_args sargs;bool array = false; -char *ptr; +char *buffer;char *buf; +int ret; +int i =0 ; -/* Get the pointer to user space memory to read */ - ptr = (char *)args[idx]; - *data_size =0 ; +/* The extra is appended to the user data in the buffer */ +BUILD_BUG_ON (SYSCALL_FAULT_USER_MAX +sizeof (EXTRA ) >= +SYSCALL_FAULT_ARG_SZ );/* * If this system call event has a size argument, use
@@ -475,67 +525,103 @@ static char *sys_fault_user(struct syscall_metadata *sys_data,if (sys_data->user_arg_size >=0 ) { array = true; size = args[sys_data->user_arg_size]; -if (size >SYSCALL_FAULT_BUF_SZ -1 ) - size =SYSCALL_FAULT_BUF_SZ -1 ; -/* use normal copy_from_user() */ - syscall_copy =NULL ; +if (size >SYSCALL_FAULT_ARG_SZ -1 ) + size =SYSCALL_FAULT_ARG_SZ -1 ; + syscall_copy = syscall_copy_user_array; } - buf =trace_user_fault_read (&sbuf->buf, ptr, size, - syscall_copy, &size); -if (!buf) +while (mask) { +int idx =ffs (mask) -1 ; + mask &= ~BIT (idx); + +if (WARN_ON_ONCE (i ==SYSCALL_FAULT_MAX_CNT )) +break ; + +/* Get the pointer to user space memory to read */ + sargs.ptr_array[i++] = (char *)args[idx]; + } + + sargs.uargs = i; + +/* Clear the values that are not used */ +for (; i <SYSCALL_FAULT_MAX_CNT ; i++) { + data_size[i] =-1 ;/* Denotes no pointer */ + } + + buffer =trace_user_fault_read (&sbuf->buf,NULL , size, + syscall_copy, &sargs); +if (!buffer)return NULL ; -/* For strings, replace any non-printable characters with '.' */ -if (!array) { -for (int i =0 ; i < size; i++) { -if (!isprint (buf[i])) - buf[i] ='.' ; - } + buf = buffer; +for (i =0 ; i < sargs.uargs; i++, buf +=SYSCALL_FAULT_ARG_SZ ) { -/* -* If the text was truncated due to our max limit, add "..." to -* the string. -*/ -if (size >SYSCALL_FAULT_BUF_SZ -sizeof (EXTRA )) { -strscpy (buf +SYSCALL_FAULT_BUF_SZ -sizeof (EXTRA ), -EXTRA ,sizeof (EXTRA )); - size =SYSCALL_FAULT_BUF_SZ ; + ret = sargs.read[i]; +if (ret <0 ) +continue ; + buf[ret] ='\0' ; + +/* For strings, replace any non-printable characters with '.' */ +if (!array) { +for (int x =0 ; x < ret; x++) { +if (!isprint (buf[x])) + buf[x] ='.' ; + } + +/* +* If the text was truncated due to our max limit, +* add "..." to the string. +*/ +if (ret >SYSCALL_FAULT_USER_MAX ) { +strscpy (buf +SYSCALL_FAULT_USER_MAX ,EXTRA , +sizeof (EXTRA )); + ret =SYSCALL_FAULT_USER_MAX +sizeof (EXTRA ); + }else { + buf[ret++] ='\0' ; + } }else { - buf[size++] ='\0' ; + ret =min (ret,SYSCALL_FAULT_USER_MAX ); } + data_size[i] = ret; } - *data_size = size; -return buf; +return buffer; }static int syscall_get_data (struct syscall_metadata *sys_data,unsigned long *args, -char **buffer,int *size,int *user_size) +char **buffer,int *size,int *user_sizes,int *uargs) {struct syscall_user_buffer *sbuf; +int i;/* If the syscall_buffer is NULL, tracing is being shutdown */ sbuf =READ_ONCE (syscall_buffer);if (!sbuf)return -1 ; - *buffer =sys_fault_user (sys_data, sbuf, args, user_size); + *buffer =sys_fault_user (sys_data, sbuf, args, user_sizes);/* * user_size is the amount of data to append. * Need to add 4 for the meta field that points to * the user memory at the end of the event and also * stores its size. */ - *size =4 + *user_size; +for (i =0 ; i <SYSCALL_FAULT_MAX_CNT ; i++) { +if (user_sizes[i] <0 ) +break ; + *size += user_sizes[i] +4 ; + } +/* Save the number of user read arguments of this syscall */ + *uargs = i;return 0 ; }static void syscall_put_data (struct syscall_metadata *sys_data,struct syscall_trace_enter *entry, -char *buffer,int size,int user_size) +char *buffer,int size,int *user_sizes,int uargs) { +char *buf = buffer;void *ptr;int val;
@@ -547,21 +633,30 @@ static void syscall_put_data(struct syscall_metadata *sys_data,/* * The meta data will store the offset of the user data from -* the beginning of the event. +* the beginning of the event. That is after the static arguments +* and the meta data fields. */ - val = (ptr - (void *)entry) +4 ; + val = (ptr - (void *)entry) +4 * uargs; -/* Store the offset and the size into the meta data */ - *(int *)ptr = val | (user_size <<16 ); +for (int i =0 ; i < uargs; i++) { + +if (i) + val += user_sizes[i -1 ]; -if (WARN_ON_ONCE ((ptr - (void *)entry + user_size) > size)) - user_size =0 ; +/* Store the offset and the size into the meta data */ + *(int *)ptr = val | (user_sizes[i] <<16 ); -/* Nothing to do if the user space was empty or faulted */ -if (user_size) { -/* Now store the user space data into the event */ +/* Skip the meta data */ ptr +=4 ; -memcpy (ptr, buffer, user_size); + } + +for (int i =0 ; i < uargs; i++, buf +=SYSCALL_FAULT_ARG_SZ ) { +/* Nothing to do if the user space was empty or faulted */ +if (!user_sizes[i]) +continue ; + +memcpy (ptr, buf, user_sizes[i]); + ptr += user_sizes[i]; } }
@@ -574,9 +669,10 @@ static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id)struct trace_event_buffer fbuffer;unsigned long args[6 ];char *user_ptr; -int user_size =0 ; +int user_sizes[SYSCALL_FAULT_MAX_CNT ] = {};int syscall_nr;int size =0 ; +int uargs =0 ;bool mayfault;/*
@@ -609,7 +705,7 @@ static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id)if (mayfault) {if (syscall_get_data (sys_data, args, &user_ptr, - &size, &user_size) <0 ) + &size, user_sizes, &uargs) <0 )return ; }
@@ -625,7 +721,7 @@ static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id)memcpy (entry->args, args,sizeof (unsigned long ) * sys_data->nb_args);if (mayfault) -syscall_put_data (sys_data, entry, user_ptr, size, user_size); +syscall_put_data (sys_data, entry, user_ptr, size, user_sizes, uargs);trace_event_buffer_commit (&fbuffer); }
@@ -767,6 +863,7 @@ static void unreg_event_syscall_exit(struct trace_event_file *file,static void check_faultable_syscall (struct trace_event_call *call,int nr) {struct syscall_metadata *sys_data = call->data; +unsigned long mask;/* Only work on entry */ if (sys_data->enter_event != call)
@@ -800,7 +897,6 @@ static void check_faultable_syscall(struct trace_event_call *call, int nr)/* user arg at position 0 */ case __NR_access:case __NR_acct: -case __NR_add_key:/* Just _type. TODO add _description */ case __NR_chdir:case __NR_chown:case __NR_chmod:
@@ -809,33 +905,19 @@ static void check_faultable_syscall(struct trace_event_call *call, int nr)case __NR_delete_module:case __NR_execve:case __NR_fsopen: -case __NR_getxattr:/* Just pathname, TODO add name */ case __NR_lchown: -case __NR_lgetxattr:/* Just pathname, TODO add name */ -case __NR_lremovexattr:/* Just pathname, TODO add name */ -case __NR_link:/* Just oldname. TODO add newname */ -case __NR_listxattr:/* Just pathname, TODO add list */ -case __NR_llistxattr:/* Just pathname, TODO add list */ -case __NR_lsetxattr:/* Just pathname, TODO add list */ case __NR_open:case __NR_memfd_create: -case __NR_mount:/* Just dev_name, TODO add dir_name and type */ case __NR_mkdir:case __NR_mknod:case __NR_mq_open:case __NR_mq_unlink: -case __NR_pivot_root:/* Just new_root, TODO add old_root */ case __NR_readlink: -case __NR_removexattr:/* Just pathname, TODO add name */ -case __NR_rename:/* Just oldname. TODO add newname */ -case __NR_request_key:/* Just _type. TODO add _description */ case __NR_rmdir: -case __NR_setxattr:/* Just pathname, TODO add list */ case __NR_shmdt:case __NR_statfs:case __NR_swapon:case __NR_swapoff: -case __NR_symlink:/* Just oldname. TODO add newname */ case __NR_truncate:case __NR_unlink:case __NR_umount2:
@@ -857,14 +939,10 @@ static void check_faultable_syscall(struct trace_event_call *call, int nr)case __NR_fspick:case __NR_fremovexattr:case __NR_futimesat: -case __NR_getxattrat:/* Just pathname, TODO add name */ case __NR_inotify_add_watch: -case __NR_linkat:/* Just oldname. TODO add newname */ -case __NR_listxattrat:/* Just pathname, TODO add list */ case __NR_mkdirat:case __NR_mknodat:case __NR_mount_setattr: -case __NR_move_mount:/* Just from_pathname, TODO add to_pathname */ case __NR_name_to_handle_at:#if defined(__ARCH_WANT_NEW_STAT) || defined(__ARCH_WANT_STAT64)case __NR_newfstatat:
@@ -874,13 +952,8 @@ static void check_faultable_syscall(struct trace_event_call *call, int nr)case __NR_open_tree:case __NR_open_tree_attr:case __NR_readlinkat: -case __NR_renameat:/* Just oldname. TODO add newname */ -case __NR_renameat2:/* Just oldname. TODO add newname */ -case __NR_removexattrat:/* Just pathname, TODO add name */ case __NR_quotactl: -case __NR_setxattrat:/* Just pathname, TODO add list */ case __NR_syslog: -case __NR_symlinkat:/* Just oldname. TODO add newname */ case __NR_statx:case __NR_unlinkat:case __NR_utimensat:
@@ -895,9 +968,56 @@ static void check_faultable_syscall(struct trace_event_call *call, int nr)case __NR_fanotify_mark: sys_data->user_mask =BIT (4 );break ; +/* 2 user args, 0 and 1 */ +case __NR_add_key: +case __NR_getxattr: +case __NR_lgetxattr: +case __NR_lremovexattr: +case __NR_link: +case __NR_listxattr: +case __NR_llistxattr: +case __NR_lsetxattr: +case __NR_pivot_root: +case __NR_removexattr: +case __NR_rename: +case __NR_request_key: +case __NR_setxattr: +case __NR_symlink: + sys_data->user_mask =BIT (0 ) |BIT (1 ); +break ; +/* 2 user args, 0 and 2 */ +case __NR_symlinkat: + sys_data->user_mask =BIT (0 ) |BIT (2 ); +break ; +/* 2 user args, 1 and 3 */ +case __NR_getxattrat: +case __NR_linkat: +case __NR_listxattrat: +case __NR_move_mount: +case __NR_renameat: +case __NR_renameat2: +case __NR_removexattrat: +case __NR_setxattrat: + sys_data->user_mask =BIT (1 ) |BIT (3 ); +break ; +case __NR_mount:/* Just dev_name and dir_name, TODO add type */ + sys_data->user_mask =BIT (0 ) |BIT (1 ) |BIT (2 ); +break ;default : sys_data->user_mask =0 ; +return ; } + +if (sys_data->user_arg_size <0 ) +return ; + +/* +* The user_arg_size can only be used when the system call +* is reading only a single address from user space. +*/ + mask = sys_data->user_mask; +if (WARN_ON (mask & (mask -1 ))) + sys_data->user_arg_size =-1 ; }static int __init init_syscall_trace (struct trace_event_call *call)
@@ -1043,10 +1163,11 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)bool valid_prog_array;bool mayfault;char *user_ptr; +int user_sizes[SYSCALL_FAULT_MAX_CNT ] = {};int syscall_nr; -int user_size;int rctx;int size =0 ; +int uargs =0 ;/* * Syscall probe called with preemption enabled, but the ring
@@ -1072,7 +1193,7 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)if (mayfault) {if (syscall_get_data (sys_data, args, &user_ptr, - &size, &user_size) <0 ) + &size, user_sizes, &uargs) <0 )return ; }
@@ -1094,7 +1215,7 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)memcpy (&rec->args, args,sizeof (unsigned long ) * sys_data->nb_args);if (mayfault) -syscall_put_data (sys_data, rec, user_ptr, size, user_size); +syscall_put_data (sys_data, rec, user_ptr, size, user_sizes, uargs); if ((valid_prog_array && !perf_call_bpf_enter(sys_data->enter_event, fake_regs, sys_data, rec)) ||
Signature · 1 line
2.51.0
kernel test robot
build botRe: [PATCH v4 07/13] tracing: Allow syscall trace events to read more than one user parameter
kernel test robot noticed the following build errors:
Hi Steven,
kernel test robot noticed the following build errors:
[auto build test ERROR on akpm-mm/mm-everything]
[also build test ERROR on linus/master v6.18-rc2 next-20251023] [cannot apply to trace/for-next acme/perf/core]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Steven-Rostedt/tracing-Make-trace_user_fault_read-exposed-to-rest-of-tracing/20251021-085719 base: https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-everything patch link: https://lore.kernel.org/r/20251021005233.417553389%40kernel.org patch subject: [PATCH v4 07/13] tracing: Allow syscall trace events to read more than one user parameter config: riscv-randconfig-r054-20251024 (https://download.01.org/0day-ci/archive/20251024/[email protected]/config) compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251024/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All errors (new ones prefixed by >>):
In file included from arch/riscv/include/asm/io.h:140:
include/asm-generic/io.h:863:2: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
863 | outsb(addr, buffer, count);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/include/asm/io.h:122:55: note: expanded from macro 'outsb'
122 | #define outsb(addr, buffer, count) __outsb(PCI_IOBASE + (addr), buffer, count)
| ~~~~~~~~~~ ^
In file included from kernel/trace/trace_syscalls.c:2:
In file included from include/trace/syscall.h:7:
In file included from include/linux/trace_events.h:9:
In file included from include/linux/hardirq.h:11:
In file included from ./arch/riscv/include/generated/asm/hardirq.h:1:
In file included from include/asm-generic/hardirq.h:17:
In file included from include/linux/irq.h:20:
In file included from include/linux/io.h:12:
In file included from arch/riscv/include/asm/io.h:140:
include/asm-generic/io.h:872:2: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
872 | outsw(addr, buffer, count);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/include/asm/io.h:123:55: note: expanded from macro 'outsw'
123 | #define outsw(addr, buffer, count) __outsw(PCI_IOBASE + (addr), buffer, count)
| ~~~~~~~~~~ ^
In file included from kernel/trace/trace_syscalls.c:2:
In file included from include/trace/syscall.h:7:
In file included from include/linux/trace_events.h:9:
In file included from include/linux/hardirq.h:11:
In file included from ./arch/riscv/include/generated/asm/hardirq.h:1:
In file included from include/asm-generic/hardirq.h:17:
In file included from include/linux/irq.h:20:
In file included from include/linux/io.h:12:
In file included from arch/riscv/include/asm/io.h:140:
include/asm-generic/io.h:881:2: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
881 | outsl(addr, buffer, count);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
arch/riscv/include/asm/io.h:124:55: note: expanded from macro 'outsl'
124 | #define outsl(addr, buffer, count) __outsl(PCI_IOBASE + (addr), buffer, count)
| ~~~~~~~~~~ ^
In file included from kernel/trace/trace_syscalls.c:2:
In file included from include/trace/syscall.h:7:
In file included from include/linux/trace_events.h:9:
In file included from include/linux/hardirq.h:11:
In file included from ./arch/riscv/include/generated/asm/hardirq.h:1:
In file included from include/asm-generic/hardirq.h:17:
In file included from include/linux/irq.h:20:
In file included from include/linux/io.h:12:
In file included from arch/riscv/include/asm/io.h:140:
include/asm-generic/io.h:1209:55: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
1209 | return (port > MMIO_UPPER_LIMIT) ? NULL : PCI_IOBASE + port;
| ~~~~~~~~~~ ^
kernel/trace/trace_syscalls.c:877:7: error: use of undeclared identifier '__NR_mq_timedsend'
877 | case __NR_mq_timedsend:
| ^
kernel/trace/trace_syscalls.c:898:7: error: use of undeclared identifier '__NR_access'
898 | case __NR_access:
| ^
kernel/trace/trace_syscalls.c:901:7: error: use of undeclared identifier '__NR_chown'
901 | case __NR_chown:
| ^
kernel/trace/trace_syscalls.c:902:7: error: use of undeclared identifier '__NR_chmod'
902 | case __NR_chmod:
| ^
kernel/trace/trace_syscalls.c:904:7: error: use of undeclared identifier '__NR_creat'
904 | case __NR_creat:
| ^
kernel/trace/trace_syscalls.c:908:7: error: use of undeclared identifier '__NR_lchown'
908 | case __NR_lchown:
| ^
kernel/trace/trace_syscalls.c:909:7: error: use of undeclared identifier '__NR_open'
909 | case __NR_open:
| ^
kernel/trace/trace_syscalls.c:911:7: error: use of undeclared identifier '__NR_mkdir'
911 | case __NR_mkdir:
| ^
kernel/trace/trace_syscalls.c:912:7: error: use of undeclared identifier '__NR_mknod'
912 | case __NR_mknod:
| ^
kernel/trace/trace_syscalls.c:915:7: error: use of undeclared identifier '__NR_readlink'
915 | case __NR_readlink:
| ^
kernel/trace/trace_syscalls.c:916:7: error: use of undeclared identifier '__NR_rmdir'
916 | case __NR_rmdir:
| ^
kernel/trace/trace_syscalls.c:918:7: error: use of undeclared identifier '__NR_statfs'
918 | case __NR_statfs:
| ^
kernel/trace/trace_syscalls.c:921:7: error: use of undeclared identifier '__NR_truncate'
921 | case __NR_truncate:
| ^
kernel/trace/trace_syscalls.c:922:7: error: use of undeclared identifier '__NR_unlink'
922 | case __NR_unlink:
| ^
kernel/trace/trace_syscalls.c:924:7: error: use of undeclared identifier '__NR_utime'
924 | case __NR_utime:
| ^
kernel/trace/trace_syscalls.c:925:7: error: use of undeclared identifier '__NR_utimes'
925 | case __NR_utimes:
| ^
kernel/trace/trace_syscalls.c:941:7: error: use of undeclared identifier '__NR_futimesat'
941 | case __NR_futimesat:
| ^
>> kernel/trace/trace_syscalls.c:959:7: error: use of undeclared identifier '__NR_utimensat'
959 | case __NR_utimensat:
| ^
kernel/trace/trace_syscalls.c:976:7: error: use of undeclared identifier '__NR_link'
976 | case __NR_link:
| ^fatal error: too many errors emitted, stopping now [-ferror-limit=] 7 warnings and 20 errors generated.
Kconfig warnings: (for reference only)
WARNING: unmet direct dependencies detected for ARCH_HAS_ELF_CORE_EFLAGS Depends on [n]: BINFMT_ELF [=n] && ELF_CORE [=y]
Selected by [y]:
- RISCV [=y]
vim +/__NR_utimensat +959 kernel/trace/trace_syscalls.c
ee08c6eccb7d12 Frederic Weisbecker 2009-03-07 857
56cbdf4ab0c570 Steven Rostedt 2025-10-20 858 /*
56cbdf4ab0c570 Steven Rostedt 2025-10-20 859 * For system calls that reference user space memory that can
56cbdf4ab0c570 Steven Rostedt 2025-10-20 860 * be recorded into the event, set the system call meta data's user_mask
56cbdf4ab0c570 Steven Rostedt 2025-10-20 861 * to the "args" index that points to the user space memory to retrieve.
56cbdf4ab0c570 Steven Rostedt 2025-10-20 862 */
56cbdf4ab0c570 Steven Rostedt 2025-10-20 863 static void check_faultable_syscall(struct trace_event_call *call, int nr)
56cbdf4ab0c570 Steven Rostedt 2025-10-20 864 {
56cbdf4ab0c570 Steven Rostedt 2025-10-20 865 struct syscall_metadata *sys_data = call->data;
f5da5a4c232ae3 Steven Rostedt 2025-10-20 866 unsigned long mask;
56cbdf4ab0c570 Steven Rostedt 2025-10-20 867
56cbdf4ab0c570 Steven Rostedt 2025-10-20 868 /* Only work on entry */
56cbdf4ab0c570 Steven Rostedt 2025-10-20 869 if (sys_data->enter_event != call)
56cbdf4ab0c570 Steven Rostedt 2025-10-20 870 return;
56cbdf4ab0c570 Steven Rostedt 2025-10-20 871
3255b2e71313ff Steven Rostedt 2025-10-20 872 sys_data->user_arg_size = -1;
3255b2e71313ff Steven Rostedt 2025-10-20 873
56cbdf4ab0c570 Steven Rostedt 2025-10-20 874 switch (nr) {
3255b2e71313ff Steven Rostedt 2025-10-20 875 /* user arg 1 with size arg at 2 */
3255b2e71313ff Steven Rostedt 2025-10-20 876 case __NR_write:
3255b2e71313ff Steven Rostedt 2025-10-20 877 case __NR_mq_timedsend:
3255b2e71313ff Steven Rostedt 2025-10-20 878 case __NR_pwrite64:
3255b2e71313ff Steven Rostedt 2025-10-20 879 sys_data->user_mask = BIT(1);
3255b2e71313ff Steven Rostedt 2025-10-20 880 sys_data->user_arg_size = 2;
3255b2e71313ff Steven Rostedt 2025-10-20 881 break;
9eb1f5e9cf4f6f Steven Rostedt 2025-10-20 882 /* user arg 0 with size arg at 1 as string */
9eb1f5e9cf4f6f Steven Rostedt 2025-10-20 883 case __NR_setdomainname:
9eb1f5e9cf4f6f Steven Rostedt 2025-10-20 884 case __NR_sethostname:
9eb1f5e9cf4f6f Steven Rostedt 2025-10-20 885 sys_data->user_mask = BIT(0);
9eb1f5e9cf4f6f Steven Rostedt 2025-10-20 886 sys_data->user_arg_size = 1;
9eb1f5e9cf4f6f Steven Rostedt 2025-10-20 887 sys_data->user_arg_is_str = 1;
9eb1f5e9cf4f6f Steven Rostedt 2025-10-20 888 break;
9eb1f5e9cf4f6f Steven Rostedt 2025-10-20 889 #if defined(__ARCH_WANT_TIME32_SYSCALLS) || __BITS_PER_LONG != 32
9eb1f5e9cf4f6f Steven Rostedt 2025-10-20 890 /* user arg 4 with size arg at 3 as string */
9eb1f5e9cf4f6f Steven Rostedt 2025-10-20 891 case __NR_kexec_file_load:
9eb1f5e9cf4f6f Steven Rostedt 2025-10-20 892 sys_data->user_mask = BIT(4);
9eb1f5e9cf4f6f Steven Rostedt 2025-10-20 893 sys_data->user_arg_size = 3;
9eb1f5e9cf4f6f Steven Rostedt 2025-10-20 894 sys_data->user_arg_is_str = 1;
9eb1f5e9cf4f6f Steven Rostedt 2025-10-20 895 break;
9eb1f5e9cf4f6f Steven Rostedt 2025-10-20 896 #endif
56cbdf4ab0c570 Steven Rostedt 2025-10-20 897 /* user arg at position 0 */
56cbdf4ab0c570 Steven Rostedt 2025-10-20 898 case __NR_access:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 899 case __NR_acct:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 900 case __NR_chdir:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 901 case __NR_chown:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 902 case __NR_chmod:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 903 case __NR_chroot:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 904 case __NR_creat:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 905 case __NR_delete_module:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 906 case __NR_execve:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 907 case __NR_fsopen:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 908 case __NR_lchown:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 909 case __NR_open:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 910 case __NR_memfd_create:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 911 case __NR_mkdir:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 912 case __NR_mknod:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 913 case __NR_mq_open:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 914 case __NR_mq_unlink:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 915 case __NR_readlink:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 916 case __NR_rmdir:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 917 case __NR_shmdt:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 918 case __NR_statfs:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 919 case __NR_swapon:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 920 case __NR_swapoff:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 921 case __NR_truncate:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 922 case __NR_unlink:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 923 case __NR_umount2:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 924 case __NR_utime:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 925 case __NR_utimes:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 926 sys_data->user_mask = BIT(0);
56cbdf4ab0c570 Steven Rostedt 2025-10-20 927 break;
56cbdf4ab0c570 Steven Rostedt 2025-10-20 928 /* user arg at position 1 */
56cbdf4ab0c570 Steven Rostedt 2025-10-20 929 case __NR_execveat:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 930 case __NR_faccessat:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 931 case __NR_faccessat2:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 932 case __NR_finit_module:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 933 case __NR_fchmodat:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 934 case __NR_fchmodat2:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 935 case __NR_fchownat:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 936 case __NR_fgetxattr:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 937 case __NR_flistxattr:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 938 case __NR_fsetxattr:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 939 case __NR_fspick:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 940 case __NR_fremovexattr:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 941 case __NR_futimesat:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 942 case __NR_inotify_add_watch:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 943 case __NR_mkdirat:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 944 case __NR_mknodat:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 945 case __NR_mount_setattr:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 946 case __NR_name_to_handle_at:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 947 #if defined(__ARCH_WANT_NEW_STAT) || defined(__ARCH_WANT_STAT64)
56cbdf4ab0c570 Steven Rostedt 2025-10-20 948 case __NR_newfstatat:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 949 #endif
56cbdf4ab0c570 Steven Rostedt 2025-10-20 950 case __NR_openat:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 951 case __NR_openat2:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 952 case __NR_open_tree:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 953 case __NR_open_tree_attr:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 954 case __NR_readlinkat:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 955 case __NR_quotactl:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 956 case __NR_syslog:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 957 case __NR_statx:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 958 case __NR_unlinkat:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 @959 case __NR_utimensat:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 960 sys_data->user_mask = BIT(1);
56cbdf4ab0c570 Steven Rostedt 2025-10-20 961 break;
56cbdf4ab0c570 Steven Rostedt 2025-10-20 962 /* user arg at position 2 */
56cbdf4ab0c570 Steven Rostedt 2025-10-20 963 case __NR_init_module:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 964 case __NR_fsconfig:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 965 sys_data->user_mask = BIT(2);
56cbdf4ab0c570 Steven Rostedt 2025-10-20 966 break;
56cbdf4ab0c570 Steven Rostedt 2025-10-20 967 /* user arg at position 4 */
56cbdf4ab0c570 Steven Rostedt 2025-10-20 968 case __NR_fanotify_mark:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 969 sys_data->user_mask = BIT(4);
56cbdf4ab0c570 Steven Rostedt 2025-10-20 970 break;
f5da5a4c232ae3 Steven Rostedt 2025-10-20 971 /* 2 user args, 0 and 1 */
f5da5a4c232ae3 Steven Rostedt 2025-10-20 972 case __NR_add_key:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 973 case __NR_getxattr:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 974 case __NR_lgetxattr:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 975 case __NR_lremovexattr:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 976 case __NR_link:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 977 case __NR_listxattr:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 978 case __NR_llistxattr:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 979 case __NR_lsetxattr:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 980 case __NR_pivot_root:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 981 case __NR_removexattr:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 982 case __NR_rename:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 983 case __NR_request_key:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 984 case __NR_setxattr:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 985 case __NR_symlink:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 986 sys_data->user_mask = BIT(0) | BIT(1);
f5da5a4c232ae3 Steven Rostedt 2025-10-20 987 break;
f5da5a4c232ae3 Steven Rostedt 2025-10-20 988 /* 2 user args, 0 and 2 */
f5da5a4c232ae3 Steven Rostedt 2025-10-20 989 case __NR_symlinkat:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 990 sys_data->user_mask = BIT(0) | BIT(2);
f5da5a4c232ae3 Steven Rostedt 2025-10-20 991 break;
f5da5a4c232ae3 Steven Rostedt 2025-10-20 992 /* 2 user args, 1 and 3 */
f5da5a4c232ae3 Steven Rostedt 2025-10-20 993 case __NR_getxattrat:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 994 case __NR_linkat:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 995 case __NR_listxattrat:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 996 case __NR_move_mount:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 997 case __NR_renameat:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 998 case __NR_renameat2:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 999 case __NR_removexattrat:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1000 case __NR_setxattrat:
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1001 sys_data->user_mask = BIT(1) | BIT(3);
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1002 break;
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1003 case __NR_mount: /* Just dev_name and dir_name, TODO add type */
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1004 sys_data->user_mask = BIT(0) | BIT(1) | BIT(2);
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1005 break;
56cbdf4ab0c570 Steven Rostedt 2025-10-20 1006 default:
56cbdf4ab0c570 Steven Rostedt 2025-10-20 1007 sys_data->user_mask = 0;
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1008 return;
56cbdf4ab0c570 Steven Rostedt 2025-10-20 1009 }
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1010
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1011 if (sys_data->user_arg_size < 0)
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1012 return;
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1013
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1014 /*
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1015 * The user_arg_size can only be used when the system call
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1016 * is reading only a single address from user space.
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1017 */
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1018 mask = sys_data->user_mask;
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1019 if (WARN_ON(mask & (mask - 1)))
f5da5a4c232ae3 Steven Rostedt 2025-10-20 1020 sys_data->user_arg_size = -1;
56cbdf4ab0c570 Steven Rostedt 2025-10-20 1021 }
56cbdf4ab0c570 Steven Rostedt 2025-10-20 1022 Signature · 2 lines
0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
Re: [PATCH v4 07/13] tracing: Allow syscall trace events to read more than one user parameter
Quoted 8 lines · kernel test robot
kernel/trace/trace_syscalls.c:877:7: error: use of undeclared identifier '__NR_mq_timedsend'
877 | case__NR_mq_timedsend:
| ^
kernel/trace/trace_syscalls.c:898:7: error: use of undeclared identifier '__NR_access'898 | case __NR_access: | ^ kernel/trace/trace_syscalls.c:901:7: error: use of undeclared identifier '__NR_chown' 901 | case __NR_chown:
You need to exclude those if the numbers are not available. It does not compare about all of them, like it is fine with __NR_openat2.
Risc-V probably did not wire them all up. So they started to use __NR_faccessat2 in favour of __NR_access. "Current" glibc probably uses just the latest one so why bother.
x86 supported it since day one so they need to keep supporting it. The newcomer just started with the last variant.
Sebastian
PATCH v4 8/13 tracing: Add a config and syscall_user_buf_size file to limit amount written
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
When a system call that can copy user space addresses into the ring buffer, it can copy up to 511 bytes of data. This can waste precious ring buffer space if the user isn't interested in the output. Add a new file "syscall_user_buf_size" that gets initialized to a new config CONFIG_SYSCALL_BUF_SIZE_DEFAULT that defaults to 63.
The config also is used to limit how much perf can read from user space.
Also lower the max down to 165, as this isn't to record everything that a system call may be passing through to the kernel. 165 is more than enough.
The reason for 165 is because adding one for the nul terminating byte, as well as possibly needing to append the "..." string turns it into 170 bytes. As this needs to save up to 3 arguments and 3 * 170 is 510 which fits nicely in 512 bytes (a power of 2).
--- Documentation/trace/ftrace.rst | 8 ++++++ kernel/trace/Kconfig | 14 +++++++++ kernel/trace/trace.c | 52 ++++++++++++++++++++++++++++++++++ kernel/trace/trace.h | 3 ++ kernel/trace/trace_syscalls.c | 50 ++++++++++++++++++-------------- 5 files changed, 105 insertions(+), 22 deletions(-)
Documentation/trace/ftrace.rst +8 -0
@@ -366,6 +366,14 @@ of ftrace. Here is a list of some of the key files: for each function. The displayed address is the patch-site address and can differ from /proc/kallsyms address. + syscall_user_buf_size: + + Some system call trace events will record the data from a user + space address that one of the parameters point to. The amount of + data per event is limited. This file holds the max number of bytes + that will be recorded into the ring buffer to hold this data. + The max value is currently 165. + dyn_ftrace_total_info: This file is for debugging purposes. The number of functions that
kernel/trace/Kconfig +14 -0
@@ -575,6 +575,20 @@ config FTRACE_SYSCALLS help Basic tracer to catch the syscall entry and exit events. +config TRACE_SYSCALL_BUF_SIZE_DEFAULT + int "System call user read max size" + range 0 165 + default 63 + depends on FTRACE_SYSCALLS + help + Some system call trace events will record the data from a user + space address that one of the parameters point to. The amount of + data per event is limited. That limit is set by this config and + this config also affects how much user space data perf can read. + + For a tracing instance, this size may be changed by writing into + its syscall_user_buf_size file. + config TRACER_SNAPSHOT bool "Create a snapshot trace buffer" select TRACER_MAX_TRACE
kernel/trace/trace.c +52 -0
82 lines not on this page · raw message
kernel/trace/trace.h +3 -0
@@ -131,6 +131,8 @@ enum trace_type {#define HIST_STACKTRACE_SIZE (HIST_STACKTRACE_DEPTH * sizeof(unsigned long))#define HIST_STACKTRACE_SKIP 5 +#define SYSCALL_FAULT_USER_MAX 165 +/* * syscalls are special, and need special handling, this is why * they are not included in trace_entries.h
@@ -430,6 +432,7 @@ struct trace_array {int function_enabled;#endif int no_filter_buffering_ref; +unsigned int syscall_buf_sz;struct list_head hist_vars;#ifdef CONFIG_TRACER_SNAPSHOT struct cond_snapshot *cond_snapshot;
kernel/trace/trace_syscalls.c +28 -22
115 lines not on this page · raw message
Signature · 1 line
2.51.0
PATCH v4 9/13 tracing: Show printable characters in syscall arrays
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
When displaying the contents of the user space data passed to the kernel, instead of just showing the array values, also print any printable content.
Instead of just:
bash-1113 [003] ..... 3433.290654: sys_write(fd: 2, buf: 0x555a8deeddb0 (72:6f:6f:74:40:64:65:62:69:61:6e:2d:78:38:36:2d:36:34:3a:7e:23:20), count: 0x16)
Display:
bash-1113 [003] ..... 3433.290654: sys_write(fd: 2, buf: 0x555a8deeddb0 (72:6f:6f:74:40:64:65:62:69:61:6e:2d:78:38:36:2d:36:34:3a:7e:23:20) "root@debian-x86-64:~# ", count: 0x16)
This only affects tracing and does not affect perf, as this only updates the output from the kernel. The output from perf is via user space. This may change by an update to libtraceevent that will then update perf to have this as well.
--- kernel/trace/trace_syscalls.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
kernel/trace/trace_syscalls.c +21 -0
@@ -155,6 +155,8 @@ print_syscall_enter(struct trace_iterator *iter, int flags,trace_seq_printf (s,"%s(" , entry->name);for (i =0 ; i < entry->nb_args; i++) { +bool printable = false; +char *str;if (trace_seq_has_overflowed (s)) goto end;
@@ -193,8 +195,11 @@ print_syscall_enter(struct trace_iterator *iter, int flags, val = trace->args[entry->user_arg_size]; + str = ptr;trace_seq_puts (s," (" );for (int x =0 ; x < len; x++, ptr++) { +if (isascii (*ptr) &&isprint (*ptr)) + printable = true;if (x)trace_seq_putc (s,':' );trace_seq_printf (s,"%02x" , *ptr);
@@ -203,6 +208,22 @@ print_syscall_enter(struct trace_iterator *iter, int flags,trace_seq_printf (s,", %s" ,EXTRA );trace_seq_putc (s,')' ); + +/* If nothing is printable, don't bother printing anything */ +if (!printable) +continue ; + +trace_seq_puts (s," \"" ); +for (int x =0 ; x < len; x++) { +if (isascii (str[x]) &&isprint (str[x])) +trace_seq_putc (s, str[x]); +else +trace_seq_putc (s,'.' ); + } +if (len < val) +trace_seq_printf (s,"\"%s" ,EXTRA ); +else +trace_seq_putc (s,'"' ); }trace_seq_putc (s,')' );
Signature · 1 line
2.51.0
PATCH v4 10/13 tracing: Add trace_seq_pop() and seq_buf_pop()
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
In order to allow an interface to remove an added character from the trace_seq and seq_buf descriptors, add helper functions trace_seq_pop() and seq_buf_pop().
--- include/linux/seq_buf.h | 17 +++++++++++++++++ include/linux/trace_seq.h | 13 +++++++++++++ 2 files changed, 30 insertions(+)
include/linux/seq_buf.h +17 -0
@@ -149,6 +149,23 @@ static inline void seq_buf_commit(struct seq_buf *s, int num) } } +/** +* seq_buf_pop - pop off the last written character +* @s: the seq_buf handle +* +* Removes the last written character to the seq_buf @s. +* +* Returns the last character or -1 if it is empty. +*/ +static inline int seq_buf_pop (struct seq_buf *s) +{ +if (!s->len) +return -1 ; + + s->len--; +return (unsigned int )s->buffer[s->len]; +} + extern __printf(2, 3) int seq_buf_printf(struct seq_buf *s, const char *fmt, ...); extern __printf(2, 0)
include/linux/trace_seq.h +13 -0
@@ -80,6 +80,19 @@ static inline bool trace_seq_has_overflowed(struct trace_seq *s)return s->full ||seq_buf_has_overflowed (&s->seq); } +/** +* trace_seq_pop - pop off the last written character +* @s: trace sequence descriptor +* +* Removes the last written character to the trace_seq @s. +* +* Returns the last character or -1 if it is empty. +*/ +static inline int trace_seq_pop (struct trace_seq *s) +{ +return seq_buf_pop (&s->seq); +} +/* * Currently only defined when tracing is enabled. */
Signature · 1 line
2.51.0
PATCH v4 11/13 tracing: Add parsing of flags to the sys_enter_openat trace event
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
Add some logic to give the openat system call trace event a bit more human readable information:
syscalls:sys_enter_openat: dfd: 0xffffff9c, filename: 0x7f0053dc121c "/etc/ld.so.cache", flags: O_RDONLY|O_CLOEXEC, mode: 0000
The above is output from "perf script" and now shows the flags used by the openat system call.
Since the output from tracing is in the kernel, it can also remove the mode field when not used (when flags does not contain O_CREATE|O_TMPFILE)
touch-1185 [002] ...1. 1291.690154: sys_openat(dfd: 4294967196, filename: 139785545139344 "/usr/lib/locale/locale-archive", flags: O_RDONLY|O_CLOEXEC) touch-1185 [002] ...1. 1291.690504: sys_openat(dfd: 18446744073709551516, filename: 140733603151330 "/tmp/x", flags: O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, mode: 0666)
As system calls have a fixed ABI, their trace events can be extended. This currently only updates the openat system call, but others may be extended in the future.
--- kernel/trace/trace_syscalls.c | 192 ++++++++++++++++++++++++++++++++-- 1 file changed, 182 insertions(+), 10 deletions(-)
kernel/trace/trace_syscalls.c +182 -10
@@ -127,6 +127,116 @@ const char *get_syscall_name(int syscall)/* Added to user strings or arrays when max limit is reached */ #define EXTRA "..." +static void get_dynamic_len_ptr (struct syscall_trace_enter *trace, +struct syscall_metadata *entry, +int *offset_p,int *len_p,unsigned char **ptr_p) +{ +unsigned char *ptr; +int offset = *offset_p; +int val; + +/* This arg points to a user space string */ + ptr = (void *)trace->args +sizeof (long ) * entry->nb_args + offset; + val = *(int *)ptr; + +/* The value is a dynamic string (len << 16 | offset) */ + ptr = (void *)trace + (val &0xffff ); + *len_p = val >>16 ; + offset +=4 ; + + *ptr_p = ptr; + *offset_p = offset; +} + +static enum print_line_t +sys_enter_openat_print (struct syscall_trace_enter *trace,struct syscall_metadata *entry, +struct trace_seq *s,struct trace_event *event) +{ +unsigned char *ptr; +int offset =0 ; +int bits, len; +bool done = false; +static const struct trace_print_flags __flags[] = + { + {O_TMPFILE ,"O_TMPFILE" }, + {O_WRONLY ,"O_WRONLY" }, + {O_RDWR ,"O_RDWR" }, + {O_CREAT ,"O_CREAT" }, + {O_EXCL ,"O_EXCL" }, + {O_NOCTTY ,"O_NOCTTY" }, + {O_TRUNC ,"O_TRUNC" }, + {O_APPEND ,"O_APPEND" }, + {O_NONBLOCK ,"O_NONBLOCK" }, + {O_DSYNC ,"O_DSYNC" }, + {O_DIRECT ,"O_DIRECT" }, + {O_LARGEFILE ,"O_LARGEFILE" }, + {O_DIRECTORY ,"O_DIRECTORY" }, + {O_NOFOLLOW ,"O_NOFOLLOW" }, + {O_NOATIME ,"O_NOATIME" }, + {O_CLOEXEC ,"O_CLOEXEC" }, + {-1 ,NULL } + }; + +trace_seq_printf (s,"%s(" , entry->name); + +for (int i =0 ; !done && i < entry->nb_args; i++) { + +if (trace_seq_has_overflowed (s)) + goto end; + +if (i) +trace_seq_puts (s,", " ); + +switch (i) { +case 2 : + bits = trace->args[2 ]; + +trace_seq_puts (s,"flags: " ); + +/* No need to show mode when not creating the file */ +if (!(bits & (O_CREAT |O_TMPFILE ))) + done = true; + +if (!(bits &O_ACCMODE )) { +if (!bits) { +trace_seq_puts (s,"O_RDONLY" ); +continue ; + } +trace_seq_puts (s,"O_RDONLY|" ); + } + +trace_print_flags_seq (s,"|" , bits, __flags); +/* +* trace_print_flags_seq() adds a '\0' to the +* buffer, but this needs to append more to the seq. +*/ +if (!trace_seq_has_overflowed (s)) +trace_seq_pop (s); + +continue ; +case 3 : +trace_seq_printf (s,"%s: 0%03o" , entry->args[i], + (unsigned int )trace->args[i]); +continue ; + } + +trace_seq_printf (s,"%s: %lu" , entry->args[i], + trace->args[i]); + +if (!(BIT (i) & entry->user_mask)) +continue ; + +get_dynamic_len_ptr (trace, entry, &offset, &len, &ptr); +trace_seq_printf (s," \"%.*s\"" , len, ptr); + } + +trace_seq_putc (s,')' ); +end: +trace_seq_putc (s,'\n' ); + +return trace_handle_return (s); +} +static enum print_line_t print_syscall_enter (struct trace_iterator *iter,int flags,struct trace_event *event)
@@ -152,6 +262,15 @@ print_syscall_enter(struct trace_iterator *iter, int flags, goto end; } +switch (entry->syscall_nr) { +case __NR_openat: +if (!tr || !(tr->trace_flags &TRACE_ITER_VERBOSE )) +return sys_enter_openat_print (trace, entry, s, event); +break ; +default : +break ; + } +trace_seq_printf (s,"%s(" , entry->name);for (i =0 ; i < entry->nb_args; i++) {
@@ -179,14 +298,7 @@ print_syscall_enter(struct trace_iterator *iter, int flags,if (!(BIT (i) & entry->user_mask))continue ; -/* This arg points to a user space string */ - ptr = (void *)trace->args +sizeof (long ) * entry->nb_args + offset; - val = *(int *)ptr; - -/* The value is a dynamic string (len << 16 | offset) */ - ptr = (void *)ent + (val &0xffff ); - len = val >>16 ; - offset +=4 ; +get_dynamic_len_ptr (trace, entry, &offset, &len, &ptr);if (entry->user_arg_size <0 || entry->user_arg_is_str) {trace_seq_printf (s," \"%.*s\"" , len, ptr);
@@ -269,6 +381,62 @@ print_syscall_exit(struct trace_iterator *iter, int flags, .size = sizeof(_type), .align = __alignof__(_type), \ .is_signed = is_signed_type(_type), .filter_type = FILTER_OTHER } +/* When len=0, we just calculate the needed length */ +#define LEN_OR_ZERO (len ? len - pos : 0) + +static int __init +sys_enter_openat_print_fmt (struct syscall_metadata *entry,char *buf,int len) +{ +int pos =0 ; + + pos +=snprintf (buf + pos,LEN_OR_ZERO , +"\"dfd: 0x%%08lx, filename: 0x%%08lx \\\"%%s\\\", flags: %%s%%s, mode: 0%%03o\"," ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +" ((unsigned long)(REC->dfd))," ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +" ((unsigned long)(REC->filename))," ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +" __get_str(__filename_val)," ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +" (REC->flags & ~3) && !(REC->flags & 3) ? \"O_RDONLY|\" : \"\", " ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +" REC->flags ? __print_flags(REC->flags, \"|\", " ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +"{ 0x%x, \"O_WRONLY\" }, " ,O_WRONLY ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +"{ 0x%x, \"O_RDWR\" }, " ,O_RDWR ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +"{ 0x%x, \"O_CREAT\" }, " ,O_CREAT ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +"{ 0x%x, \"O_EXCL\" }, " ,O_EXCL ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +"{ 0x%x, \"O_NOCTTY\" }, " ,O_NOCTTY ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +"{ 0x%x, \"O_TRUNC\" }, " ,O_TRUNC ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +"{ 0x%x, \"O_APPEND\" }, " ,O_APPEND ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +"{ 0x%x, \"O_NONBLOCK\" }, " ,O_NONBLOCK ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +"{ 0x%x, \"O_DSYNC\" }, " ,O_DSYNC ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +"{ 0x%x, \"O_DIRECT\" }, " ,O_DIRECT ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +"{ 0x%x, \"O_LARGEFILE\" }, " ,O_LARGEFILE ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +"{ 0x%x, \"O_DIRECTORY\" }, " ,O_DIRECTORY ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +"{ 0x%x, \"O_NOFOLLOW\" }, " ,O_NOFOLLOW ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +"{ 0x%x, \"O_NOATIME\" }, " ,O_NOATIME ); + pos +=snprintf (buf + pos,LEN_OR_ZERO , +"{ 0x%x, \"O_CLOEXEC\" }) : \"O_RDONLY\", " ,O_CLOEXEC ); + + pos +=snprintf (buf + pos,LEN_OR_ZERO , +" ((unsigned long)(REC->mode))" ); +return pos; +} +static int __init __set_enter_print_fmt (struct syscall_metadata *entry,char *buf,int len) {
@@ -276,8 +444,12 @@ __set_enter_print_fmt(struct syscall_metadata *entry, char *buf, int len)int i;int pos =0 ; -/* When len=0, we just calculate the needed length */ -#define LEN_OR_ZERO (len ? len - pos : 0) +switch (entry->syscall_nr) { +case __NR_openat: +return sys_enter_openat_print_fmt (entry, buf, len); +default : +break ; + } pos +=snprintf (buf + pos,LEN_OR_ZERO ,"\"" );for (i =0 ; i < entry->nb_args; i++) {
Signature · 1 line
2.51.0
PATCH v4 12/13 tracing: Check for printable characters when printing field dyn strings
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
When the "fields" option is enabled, it prints each trace event field based on its type. But a dynamic array and a dynamic string can both have a "char *" type. Printing it as a string can cause escape characters to be printed and mess up the output of the trace.
For dynamic strings, test if there are any non-printable characters, and if so, print both the string with the non printable characters as '.', and the print the hex value of the array.
--- kernel/trace/trace_output.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-)
kernel/trace/trace_output.c +25 -2
@@ -950,7 +950,9 @@ static void print_fields(struct trace_iterator *iter, struct trace_event_call *cint offset;int len;int ret; +int i;void *pos; +char *str;list_for_each_entry_reverse (field, head, link) {trace_seq_printf (&iter->seq," %s=" , field->name);
@@ -977,8 +979,29 @@ static void print_fields(struct trace_iterator *iter, struct trace_event_call *ctrace_seq_puts (&iter->seq,"<OVERFLOW>" );break ; } - pos = (void *)iter->ent + offset; -trace_seq_printf (&iter->seq,"%.*s" , len, (char *)pos); + str = (char *)iter->ent + offset; +/* Check if there's any non printable strings */ +for (i =0 ; i < len; i++) { +if (str[i] && !(isascii (str[i]) &&isprint (str[i]))) +break ; + } +if (i < len) { +for (i =0 ; i < len; i++) { +if (isascii (str[i]) &&isprint (str[i])) +trace_seq_putc (&iter->seq, str[i]); +else +trace_seq_putc (&iter->seq,'.' ); + } +trace_seq_puts (&iter->seq," (" ); +for (i =0 ; i < len; i++) { +if (i) +trace_seq_putc (&iter->seq,':' ); +trace_seq_printf (&iter->seq,"%02x" , str[i]); + } +trace_seq_putc (&iter->seq,')' ); + }else { +trace_seq_printf (&iter->seq,"%.*s" , len, str); + }break ;case FILTER_PTR_STRING :if (!iter->fmt_size)
Signature · 1 line
2.51.0
PATCH v4 13/13 tracing: Have persistent ring buffer print syscalls normally
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
The persistent ring buffer from a previous boot has to be careful printing events as the print formats of random events can have pointers to strings and such that are not available.
Ftrace static events (like the function tracer event) are stable and are printed normally.
System call event formats are also stable. Allow them to be printed normally as well:
Instead of:
<...>-1 [005] ...1. 57.240405: sys_enter_waitid: __syscall_nr=0xf7 (247) which=0x1 (1) upid=0x499 (1177) infop=0x7ffd5294d690 (140725988939408) options=0x5 (5) ru=0x0 (0) <...>-1 [005] ...1. 57.240433: sys_exit_waitid: __syscall_nr=0xf7 (247) ret=0x0 (0) <...>-1 [005] ...1. 57.240437: sys_enter_rt_sigprocmask: __syscall_nr=0xe (14) how=0x2 (2) nset=0x7ffd5294d7c0 (140725988939712) oset=0x0 (0) sigsetsize=0x8 (8) <...>-1 [005] ...1. 57.240438: sys_exit_rt_sigprocmask: __syscall_nr=0xe (14) ret=0x0 (0) <...>-1 [005] ...1. 57.240442: sys_enter_close: __syscall_nr=0x3 (3) fd=0x4 (4) <...>-1 [005] ...1. 57.240463: sys_exit_close: __syscall_nr=0x3 (3) ret=0x0 (0) <...>-1 [005] ...1. 57.240485: sys_enter_openat: __syscall_nr=0x101 (257) dfd=0xffffffffffdfff9c (-2097252) filename=(0xffff8b81639ca01c) flags=0x80000 (524288) mode=0x0 (0) __filename_val=/run/systemd/reboot-param <...>-1 [005] ...1. 57.240555: sys_exit_openat: __syscall_nr=0x101 (257) ret=0xffffffffffdffffe (-2097154) <...>-1 [005] ...1. 57.240571: sys_enter_openat: __syscall_nr=0x101 (257) dfd=0xffffffffffdfff9c (-2097252) filename=(0xffff8b81639ca01c) flags=0x80000 (524288) mode=0x0 (0) __filename_val=/run/systemd/reboot-param <...>-1 [005] ...1. 57.240620: sys_exit_openat: __syscall_nr=0x101 (257) ret=0xffffffffffdffffe (-2097154) <...>-1 [005] ...1. 57.240629: sys_enter_writev: __syscall_nr=0x14 (20) fd=0x3 (3) vec=0x7ffd5294ce50 (140725988937296) vlen=0x7 (7) <...>-1 [005] ...1. 57.242281: sys_exit_writev: __syscall_nr=0x14 (20) ret=0x24 (36) <...>-1 [005] ...1. 57.242286: sys_enter_reboot: __syscall_nr=0xa9 (169) magic1=0xfee1dead (4276215469) magic2=0x28121969 (672274793) cmd=0x1234567 (19088743) arg=0x0 (0)
Have:
<...>-1 [000] ...1. 91.446011: sys_waitid(which: 1, upid: 0x4d2, infop: 0x7ffdccdadfd0, options: 5, ru: 0) <...>-1 [000] ...1. 91.446042: sys_waitid -> 0x0 <...>-1 [000] ...1. 91.446045: sys_rt_sigprocmask(how: 2, nset: 0x7ffdccdae100, oset: 0, sigsetsize: 8) <...>-1 [000] ...1. 91.446047: sys_rt_sigprocmask -> 0x0 <...>-1 [000] ...1. 91.446051: sys_close(fd: 4) <...>-1 [000] ...1. 91.446073: sys_close -> 0x0 <...>-1 [000] ...1. 91.446095: sys_openat(dfd: 18446744073709551516, filename: 139732544945794 "/run/systemd/reboot-param", flags: O_RDONLY|O_CLOEXEC) <...>-1 [000] ...1. 91.446165: sys_openat -> 0xfffffffffffffffe <...>-1 [000] ...1. 91.446182: sys_openat(dfd: 18446744073709551516, filename: 139732544945794 "/run/systemd/reboot-param", flags: O_RDONLY|O_CLOEXEC) <...>-1 [000] ...1. 91.446233: sys_openat -> 0xfffffffffffffffe <...>-1 [000] ...1. 91.446242: sys_writev(fd: 3, vec: 0x7ffdccdad790, vlen: 7) <...>-1 [000] ...1. 91.447877: sys_writev -> 0x24 <...>-1 [000] ...1. 91.447883: sys_reboot(magic1: 0xfee1dead, magic2: 0x28121969, cmd: 0x1234567, arg: 0)
---
Changes since v3: https://lore.kernel.org/[email protected]
- Fixed up the code to determine if it's a syscall event or not when CONFIG_FTRACE_SYSCALLS is not defined (kernel test robot)
kernel/trace/trace.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-)
kernel/trace/trace.c +23 -4
@@ -20,6 +20,7 @@#include <linux/security.h> #include <linux/seq_file.h> #include <linux/irqflags.h> +#include <linux/syscalls.h> #include <linux/debugfs.h> #include <linux/tracefs.h> #include <linux/pagemap.h>
@@ -4219,6 +4220,22 @@ static void test_cpu_buff_start(struct trace_iterator *iter) iter->cpu); } +#ifdef CONFIG_FTRACE_SYSCALLS +static bool is_syscall_event (struct trace_event *event) +{ +return (event->funcs == &enter_syscall_print_funcs) || + (event->funcs == &exit_syscall_print_funcs); + +} +#define syscall_buf_size CONFIG_TRACE_SYSCALL_BUF_SIZE_DEFAULT +#else +static inline bool is_syscall_event (struct trace_event *event) +{ +return false; +} +#define syscall_buf_size 0 +#endif /* CONFIG_FTRACE_SYSCALLS */ +static enum print_line_t print_trace_fmt (struct trace_iterator *iter) {struct trace_array *tr = iter->tr;
@@ -4251,10 +4268,12 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter)* safe to use if the array has delta offsets * Force printing via the fields. */ -if ((tr->text_delta) && - event->type > __TRACE_LAST_TYPE) +if ((tr->text_delta)) { +/* ftrace and system call events are still OK */ +if ((event->type > __TRACE_LAST_TYPE) && + !is_syscall_event (event))return print_event_fields (iter, event); - + }return event->funcs->trace (iter, sym_flags, event); }
@@ -11436,7 +11455,7 @@ __init static int tracer_alloc_buffers(void) global_trace.flags =TRACE_ARRAY_FL_GLOBAL ; - global_trace.syscall_buf_sz =CONFIG_TRACE_SYSCALL_BUF_SIZE_DEFAULT ; + global_trace.syscall_buf_sz = syscall_buf_size;INIT_LIST_HEAD (&global_trace.systems);INIT_LIST_HEAD (&global_trace.events);
Signature · 1 line
2.51.0