]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - kernel/trace/bpf_trace.c
bpf: fix bpf_trace_printk on 32 bit archs
[karo-tx-linux.git] / kernel / trace / bpf_trace.c
index 460a031c77e592411b83a4071d976706db975687..dc498b605d5dd36137eaba7bd0ee93da72a36c33 100644 (file)
@@ -122,8 +122,8 @@ static const struct bpf_func_proto *bpf_get_probe_write_proto(void)
 }
 
 /*
- * limited trace_printk()
- * only %d %u %x %ld %lu %lx %lld %llu %llx %p %s conversion specifiers allowed
+ * Only limited trace_printk() conversion specifiers allowed:
+ * %d %i %u %x %ld %li %lu %lx %lld %lli %llu %llx %p %s
  */
 BPF_CALL_5(bpf_trace_printk, char *, fmt, u32, fmt_size, u64, arg1,
           u64, arg2, u64, arg3)
@@ -198,15 +198,42 @@ BPF_CALL_5(bpf_trace_printk, char *, fmt, u32, fmt_size, u64, arg1,
                        i++;
                }
 
-               if (fmt[i] != 'd' && fmt[i] != 'u' && fmt[i] != 'x')
+               if (fmt[i] != 'i' && fmt[i] != 'd' &&
+                   fmt[i] != 'u' && fmt[i] != 'x')
                        return -EINVAL;
                fmt_cnt++;
        }
 
