Merge tag 'block-5.13-2021-05-09' of git://git.kernel.dk/linux-block
[platform/kernel/linux-rpi.git] / kernel / trace / bpf_trace.c
index 0d23755..d2d7cf6 100644 (file)
@@ -372,188 +372,34 @@ static const struct bpf_func_proto *bpf_get_probe_write_proto(void)
        return &bpf_probe_write_user_proto;
 }
 
-static void bpf_trace_copy_string(char *buf, void *unsafe_ptr, char fmt_ptype,
-               size_t bufsz)
-{
-       void __user *user_ptr = (__force void __user *)unsafe_ptr;
-
-       buf[0] = 0;
-
-       switch (fmt_ptype) {
-       case 's':
-#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
-               if ((unsigned long)unsafe_ptr < TASK_SIZE) {
-                       strncpy_from_user_nofault(buf, user_ptr, bufsz);
-                       break;
-               }
-               fallthrough;
-#endif
-       case 'k':
-               strncpy_from_kernel_nofault(buf, unsafe_ptr, bufsz);
-               break;
-       case 'u':
-               strncpy_from_user_nofault(buf, user_ptr, bufsz);
-               break;
-       }
-}
-
 static DEFINE_RAW_SPINLOCK(trace_printk_lock);
 
-#define BPF_TRACE_PRINTK_SIZE   1024
+#define MAX_TRACE_PRINTK_VARARGS       3
+#define BPF_TRACE_PRINTK_SIZE          1024
 
