bpf: Add bpf_snprintf_btf helper
authorAlan Maguire <alan.maguire@oracle.com>
Mon, 28 Sep 2020 11:31:05 +0000 (12:31 +0100)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 29 Sep 2020 01:26:58 +0000 (18:26 -0700)
A helper is added to support tracing kernel type information in BPF
using the BPF Type Format (BTF).  Its signature is

long bpf_snprintf_btf(char *str, u32 str_size, struct btf_ptr *ptr,
      u32 btf_ptr_size, u64 flags);

struct btf_ptr * specifies

- a pointer to the data to be traced
- the BTF id of the type of data pointed to
- a flags field is provided for future use; these flags
  are not to be confused with the BTF_F_* flags
  below that control how the btf_ptr is displayed; the
  flags member of the struct btf_ptr may be used to
  disambiguate types in kernel versus module BTF, etc;
  the main distinction is the flags relate to the type
  and information needed in identifying it; not how it
  is displayed.

For example a BPF program with a struct sk_buff *skb
could do the following:

static struct btf_ptr b = { };

b.ptr = skb;
b.type_id = __builtin_btf_type_id(struct sk_buff, 1);
bpf_snprintf_btf(str, sizeof(str), &b, sizeof(b), 0, 0);

Default output looks like this:

(struct sk_buff){
 .transport_header = (__u16)65535,
 .mac_header = (__u16)65535,
 .end = (sk_buff_data_t)192,
 .head = (unsigned char *)0x000000007524fd8b,
 .data = (unsigned char *)0x000000007524fd8b,
 .truesize = (unsigned int)768,
 .users = (refcount_t){
  .refs = (atomic_t){
   .counter = (int)1,
  },
 },
}

Flags modifying display are as follows:

- BTF_F_COMPACT: no formatting around type information
- BTF_F_NONAME: no struct/union member names/types
- BTF_F_PTR_RAW: show raw (unobfuscated) pointer values;
equivalent to %px.
- BTF_F_ZERO: show zero-valued struct/union members;
they are not displayed by default

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/1601292670-1616-4-git-send-email-alan.maguire@oracle.com
include/linux/bpf.h
include/linux/btf.h
include/uapi/linux/bpf.h
kernel/bpf/core.c
kernel/bpf/helpers.c
kernel/trace/bpf_trace.c
scripts/bpf_helpers_doc.py
tools/include/uapi/linux/bpf.h

index e620a4b..768b533 100644 (file)
@@ -1822,6 +1822,7 @@ extern const struct bpf_func_proto bpf_skc_to_tcp_timewait_sock_proto;
 extern const struct bpf_func_proto bpf_skc_to_tcp_request_sock_proto;
 extern const struct bpf_func_proto bpf_skc_to_udp6_sock_proto;
 extern const struct bpf_func_proto bpf_copy_from_user_proto;
+extern const struct bpf_func_proto bpf_snprintf_btf_proto;
 
 const struct bpf_func_proto *bpf_tracing_func_proto(
        enum bpf_func_id func_id, const struct bpf_prog *prog);
index d0f5d3c..3e5cdc2 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/types.h>
 #include <uapi/linux/btf.h>
+#include <uapi/linux/bpf.h>
 
 #define BTF_TYPE_EMIT(type) ((void)(type *)0)
 
