diff options
Diffstat (limited to 'kernel/trace/trace_syscalls.c')
| -rw-r--r-- | kernel/trace/trace_syscalls.c | 121 |
1 files changed, 87 insertions, 34 deletions
diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index ed9332f8bdf8..3f3cdfc9958e 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -124,7 +124,7 @@ const char *get_syscall_name(int syscall) return entry->name; } -/* Added to user strings when max limit is reached */ +/* Added to user strings or arrays when max limit is reached */ #define EXTRA "..." static enum print_line_t @@ -136,9 +136,8 @@ print_syscall_enter(struct trace_iterator *iter, int flags, struct trace_entry *ent = iter->ent; struct syscall_trace_enter *trace; struct syscall_metadata *entry; - int i, syscall, val; + int i, syscall, val, len; unsigned char *ptr; - int len; trace = (typeof(trace))ent; syscall = trace->nr; @@ -185,7 +184,23 @@ print_syscall_enter(struct trace_iterator *iter, int flags, ptr = (void *)ent + (val & 0xffff); len = val >> 16; - trace_seq_printf(s, " \"%.*s\"", len, ptr); + if (entry->user_arg_size < 0) { + trace_seq_printf(s, " \"%.*s\"", len, ptr); + continue; + } + + val = trace->args[entry->user_arg_size]; + + trace_seq_puts(s, " ("); + for (int x = 0; x < len; x++, ptr++) { + if (x) + trace_seq_putc(s, ':'); + trace_seq_printf(s, "%02x", *ptr); + } + if (len < val) + trace_seq_printf(s, ", %s", EXTRA); + + trace_seq_putc(s, ')'); } trace_seq_putc(s, ')'); @@ -250,8 +265,11 @@ __set_enter_print_fmt(struct syscall_metadata *entry, char *buf, int len) if (!(BIT(i) & entry->user_mask)) continue; - /* Add the format for the user space string */ - pos += snprintf(buf + pos, LEN_OR_ZERO, " \\\"%%s\\\""); + /* Add the format for the user space string or array */ + if (entry->user_arg_size < 0) + pos += snprintf(buf + pos, LEN_OR_ZERO, " \\\"%%s\\\""); + else + pos += snprintf(buf + pos, LEN_OR_ZERO, " (%%s)"); } pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); @@ -260,9 +278,14 @@ __set_enter_print_fmt(struct syscall_metadata *entry, char *buf, int len) ", ((unsigned long)(REC->%s))", entry->args[i]); if (!(BIT(i) & entry->user_mask)) continue; - /* The user space string for arg has name __<arg>_val */ - pos += snprintf(buf + pos, LEN_OR_ZERO, ", __get_str(__%s_val)", - entry->args[i]); + /* The user space data for arg has name __<arg>_val */ + if (entry->user_arg_size < 0) { + pos += snprintf(buf + pos, LEN_OR_ZERO, ", __get_str(__%s_val)", + entry->args[i]); + } else { + pos += snprintf(buf + pos, LEN_OR_ZERO, ", __print_dynamic_array(__%s_val, 1)", + entry->args[i]); + } } #undef LEN_OR_ZERO @@ -333,9 +356,9 @@ static int __init syscall_enter_define_fields(struct trace_event_call *call) idx = ffs(mask) - 1; /* - * User space strings are faulted into a temporary buffer and then - * added as a dynamic string to the end of the event. - * The user space string name for the arg pointer is "__<arg>_val". + * 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); @@ -431,9 +454,11 @@ static char *sys_fault_user(struct syscall_metadata *sys_data, struct syscall_user_buffer *sbuf, unsigned long *args, unsigned int *data_size) { + 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; + bool array = false; char *ptr; char *buf; @@ -441,27 +466,43 @@ static char *sys_fault_user(struct syscall_metadata *sys_data, ptr = (char *)args[idx]; *data_size = 0; + /* + * If this system call event has a size argument, use + * it to define how much of user space memory to read, + * and read it as an array and not a string. + */ + 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; + } + buf = trace_user_fault_read(&sbuf->buf, ptr, size, - syscall_copy_user, &size); + syscall_copy, &size); if (!buf) return NULL; - /* Replace any non-printable characters with '.' */ - for (int i = 0; i < size; i++) { - if (!isprint(buf[i])) - buf[i] = '.'; - } + /* For strings, replace any non-printable characters with '.' */ + if (!array) { + for (int i = 0; i < size; i++) { + if (!isprint(buf[i])) + buf[i] = '.'; + } - /* - * 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; - } else { - buf[size++] = '\0'; + /* + * 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; + } else { + buf[size++] = '\0'; + } } *data_size = size; @@ -492,7 +533,7 @@ syscall_get_data(struct syscall_metadata *sys_data, unsigned long *args, static void syscall_put_data(struct syscall_metadata *sys_data, struct syscall_trace_enter *entry, - char *buffer, int size) + char *buffer, int size, int user_size) { void *ptr; int val; @@ -510,13 +551,16 @@ static void syscall_put_data(struct syscall_metadata *sys_data, val = (ptr - (void *)entry) + 4; /* Store the offset and the size into the meta data */ - *(int *)ptr = val | (size << 16); + *(int *)ptr = val | (user_size << 16); + + if (WARN_ON_ONCE((ptr - (void *)entry + user_size) > size)) + user_size = 0; /* Nothing to do if the user space was empty or faulted */ - if (size) { + if (user_size) { /* Now store the user space data into the event */ ptr += 4; - memcpy(ptr, buffer, size); + memcpy(ptr, buffer, user_size); } } @@ -580,7 +624,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, user_size); + syscall_put_data(sys_data, entry, user_ptr, size, user_size); trace_event_buffer_commit(&fbuffer); } @@ -727,7 +771,16 @@ static void check_faultable_syscall(struct trace_event_call *call, int nr) if (sys_data->enter_event != call) return; + sys_data->user_arg_size = -1; + switch (nr) { + /* user arg 1 with size arg at 2 */ + case __NR_write: + case __NR_mq_timedsend: + case __NR_pwrite64: + sys_data->user_mask = BIT(1); + sys_data->user_arg_size = 2; + break; /* user arg at position 0 */ #ifdef __NR_access case __NR_access: @@ -1065,7 +1118,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, user_size); + syscall_put_data(sys_data, rec, user_ptr, size, user_size); if ((valid_prog_array && !perf_call_bpf_enter(sys_data->enter_event, fake_regs, sys_data, rec)) || |