blob: b8261348884a6edfb71ec6da54e83004b5052f30 [file] [edit]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Include vmlinux.h first to declare all kernel types.
#include "include/mons/vmlinux/vmlinux.h"
// Do not move vmlinux.h include
#include <linux/errno.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
/* This include must be after vmlinux.h */
#include "include/fdmon.h"
struct hkey {
u64 call_id;
};
struct hval {
s32 payload;
};
/*
* We need to merge CALL and RET events for certain functions so that
* we can record CALL arguments and returned value.
*/
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 1373);
__type(key, struct hkey);
__type(value, struct hval);
} events SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1024 * sizeof(struct fdmon_event));
} rb SEC(".maps");
static u64 generate_call_id(enum fdmon_event_type type) {
return (u64)((s32)type << 31) | (s32)bpf_get_current_pid_tgid();
}
static int save_ustack(struct pt_regs* ctx, struct fdmon_event* event) {
long ret = bpf_get_stack(ctx, event->ustack_ents, sizeof(event->ustack_ents),
BPF_F_USER_STACK);
if (ret < 0) {
return -EINVAL;
}
event->num_ustack_ents = ret / sizeof(event->ustack_ents[0]);
return 0;
}
static struct fdmon_event* bpf_ringbuf_event_get(void) {
struct fdmon_event* event;
event = bpf_ringbuf_reserve(&rb, sizeof(*event), 0);
if (!event) {
return NULL;
}
event->type = FDMON_EVENT_INVALID;
event->num_ustack_ents = 0;
return event;
}
static int fdmon_event(struct pt_regs* ctx,
enum fdmon_event_type type,
s32 ofd,
s32 nfd) {
struct fdmon_event* event;
u64 id;
event = bpf_ringbuf_event_get();
if (!event) {
return -ENOMEM;
}
id = bpf_get_current_pid_tgid();
event->pid = id >> 32;
event->tid = (u32)id;
bpf_get_current_comm(&event->comm, sizeof(event->comm));
if (type == FDMON_EVENT_OPEN || type == FDMON_EVENT_DUP) {
if (save_ustack(ctx, event)) {
bpf_ringbuf_submit(event, 0);
return -EINVAL;
}
}
event->type = type;
event->nfd = nfd;
event->ofd = ofd;
bpf_ringbuf_submit(event, 0);
return 0;
}
/*
* Workaround
*
* These macros were introduced in libbpf-1.2 while we are still
* on libbpf-1.1
*/
#ifndef BPF_UPROBE
#define BPF_UPROBE(name, args...) BPF_KPROBE(name, ##args)
#endif
#ifndef BPF_URETPROBE
#define BPF_URETPROBE(name, args...) BPF_KRETPROBE(name, ##args)
#endif
static int __call_event(struct pt_regs* ctx,
enum fdmon_event_type type,
s32 payload) {
struct hval v;
struct hkey k;
long ret;
k.call_id = generate_call_id(type);
v.payload = payload;
ret = bpf_map_update_elem(&events, &k, &v, BPF_ANY);
return !ret ? 0 : -EINVAL;
}
static int __ret_event(struct pt_regs* ctx,
enum fdmon_event_type type,
s32 retval) {
struct hval* v;
struct hkey k;
k.call_id = generate_call_id(type);
v = bpf_map_lookup_elem(&events, &k);
if (v) {
return fdmon_event(ctx, type, v->payload, retval);
}
/*
* We didn't find the pairing CALL event, so use -1 for fd to indicate
* this
*/
return fdmon_event(ctx, type, -1, retval);
}
SEC("uretprobe")
int BPF_UPROBE(ret_open) {
return fdmon_event(ctx, FDMON_EVENT_OPEN, 0, PT_REGS_RC(ctx));
}
SEC("uprobe")
int BPF_UPROBE(call_dup, s32 fd) {
return __call_event(ctx, FDMON_EVENT_DUP, fd);
}
SEC("uretprobe")
int BPF_URETPROBE(ret_dup) {
return __ret_event(ctx, FDMON_EVENT_DUP, PT_REGS_RC(ctx));
}
SEC("uprobe")
int BPF_UPROBE(call_dup2, s32 ofd, s32 nfd) {
return fdmon_event(ctx, FDMON_EVENT_DUP, ofd, nfd);
}
SEC("uprobe")
int BPF_UPROBE(call_close, s32 fd) {
return fdmon_event(ctx, FDMON_EVENT_CLOSE, 0, fd);
}
char LICENSE[] SEC("license") = "Dual BSD/GPL";