@@ -59,10 +60,10 @@ const struct btf_type *btf_type_id_size(const struct btf *btf,
  *     - BTF_SHOW_UNSAFE: skip use of bpf_probe_read() to safely read
  *       data before displaying it.
  */
-#define BTF_SHOW_COMPACT       (1ULL << 0)
-#define BTF_SHOW_NONAME                (1ULL << 1)
-#define BTF_SHOW_PTR_RAW       (1ULL << 2)
-#define BTF_SHOW_ZERO          (1ULL << 3)
+#define BTF_SHOW_COMPACT       BTF_F_COMPACT
+#define BTF_SHOW_NONAME                BTF_F_NONAME
+#define BTF_SHOW_PTR_RAW       BTF_F_PTR_RAW
+#define BTF_SHOW_ZERO          BTF_F_ZERO
 #define BTF_SHOW_UNSAFE                (1ULL << 4)
 
 void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj,
index 82522f0..cca9eb1 100644 (file)
@@ -3594,6 +3594,42 @@ union bpf_attr {
  *             the data in *dst*. This is a wrapper of **copy_from_user**\ ().
  *     Return
  *             0 on success, or a negative error in case of failure.
+ *
+ * long bpf_snprintf_btf(char *str, u32 str_size, struct btf_ptr *ptr, u32 btf_ptr_size, u64 flags)
+ *     Description
+ *             Use BTF to store a string representation of *ptr*->ptr in *str*,
+ *             using *ptr*->type_id.  This value should specify the type
+ *             that *ptr*->ptr points to. LLVM __builtin_btf_type_id(type, 1)
+ *             can be used to look up vmlinux BTF type ids. Traversing the
+ *             data structure using BTF, the type information and values are
+ *             stored in the first *str_size* - 1 bytes of *str*.  Safe copy of
+ *             the pointer data is carried out to avoid kernel crashes during
+ *             operation.  Smaller types can use string space on the stack;
+ *             larger programs can use map data to store the string
+ *             representation.
+ *
+ *             The string can be subsequently shared with userspace via
+ *             bpf_perf_event_output() or ring buffer interfaces.
+ *             bpf_trace_printk() is to be avoided as it places too small
+ *             a limit on string size to be useful.
+ *
+ *             *flags* is a combination of
+ *
+ *             **BTF_F_COMPACT**
+ *                     no formatting around type information
+ *             **BTF_F_NONAME**
+ *                     no struct/union member names/types
+ *             **BTF_F_PTR_RAW**
+ *                     show raw (unobfuscated) pointer values;
+ *                     equivalent to printk specifier %px.
+ *             **BTF_F_ZERO**
+ *                     show zero-valued struct/union members; they
+ *                     are not displayed by default
+ *
+ *     Return
+ *             The number of bytes that were written (or would have been
+ *             written if output had to be truncated due to string size),
+ *             or a negative error in cases of failure.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -3745,6 +3781,7 @@ union bpf_attr {
        FN(inode_storage_delete),       \
        FN(d_path),                     \
        FN(copy_from_user),             \
+       FN(snprintf_btf),               \
        /* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
@@ -4853,4 +4890,34 @@ struct bpf_sk_lookup {
        __u32 local_port;       /* Host byte order */
 };
 
+/*
+ * struct btf_ptr is used for typed pointer representation; the
+ * type id is used to render the pointer data as the appropriate type
+ * via the bpf_snprintf_btf() helper described above.  A flags field -
+ * potentially to specify additional details about the BTF pointer
+ * (rather than its mode of display) - is included for future use.
+ * Display flags - BTF_F_* - are passed to bpf_snprintf_btf separately.
+ */
+struct btf_ptr {
+       void *ptr;
+       __u32 type_id;
+       __u32 flags;            /* BTF ptr flags; unused at present. */
+};
+
+/*
+ * Flags to control bpf_snprintf_btf() behaviour.
+ *     - BTF_F_COMPACT: no formatting around type information
+ *     - BTF_F_NONAME: no struct/union member names/types
+ *     - BTF_F_PTR_RAW: show raw (unobfuscated) pointer values;
+ *       equivalent to %px.
+ *     - BTF_F_ZERO: show zero-valued struct/union members; they
+ *       are not displayed by default
+ */
+enum {
+       BTF_F_COMPACT   =       (1ULL << 0),
+       BTF_F_NONAME    =       (1ULL << 1),
+       BTF_F_PTR_RAW   =       (1ULL << 2),
+       BTF_F_ZERO      =       (1ULL << 3),
+};
+
 #endif /* _UAPI__LINUX_BPF_H__ */
index c4811b1..403fb23 100644 (file)
@@ -2216,6 +2216,7 @@ const struct bpf_func_proto bpf_get_current_cgroup_id_proto __weak;
 const struct bpf_func_proto bpf_get_current_ancestor_cgroup_id_proto __weak;
 const struct bpf_func_proto bpf_get_local_storage_proto __weak;
 const struct bpf_func_proto bpf_get_ns_current_pid_tgid_proto __weak;
+const struct bpf_func_proto bpf_snprintf_btf_proto __weak;
 
 const struct bpf_func_proto * __weak bpf_get_trace_printk_proto(void)
 {
index 5cc7425..e825441 100644 (file)
@@ -683,6 +683,10 @@ bpf_base_func_proto(enum bpf_func_id func_id)
                if (!perfmon_capable())
                        return NULL;
                return bpf_get_trace_printk_proto();
+       case BPF_FUNC_snprintf_btf:
+               if (!perfmon_capable())
+                       return NULL;
+               return &bpf_snprintf_btf_proto;
        case BPF_FUNC_jiffies64:
                return &bpf_jiffies64_proto;
        default:
index 2834866..140e1be 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/slab.h>
 #include <linux/bpf.h>
 #include <linux/bpf_perf_event.h>
+#include <linux/btf.h>
 #include <linux/filter.h>
 #include <linux/uaccess.h>
 #include <linux/ctype.h>
@@ -16,6 +17,9 @@
 #include <linux/error-injection.h>
 #include <linux/btf_ids.h>
 
+#include <uapi/linux/bpf.h>
+#include <uapi/linux/btf.h>
+
 #include <asm/tlb.h>
 
 #include "trace_probe.h"
@@ -1147,6 +1151,65 @@ static const struct bpf_func_proto bpf_d_path_proto = {
        .allowed        = bpf_d_path_allowed,
 };
 
+#define BTF_F_ALL      (BTF_F_COMPACT  | BTF_F_NONAME | \
+                        BTF_F_PTR_RAW | BTF_F_ZERO)
+
+static int bpf_btf_printf_prepare(struct btf_ptr *ptr, u32 btf_ptr_size,
+                                 u64 flags, const struct btf **btf,
+                                 s32 *btf_id)
+{
+       const struct btf_type *t;
+
+       if (unlikely(flags & ~(BTF_F_ALL)))
+               return -EINVAL;
+
+       if (btf_ptr_size != sizeof(struct btf_ptr))
+               return -EINVAL;
+
+       *btf = bpf_get_btf_vmlinux();
+
+       if (IS_ERR_OR_NULL(*btf))
+               return PTR_ERR(*btf);
+
+       if (ptr->type_id > 0)
+               *btf_id = ptr->type_id;
+       else
+               return -EINVAL;
+
+       if (*btf_id > 0)
+               t = btf_type_by_id(*btf, *btf_id);
+       if (*btf_id <= 0 || !t)
+               return -ENOENT;
+
+       return 0;
+}
+
+BPF_CALL_5(bpf_snprintf_btf, char *, str, u32, str_size, struct btf_ptr *, ptr,
+          u32, btf_ptr_size, u64, flags)
+{
+       const struct btf *btf;
+       s32 btf_id;
+       int ret;
+
+       ret = bpf_btf_printf_prepare(ptr, btf_ptr_size, flags, &btf, &btf_id);
+       if (ret)
+               return ret;
+
+       return btf_type_snprintf_show(btf, btf_id, ptr->ptr, str, str_size,
+                                     flags);
+}
+
+const struct bpf_func_proto bpf_snprintf_btf_proto = {
+       .func           = bpf_snprintf_btf,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_MEM,
+       .arg2_type      = ARG_CONST_SIZE,
+       .arg3_type      = ARG_PTR_TO_MEM,
+       .arg4_type      = ARG_CONST_SIZE,
+       .arg5_type      = ARG_ANYTHING,
+};
+
 const struct bpf_func_proto *
 bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 {
@@ -1233,6 +1296,8 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_get_task_stack_proto;
        case BPF_FUNC_copy_from_user:
                return prog->aux->sleepable ? &bpf_copy_from_user_proto : NULL;
+       case BPF_FUNC_snprintf_btf:
+               return &bpf_snprintf_btf_proto;
        default:
                return NULL;
        }
index 0838817..7d86fdd 100755 (executable)
@@ -433,6 +433,7 @@ class PrinterHelpers(Printer):
             'struct sk_msg_md',
             'struct xdp_md',
             'struct path',
+            'struct btf_ptr',
     ]
     known_types = {
             '...',
@@ -474,6 +475,7 @@ class PrinterHelpers(Printer):
             'struct udp6_sock',
             'struct task_struct',
             'struct path',
+            'struct btf_ptr',
     }
     mapped_types = {
             'u8': '__u8',
index 82522f0..cca9eb1 100644 (file)
@@ -3594,6 +3594,42 @@ union bpf_attr {
  *             the data in *dst*. This is a wrapper of **copy_from_user**\ ().
  *     Return
  *             0 on success, or a negative error in case of failure.
+ *
+ * long bpf_snprintf_btf(char *str, u32 str_size, struct btf_ptr *ptr, u32 btf_ptr_size, u64 flags)
+ *     Description
+ *             Use BTF to store a string representation of *ptr*->ptr in *str*,
+ *             using *ptr*->type_id.  This value should specify the type
+ *             that *ptr*->ptr points to. LLVM __builtin_btf_type_id(type, 1)
+ *             can be used to look up vmlinux BTF type ids. Traversing the
+ *             data structure using BTF, the type information and values are
+ *             stored in the first *str_size* - 1 bytes of *str*.  Safe copy of
+ *             the pointer data is carried out to avoid kernel crashes during
+ *             operation.  Smaller types can use string space on the stack;
+ *             larger programs can use map data to store the string
+ *             representation.
+ *
+ *             The string can be subsequently shared with userspace via
+ *             bpf_perf_event_output() or ring buffer interfaces.
+ *             bpf_trace_printk() is to be avoided as it places too small
+ *             a limit on string size to be useful.
+ *
+ *             *flags* is a combination of
+ *
+ *             **BTF_F_COMPACT**
+ *                     no formatting around type information
+ *             **BTF_F_NONAME**
+ *                     no struct/union member names/types
+ *             **BTF_F_PTR_RAW**
+ *                     show raw (unobfuscated) pointer values;
+ *                     equivalent to printk specifier %px.
+ *             **BTF_F_ZERO**
+ *                     show zero-valued struct/union members; they
+ *                     are not displayed by default
+ *
+ *     Return
+ *             The number of bytes that were written (or would have been
+ *             written if output had to be truncated due to string size),
+ *             or a negative error in cases of failure.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -3745,6 +3781,7 @@ union bpf_attr {
        FN(inode_storage_delete),       \
        FN(d_path),                     \
        FN(copy_from_user),             \
+       FN(snprintf_btf),               \
        /* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
@@ -4853,4 +4890,34 @@ struct bpf_sk_lookup {
        __u32 local_port;       /* Host byte order */
 };
 
+/*
+ * struct btf_ptr is used for typed pointer representation; the
+ * type id is used to render the pointer data as the appropriate type
+ * via the bpf_snprintf_btf() helper described above.  A flags field -
+ * potentially to specify additional details about the BTF pointer
+ * (rather than its mode of display) - is included for future use.
+ * Display flags - BTF_F_* - are passed to bpf_snprintf_btf separately.
+ */
+struct btf_ptr {
+       void *ptr;
+       __u32 type_id;
+       __u32 flags;            /* BTF ptr flags; unused at present. */
+};
+
+/*
+ * Flags to control bpf_snprintf_btf() behaviour.
+ *     - BTF_F_COMPACT: no formatting around type information
+ *     - BTF_F_NONAME: no struct/union member names/types
+ *     - BTF_F_PTR_RAW: show raw (unobfuscated) pointer values;
+ *       equivalent to %px.
+ *     - BTF_F_ZERO: show zero-valued struct/union members; they
+ *       are not displayed by default
+ */
+enum {
+       BTF_F_COMPACT   =       (1ULL << 0),
+       BTF_F_NONAME    =       (1ULL << 1),
+       BTF_F_PTR_RAW   =       (1ULL << 2),
+       BTF_F_ZERO      =       (1ULL << 3),
+};
+
 #endif /* _UAPI__LINUX_BPF_H__ */