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
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;
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, ')');
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, "\"");
", ((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
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);
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;
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;
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;
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);
}
}
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);
}
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:
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)) ||