mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-04 04:06:26 +00:00
bpf: Add bpf_snprintf_btf helper
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
This commit is contained in:
parent
31d0bc8163
commit
c4d0bfb450
@ -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_tcp_request_sock_proto;
|
||||||
extern const struct bpf_func_proto bpf_skc_to_udp6_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_copy_from_user_proto;
|
||||||
|
extern const struct bpf_func_proto bpf_snprintf_btf_proto;
|
||||||
|
|
||||||
const struct bpf_func_proto *bpf_tracing_func_proto(
|
const struct bpf_func_proto *bpf_tracing_func_proto(
|
||||||
enum bpf_func_id func_id, const struct bpf_prog *prog);
|
enum bpf_func_id func_id, const struct bpf_prog *prog);
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <uapi/linux/btf.h>
|
#include <uapi/linux/btf.h>
|
||||||
|
#include <uapi/linux/bpf.h>
|
||||||
|
|
||||||
#define BTF_TYPE_EMIT(type) ((void)(type *)0)
|
#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
|
* - BTF_SHOW_UNSAFE: skip use of bpf_probe_read() to safely read
|
||||||
* data before displaying it.
|
* data before displaying it.
|
||||||
*/
|
*/
|
||||||
#define BTF_SHOW_COMPACT (1ULL << 0)
|
#define BTF_SHOW_COMPACT BTF_F_COMPACT
|
||||||
#define BTF_SHOW_NONAME (1ULL << 1)
|
#define BTF_SHOW_NONAME BTF_F_NONAME
|
||||||
#define BTF_SHOW_PTR_RAW (1ULL << 2)
|
#define BTF_SHOW_PTR_RAW BTF_F_PTR_RAW
|
||||||
#define BTF_SHOW_ZERO (1ULL << 3)
|
#define BTF_SHOW_ZERO BTF_F_ZERO
|
||||||
#define BTF_SHOW_UNSAFE (1ULL << 4)
|
#define BTF_SHOW_UNSAFE (1ULL << 4)
|
||||||
|
|
||||||
void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj,
|
void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj,
|
||||||
|
@ -3594,6 +3594,42 @@ union bpf_attr {
|
|||||||
* the data in *dst*. This is a wrapper of **copy_from_user**\ ().
|
* the data in *dst*. This is a wrapper of **copy_from_user**\ ().
|
||||||
* Return
|
* Return
|
||||||
* 0 on success, or a negative error in case of failure.
|
* 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) \
|
#define __BPF_FUNC_MAPPER(FN) \
|
||||||
FN(unspec), \
|
FN(unspec), \
|
||||||
@ -3745,6 +3781,7 @@ union bpf_attr {
|
|||||||
FN(inode_storage_delete), \
|
FN(inode_storage_delete), \
|
||||||
FN(d_path), \
|
FN(d_path), \
|
||||||
FN(copy_from_user), \
|
FN(copy_from_user), \
|
||||||
|
FN(snprintf_btf), \
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
/* 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 */
|
__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__ */
|
#endif /* _UAPI__LINUX_BPF_H__ */
|
||||||
|
@ -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_current_ancestor_cgroup_id_proto __weak;
|
||||||
const struct bpf_func_proto bpf_get_local_storage_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_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)
|
const struct bpf_func_proto * __weak bpf_get_trace_printk_proto(void)
|
||||||
{
|
{
|
||||||
|
@ -683,6 +683,10 @@ bpf_base_func_proto(enum bpf_func_id func_id)
|
|||||||
if (!perfmon_capable())
|
if (!perfmon_capable())
|
||||||
return NULL;
|
return NULL;
|
||||||
return bpf_get_trace_printk_proto();
|
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:
|
case BPF_FUNC_jiffies64:
|
||||||
return &bpf_jiffies64_proto;
|
return &bpf_jiffies64_proto;
|
||||||
default:
|
default:
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/bpf.h>
|
#include <linux/bpf.h>
|
||||||
#include <linux/bpf_perf_event.h>
|
#include <linux/bpf_perf_event.h>
|
||||||
|
#include <linux/btf.h>
|
||||||
#include <linux/filter.h>
|
#include <linux/filter.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/ctype.h>
|
#include <linux/ctype.h>
|
||||||
@ -16,6 +17,9 @@
|
|||||||
#include <linux/error-injection.h>
|
#include <linux/error-injection.h>
|
||||||
#include <linux/btf_ids.h>
|
#include <linux/btf_ids.h>
|
||||||
|
|
||||||
|
#include <uapi/linux/bpf.h>
|
||||||
|
#include <uapi/linux/btf.h>
|
||||||
|
|
||||||
#include <asm/tlb.h>
|
#include <asm/tlb.h>
|
||||||
|
|
||||||
#include "trace_probe.h"
|
#include "trace_probe.h"
|
||||||
@ -1147,6 +1151,65 @@ static const struct bpf_func_proto bpf_d_path_proto = {
|
|||||||
.allowed = bpf_d_path_allowed,
|
.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 *
|
const struct bpf_func_proto *
|
||||||
bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
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;
|
return &bpf_get_task_stack_proto;
|
||||||
case BPF_FUNC_copy_from_user:
|
case BPF_FUNC_copy_from_user:
|
||||||
return prog->aux->sleepable ? &bpf_copy_from_user_proto : NULL;
|
return prog->aux->sleepable ? &bpf_copy_from_user_proto : NULL;
|
||||||
|
case BPF_FUNC_snprintf_btf:
|
||||||
|
return &bpf_snprintf_btf_proto;
|
||||||
default:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -433,6 +433,7 @@ class PrinterHelpers(Printer):
|
|||||||
'struct sk_msg_md',
|
'struct sk_msg_md',
|
||||||
'struct xdp_md',
|
'struct xdp_md',
|
||||||
'struct path',
|
'struct path',
|
||||||
|
'struct btf_ptr',
|
||||||
]
|
]
|
||||||
known_types = {
|
known_types = {
|
||||||
'...',
|
'...',
|
||||||
@ -474,6 +475,7 @@ class PrinterHelpers(Printer):
|
|||||||
'struct udp6_sock',
|
'struct udp6_sock',
|
||||||
'struct task_struct',
|
'struct task_struct',
|
||||||
'struct path',
|
'struct path',
|
||||||
|
'struct btf_ptr',
|
||||||
}
|
}
|
||||||
mapped_types = {
|
mapped_types = {
|
||||||
'u8': '__u8',
|
'u8': '__u8',
|
||||||
|
@ -3594,6 +3594,42 @@ union bpf_attr {
|
|||||||
* the data in *dst*. This is a wrapper of **copy_from_user**\ ().
|
* the data in *dst*. This is a wrapper of **copy_from_user**\ ().
|
||||||
* Return
|
* Return
|
||||||
* 0 on success, or a negative error in case of failure.
|
* 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) \
|
#define __BPF_FUNC_MAPPER(FN) \
|
||||||
FN(unspec), \
|
FN(unspec), \
|
||||||
@ -3745,6 +3781,7 @@ union bpf_attr {
|
|||||||
FN(inode_storage_delete), \
|
FN(inode_storage_delete), \
|
||||||
FN(d_path), \
|
FN(d_path), \
|
||||||
FN(copy_from_user), \
|
FN(copy_from_user), \
|
||||||
|
FN(snprintf_btf), \
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
/* 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 */
|
__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__ */
|
#endif /* _UAPI__LINUX_BPF_H__ */
|
||||||
|
Loading…
Reference in New Issue
Block a user