-static __printf(1, 0) int bpf_do_trace_printk(const char *fmt, ...)
+BPF_CALL_5(bpf_trace_printk, char *, fmt, u32, fmt_size, u64, arg1,
+          u64, arg2, u64, arg3)
 {
+       u64 args[MAX_TRACE_PRINTK_VARARGS] = { arg1, arg2, arg3 };
+       u32 *bin_args;
        static char buf[BPF_TRACE_PRINTK_SIZE];
        unsigned long flags;
-       va_list ap;
        int ret;
 
+       ret = bpf_bprintf_prepare(fmt, fmt_size, args, &bin_args,
+                                 MAX_TRACE_PRINTK_VARARGS);
+       if (ret < 0)
+               return ret;
+
        raw_spin_lock_irqsave(&trace_printk_lock, flags);
-       va_start(ap, fmt);
-       ret = vsnprintf(buf, sizeof(buf), fmt, ap);
-       va_end(ap);
-       /* vsnprintf() will not append null for zero-length strings */
-       if (ret == 0)
-               buf[0] = '\0';
+       ret = bstr_printf(buf, sizeof(buf), fmt, bin_args);
+
        trace_bpf_trace_printk(buf);
        raw_spin_unlock_irqrestore(&trace_printk_lock, flags);
 
-       return ret;
-}
-
-/*
- * Only limited trace_printk() conversion specifiers allowed:
- * %d %i %u %x %ld %li %lu %lx %lld %lli %llu %llx %p %pB %pks %pus %s
- */
-BPF_CALL_5(bpf_trace_printk, char *, fmt, u32, fmt_size, u64, arg1,
-          u64, arg2, u64, arg3)
-{
-       int i, mod[3] = {}, fmt_cnt = 0;
-       char buf[64], fmt_ptype;
-       void *unsafe_ptr = NULL;
-       bool str_seen = false;
-
-       /*
-        * bpf_check()->check_func_arg()->check_stack_boundary()
-        * guarantees that fmt points to bpf program stack,
-        * fmt_size bytes of it were initialized and fmt_size > 0
-        */
-       if (fmt[--fmt_size] != 0)
-               return -EINVAL;
-
-       /* check format string for allowed specifiers */
-       for (i = 0; i < fmt_size; i++) {
-               if ((!isprint(fmt[i]) && !isspace(fmt[i])) || !isascii(fmt[i]))
-                       return -EINVAL;
-
-               if (fmt[i] != '%')
-                       continue;
-
-               if (fmt_cnt >= 3)
-                       return -EINVAL;
-
-               /* fmt[i] != 0 && fmt[last] == 0, so we can access fmt[i + 1] */
-               i++;
-               if (fmt[i] == 'l') {
-                       mod[fmt_cnt]++;
-                       i++;
-               } else if (fmt[i] == 'p') {
-                       mod[fmt_cnt]++;
-                       if ((fmt[i + 1] == 'k' ||
-                            fmt[i + 1] == 'u') &&
-                           fmt[i + 2] == 's') {
-                               fmt_ptype = fmt[i + 1];
-                               i += 2;
-                               goto fmt_str;
-                       }
-
-                       if (fmt[i + 1] == 'B') {
-                               i++;
-                               goto fmt_next;
-                       }
-
-                       /* disallow any further format extensions */
-                       if (fmt[i + 1] != 0 &&
-                           !isspace(fmt[i + 1]) &&
-                           !ispunct(fmt[i + 1]))
-                               return -EINVAL;
-
-                       goto fmt_next;
-               } else if (fmt[i] == 's') {
-                       mod[fmt_cnt]++;
-                       fmt_ptype = fmt[i];
-fmt_str:
-                       if (str_seen)
-                               /* allow only one '%s' per fmt string */
-                               return -EINVAL;
-                       str_seen = true;
-
-                       if (fmt[i + 1] != 0 &&
-                           !isspace(fmt[i + 1]) &&
-                           !ispunct(fmt[i + 1]))
-                               return -EINVAL;
-
-                       switch (fmt_cnt) {
-                       case 0:
-                               unsafe_ptr = (void *)(long)arg1;
-                               arg1 = (long)buf;
-                               break;
-                       case 1:
-                               unsafe_ptr = (void *)(long)arg2;
-                               arg2 = (long)buf;
-                               break;
-                       case 2:
-                               unsafe_ptr = (void *)(long)arg3;
-                               arg3 = (long)buf;
-                               break;
-                       }
-
-                       bpf_trace_copy_string(buf, unsafe_ptr, fmt_ptype,
-                                       sizeof(buf));
-                       goto fmt_next;
-               }
-
-               if (fmt[i] == 'l') {
-                       mod[fmt_cnt]++;
-                       i++;
-               }
-
-               if (fmt[i] != 'i' && fmt[i] != 'd' &&
-                   fmt[i] != 'u' && fmt[i] != 'x')
-                       return -EINVAL;
-fmt_next:
-               fmt_cnt++;
-       }
+       bpf_bprintf_cleanup();
 
-/* Horrid workaround for getting va_list handling working with different
- * argument type combinations generically for 32 and 64 bit archs.
- */
-#define __BPF_TP_EMIT()        __BPF_ARG3_TP()
-#define __BPF_TP(...)                                                  \
-       bpf_do_trace_printk(fmt, ##__VA_ARGS__)
-
-#define __BPF_ARG1_TP(...)                                             \
-       ((mod[0] == 2 || (mod[0] == 1 && __BITS_PER_LONG == 64))        \
-         ? __BPF_TP(arg1, ##__VA_ARGS__)                               \
-         : ((mod[0] == 1 || (mod[0] == 0 && __BITS_PER_LONG == 32))    \
-             ? __BPF_TP((long)arg1, ##__VA_ARGS__)                     \
-             : __BPF_TP((u32)arg1, ##__VA_ARGS__)))
-
-#define __BPF_ARG2_TP(...)                                             \
-       ((mod[1] == 2 || (mod[1] == 1 && __BITS_PER_LONG == 64))        \
-         ? __BPF_ARG1_TP(arg2, ##__VA_ARGS__)                          \
-         : ((mod[1] == 1 || (mod[1] == 0 && __BITS_PER_LONG == 32))    \
-             ? __BPF_ARG1_TP((long)arg2, ##__VA_ARGS__)                \
-             : __BPF_ARG1_TP((u32)arg2, ##__VA_ARGS__)))
-
-#define __BPF_ARG3_TP(...)                                             \
-       ((mod[2] == 2 || (mod[2] == 1 && __BITS_PER_LONG == 64))        \
-         ? __BPF_ARG2_TP(arg3, ##__VA_ARGS__)                          \
-         : ((mod[2] == 1 || (mod[2] == 0 && __BITS_PER_LONG == 32))    \
-             ? __BPF_ARG2_TP((long)arg3, ##__VA_ARGS__)                \
-             : __BPF_ARG2_TP((u32)arg3, ##__VA_ARGS__)))
-
-       return __BPF_TP_EMIT();
+       return ret;
 }
 
 static const struct bpf_func_proto bpf_trace_printk_proto = {
@@ -581,184 +427,27 @@ const struct bpf_func_proto *bpf_get_trace_printk_proto(void)
 }
 
 #define MAX_SEQ_PRINTF_VARARGS         12
-#define MAX_SEQ_PRINTF_MAX_MEMCPY      6
-#define MAX_SEQ_PRINTF_STR_LEN         128
-
-struct bpf_seq_printf_buf {
-       char buf[MAX_SEQ_PRINTF_MAX_MEMCPY][MAX_SEQ_PRINTF_STR_LEN];
-};
-static DEFINE_PER_CPU(struct bpf_seq_printf_buf, bpf_seq_printf_buf);
-static DEFINE_PER_CPU(int, bpf_seq_printf_buf_used);
 
 BPF_CALL_5(bpf_seq_printf, struct seq_file *, m, char *, fmt, u32, fmt_size,
           const void *, data, u32, data_len)
 {
-       int err = -EINVAL, fmt_cnt = 0, memcpy_cnt = 0;
-       int i, buf_used, copy_size, num_args;
-       u64 params[MAX_SEQ_PRINTF_VARARGS];
-       struct bpf_seq_printf_buf *bufs;
-       const u64 *args = data;
-
-       buf_used = this_cpu_inc_return(bpf_seq_printf_buf_used);
-       if (WARN_ON_ONCE(buf_used > 1)) {
-               err = -EBUSY;
-               goto out;
-       }
-
-       bufs = this_cpu_ptr(&bpf_seq_printf_buf);
-
-       /*
-        * bpf_check()->check_func_arg()->check_stack_boundary()
-        * guarantees that fmt points to bpf program stack,
-        * fmt_size bytes of it were initialized and fmt_size > 0
-        */
-       if (fmt[--fmt_size] != 0)
-               goto out;
-
-       if (data_len & 7)
-               goto out;
-
-       for (i = 0; i < fmt_size; i++) {
-               if (fmt[i] == '%') {
-                       if (fmt[i + 1] == '%')
-                               i++;
-                       else if (!data || !data_len)
-                               goto out;
-               }
-       }
+       int err, num_args;
+       u32 *bin_args;
 
+       if (data_len & 7 || data_len > MAX_SEQ_PRINTF_VARARGS * 8 ||
+           (data_len && !data))
+               return -EINVAL;
        num_args = data_len / 8;
 
-       /* check format string for allowed specifiers */
-       for (i = 0; i < fmt_size; i++) {
-               /* only printable ascii for now. */
-               if ((!isprint(fmt[i]) && !isspace(fmt[i])) || !isascii(fmt[i])) {
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               if (fmt[i] != '%')
-                       continue;
-
-               if (fmt[i + 1] == '%') {
-                       i++;
-                       continue;
-               }
-
-               if (fmt_cnt >= MAX_SEQ_PRINTF_VARARGS) {
-                       err = -E2BIG;
-                       goto out;
-               }
-
-               if (fmt_cnt >= num_args) {
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               /* fmt[i] != 0 && fmt[last] == 0, so we can access fmt[i + 1] */
-               i++;
-
-               /* skip optional "[0 +-][num]" width formating field */
-               while (fmt[i] == '0' || fmt[i] == '+'  || fmt[i] == '-' ||
-                      fmt[i] == ' ')
-                       i++;
-               if (fmt[i] >= '1' && fmt[i] <= '9') {
-                       i++;
-                       while (fmt[i] >= '0' && fmt[i] <= '9')
-                               i++;
-               }
-
-               if (fmt[i] == 's') {
-                       void *unsafe_ptr;
-
-                       /* try our best to copy */
-                       if (memcpy_cnt >= MAX_SEQ_PRINTF_MAX_MEMCPY) {
-                               err = -E2BIG;
-                               goto out;
-                       }
-
-                       unsafe_ptr = (void *)(long)args[fmt_cnt];
-                       err = strncpy_from_kernel_nofault(bufs->buf[memcpy_cnt],
-                                       unsafe_ptr, MAX_SEQ_PRINTF_STR_LEN);
-                       if (err < 0)
-                               bufs->buf[memcpy_cnt][0] = '\0';
-                       params[fmt_cnt] = (u64)(long)bufs->buf[memcpy_cnt];
-
-                       fmt_cnt++;
-                       memcpy_cnt++;
-                       continue;
-               }
-
-               if (fmt[i] == 'p') {
-                       if (fmt[i + 1] == 0 ||
-                           fmt[i + 1] == 'K' ||
-                           fmt[i + 1] == 'x' ||
-                           fmt[i + 1] == 'B') {
-                               /* just kernel pointers */
-                               params[fmt_cnt] = args[fmt_cnt];
-                               fmt_cnt++;
-                               continue;
-                       }
-
-                       /* only support "%pI4", "%pi4", "%pI6" and "%pi6". */
-                       if (fmt[i + 1] != 'i' && fmt[i + 1] != 'I') {
-                               err = -EINVAL;
-                               goto out;
-                       }
-                       if (fmt[i + 2] != '4' && fmt[i + 2] != '6') {
-                               err = -EINVAL;
-                               goto out;
-                       }
-
-                       if (memcpy_cnt >= MAX_SEQ_PRINTF_MAX_MEMCPY) {
-                               err = -E2BIG;
-                               goto out;
-                       }
-
-
-                       copy_size = (fmt[i + 2] == '4') ? 4 : 16;
-
-                       err = copy_from_kernel_nofault(bufs->buf[memcpy_cnt],
-                                               (void *) (long) args[fmt_cnt],
-                                               copy_size);
-                       if (err < 0)
-                               memset(bufs->buf[memcpy_cnt], 0, copy_size);
-                       params[fmt_cnt] = (u64)(long)bufs->buf[memcpy_cnt];
-
-                       i += 2;
-                       fmt_cnt++;
-                       memcpy_cnt++;
-                       continue;
-               }
-
-               if (fmt[i] == 'l') {
-                       i++;
-                       if (fmt[i] == 'l')
-                               i++;
-               }
-
-               if (fmt[i] != 'i' && fmt[i] != 'd' &&
-                   fmt[i] != 'u' && fmt[i] != 'x' &&
-                   fmt[i] != 'X') {
-                       err = -EINVAL;
-                       goto out;
-               }
+       err = bpf_bprintf_prepare(fmt, fmt_size, data, &bin_args, num_args);
+       if (err < 0)
+               return err;
 
-               params[fmt_cnt] = args[fmt_cnt];
-               fmt_cnt++;
-       }
+       seq_bprintf(m, fmt, bin_args);
 
-       /* Maximumly we can have MAX_SEQ_PRINTF_VARARGS parameter, just give
-        * all of them to seq_printf().
-        */
-       seq_printf(m, fmt, params[0], params[1], params[2], params[3],
-                  params[4], params[5], params[6], params[7], params[8],
-                  params[9], params[10], params[11]);
+       bpf_bprintf_cleanup();
 
-       err = seq_has_overflowed(m) ? -EOVERFLOW : 0;
-out:
-       this_cpu_dec(bpf_seq_printf_buf_used);
-       return err;
+       return seq_has_overflowed(m) ? -EOVERFLOW : 0;
 }
 
 BTF_ID_LIST_SINGLE(btf_seq_file_ids, struct, seq_file)
@@ -1373,6 +1062,8 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_task_storage_delete_proto;
        case BPF_FUNC_for_each_map_elem:
                return &bpf_for_each_map_elem_proto;
+       case BPF_FUNC_snprintf:
+               return &bpf_snprintf_proto;
        default:
                return NULL;
        }