-       return __trace_printk(1/* fake ip will not be printed */, fmt,
-                             mod[0] == 2 ? arg1 : mod[0] == 1 ? (long) arg1 : (u32) arg1,
-                             mod[1] == 2 ? arg2 : mod[1] == 1 ? (long) arg2 : (u32) arg2,
-                             mod[2] == 2 ? arg3 : mod[2] == 1 ? (long) arg3 : (u32) arg3);
+/* 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(...)                                                  \
+       __trace_printk(1 /* Fake ip will not be printed. */,            \
+                      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();
 }
 
 static const struct bpf_func_proto bpf_trace_printk_proto = {
@@ -234,7 +261,8 @@ BPF_CALL_2(bpf_perf_event_read, struct bpf_map *, map, u64, flags)
        unsigned int cpu = smp_processor_id();
        u64 index = flags & BPF_F_INDEX_MASK;
        struct bpf_event_entry *ee;
-       struct perf_event *event;
+       u64 value = 0;
+       int err;
 
        if (unlikely(flags & ~(BPF_F_INDEX_MASK)))
                return -EINVAL;
@@ -247,21 +275,14 @@ BPF_CALL_2(bpf_perf_event_read, struct bpf_map *, map, u64, flags)
        if (!ee)
                return -ENOENT;
 
-       event = ee->event;
-       if (unlikely(event->attr.type != PERF_TYPE_HARDWARE &&
-                    event->attr.type != PERF_TYPE_RAW))
-               return -EINVAL;
-
-       /* make sure event is local and doesn't have pmu::count */
-       if (unlikely(event->oncpu != cpu || event->pmu->count))
-               return -EINVAL;
-
+       err = perf_event_read_local(ee->event, &value);
        /*
-        * we don't know if the function is run successfully by the
-        * return value. It can be judged in other places, such as
-        * eBPF programs.
+        * this api is ugly since we miss [-22..-2] range of valid
+        * counter values, but that's uapi
         */
-       return perf_event_read_local(event);
+       if (err)
+               return err;
+       return value;
 }
 
 static const struct bpf_func_proto bpf_perf_event_read_proto = {
@@ -272,14 +293,16 @@ static const struct bpf_func_proto bpf_perf_event_read_proto = {
        .arg2_type      = ARG_ANYTHING,
 };
 
+static DEFINE_PER_CPU(struct perf_sample_data, bpf_sd);
+
 static __always_inline u64
 __bpf_perf_event_output(struct pt_regs *regs, struct bpf_map *map,
                        u64 flags, struct perf_raw_record *raw)
 {
        struct bpf_array *array = container_of(map, struct bpf_array, map);
+       struct perf_sample_data *sd = this_cpu_ptr(&bpf_sd);
        unsigned int cpu = smp_processor_id();
        u64 index = flags & BPF_F_INDEX_MASK;
-       struct perf_sample_data sample_data;
        struct bpf_event_entry *ee;
        struct perf_event *event;
 
@@ -300,9 +323,9 @@ __bpf_perf_event_output(struct pt_regs *regs, struct bpf_map *map,
        if (unlikely(event->oncpu != cpu))
                return -EOPNOTSUPP;
 
-       perf_sample_data_init(&sample_data, 0, 0);
-       sample_data.raw = raw;
-       perf_event_output(event, &sample_data, regs);
+       perf_sample_data_init(sd, 0, 0);
+       sd->raw = raw;
+       perf_event_output(event, sd, regs);
        return 0;
 }
 
@@ -483,7 +506,7 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func
 
 /* bpf+kprobe programs can access fields of 'struct pt_regs' */
 static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type type,
-                                       enum bpf_reg_type *reg_type)
+                                       struct bpf_insn_access_aux *info)
 {
        if (off < 0 || off >= sizeof(struct pt_regs))
                return false;
@@ -566,7 +589,7 @@ static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id)
 }
 
 static bool tp_prog_is_valid_access(int off, int size, enum bpf_access_type type,
-                                   enum bpf_reg_type *reg_type)
+                                   struct bpf_insn_access_aux *info)
 {
        if (off < sizeof(void *) || off >= PERF_MAX_TRACE_SIZE)
                return false;
@@ -585,40 +608,47 @@ const struct bpf_verifier_ops tracepoint_prog_ops = {
 };
 
 static bool pe_prog_is_valid_access(int off, int size, enum bpf_access_type type,
-                                   enum bpf_reg_type *reg_type)
+                                   struct bpf_insn_access_aux *info)
 {
+       const int size_sp = FIELD_SIZEOF(struct bpf_perf_event_data,
+                                        sample_period);
+
        if (off < 0 || off >= sizeof(struct bpf_perf_event_data))
                return false;
        if (type != BPF_READ)
                return false;
        if (off % size != 0)
                return false;
-       if (off == offsetof(struct bpf_perf_event_data, sample_period)) {
-               if (size != sizeof(u64))
+
+       switch (off) {
+       case bpf_ctx_range(struct bpf_perf_event_data, sample_period):
+               bpf_ctx_record_field_size(info, size_sp);
+               if (!bpf_ctx_narrow_access_ok(off, size, size_sp))
                        return false;
-       } else {
+               break;
+       default:
                if (size != sizeof(long))
                        return false;
        }
+
        return true;
 }
 
 static u32 pe_prog_convert_ctx_access(enum bpf_access_type type,
                                      const struct bpf_insn *si,
                                      struct bpf_insn *insn_buf,
-                                     struct bpf_prog *prog)
+                                     struct bpf_prog *prog, u32 *target_size)
 {
        struct bpf_insn *insn = insn_buf;
 
        switch (si->off) {
        case offsetof(struct bpf_perf_event_data, sample_period):
-               BUILD_BUG_ON(FIELD_SIZEOF(struct perf_sample_data, period) != sizeof(u64));
-
                *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_perf_event_data_kern,
                                                       data), si->dst_reg, si->src_reg,
                                      offsetof(struct bpf_perf_event_data_kern, data));
                *insn++ = BPF_LDX_MEM(BPF_DW, si->dst_reg, si->dst_reg,
-                                     offsetof(struct perf_sample_data, period));
+                                     bpf_target_off(struct perf_sample_data, period, 8,
+                                                    target_size));
                break;
        default:
                *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_perf_event_data_kern,