perf trace: WIP: sockaddr syscall arg beautifier
We need to use the following BPF program together with the following
(long) 'perf trace' command line, more work is needed to do this setup
completely transparently and also to cache the resulting .o -target bpf
clang generated file so that we don't require that clang is present on
all machines where this will run.
Also needed is a way to do the copy in move_addr_to_kernel() _after_ the
size validation is done against sizeof(struct sockaddr_storage) (the
regs for the parms seems busted for that kprobes case, say,
move_addr_to_kernel:8) and convince the kernel bpf validator that this
will not copy more than whats gets validated by the kernel and no info
leak will take place (the 'record' variable is all zeroed by the
initialization of some of its fields (.ulen, for instance).
# perf trace --ev bpf-output/no-inherit=1,name=__move_addr_to_kernel/ \
--ev syscall_copy_pointer_args.c/map:__move_addr_to_kernel.event=__move_addr_to_kernel/ \
ssh ipv6.google.com
# cat syscall_copy_pointer_args.c
#include <uapi/linux/socket.h>
#include <uapi/linux/ptrace.h>
#include <uapi/linux/bpf.h>
#define SEC(NAME) __attribute__((section(NAME), used))
/* x86_64 */
#define PT_REGS_PARM1(x) ((x)->di)
#define PT_REGS_PARM2(x) ((x)->si)
#define PT_REGS_PARM3(x) ((x)->dx)
#define AF_UNSPEC 0
#define AF_UNIX 1 /* Unix domain sockets */
#define AF_LOCAL 1 /* POSIX name for AF_UNIX */
#define AF_INET 2 /* Internet IP Protocol */
#define AF_INET6 10 /* IP version 6 */
/* Internet address. */
struct in_addr {
u32 s_addr;
};
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
struct sockaddr_in {
__kernel_sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
/* Pad to size of `struct sockaddr'. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
struct in6_addr {
union {
__u8 u6_addr8[16];
__be16 u6_addr16[8];
__be32 u6_addr32[4];
} in6_u;
#define s6_addr in6_u.u6_addr8
#define s6_addr16 in6_u.u6_addr16
#define s6_addr32 in6_u.u6_addr32
};
struct sockaddr_in6 {
unsigned short int sin6_family; /* AF_INET6 */
__be16 sin6_port; /* Transport layer port # */
__be32 sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
__u32 sin6_scope_id; /* scope id (new in RFC2553) */
};
#define UNIX_PATH_MAX 108
struct sockaddr_un {
__kernel_sa_family_t sun_family; /* AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* pathname */
};
struct bpf_map_def {
unsigned int type, key_size, value_size, max_entries;
};
/*
* Do not make this static, it has to be seen by the perf/bpf glue
* code to create the bpf-output event automagically.
*/
struct bpf_map_def SEC("maps") __move_addr_to_kernel = {
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(u32),
.max_entries = __NR_CPUS__,
};
static int (*get_smp_processor_id)(void) = (void *)BPF_FUNC_get_smp_processor_id;
static int (*trace_printk)(const char *fmt, int fmt_size, ...) = (void *)BPF_FUNC_trace_printk;
static int (*perf_event_output)(struct pt_regs *, struct bpf_map_def *, int, void *, unsigned long) = (void *)BPF_FUNC_perf_event_output;
static int (*probe_read)(void *dst, int size, void *unsafe_ptr) = (void *)BPF_FUNC_probe_read;
SEC("move_addr_to_kernel=move_addr_to_kernel")
int move_addr_to_kernel(struct pt_regs *ctx, int err)
{
char err_str[] = "move_addr_to_kernel %d\n";
int ulen = PT_REGS_PARM2(ctx), rc;
struct __kernel_sockaddr_storage *uaddr = (void *)PT_REGS_PARM1(ctx);
struct {
void *uaddr;
int ulen;
struct __kernel_sockaddr_storage ss;
} record = {
.uaddr = (void *)PT_REGS_PARM1(ctx),
.ulen = ulen,
};
int ss_len = offsetof(typeof(record), ss);
probe_read(&record.ss.ss_family, sizeof(record.ss.ss_family), uaddr);
switch (record.ss.ss_family) {
case AF_UNSPEC: return 0;
case AF_INET: ss_len += sizeof(struct sockaddr_in); break;
case AF_INET6: ss_len += sizeof(struct sockaddr_in6); break;
case AF_LOCAL: ss_len += sizeof(struct sockaddr_un); break;
default: ss_len = sizeof(record.ss); break;
}
probe_read(&record.ss, ss_len, uaddr);
perf_event_output(ctx, &__move_addr_to_kernel, get_smp_processor_id(), &record, ss_len);
return 1;
out_err:
trace_printk(err_str, sizeof(err_str), rc);
return 0;
}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
1 file changed