Skip to content

Commit

Permalink
Add probes for memfd_create and friends (#185)
Browse files Browse the repository at this point in the history
* Add probes for memfd_create and friends

New events added:

EBPF_EVENT_FILE_MEMFD_OPEN
EBPF_EVENT_FILE_SHMEM_OPEN
EBPF_EVENT_PROCESS_MEMFD_CREATE
EBPF_EVENT_PROCESS_SHMGET
EBPF_EVENT_PROCESS_PTRACE
EBPF_EVENT_PROCESS_LOAD_MODULE

Also added new fields to process exec event

* clang-format fixes

* Address review comments

* clang-format 14 fixes for unrelated code

* Fix memory corruption in EventsTrace

* Add missing commas in JSON output

* Fix commas again

* Condense is_foo from exec into flags. (#194)

* Address review

* clang-format

* clang format

* Use types with well defined sizes

* Address review comments

* clang-format fixes

---------

Co-authored-by: Christiano Haesbaert <[email protected]>
  • Loading branch information
stanek-michal and haesbaert authored May 29, 2024
1 parent cea6329 commit 61bdb7f
Show file tree
Hide file tree
Showing 6 changed files with 617 additions and 42 deletions.
71 changes: 68 additions & 3 deletions GPL/Events/EbpfEventProto.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,15 @@ enum ebpf_event_type {
EBPF_EVENT_FILE_CREATE = (1 << 9),
EBPF_EVENT_FILE_RENAME = (1 << 10),
EBPF_EVENT_FILE_MODIFY = (1 << 11),
EBPF_EVENT_NETWORK_CONNECTION_ACCEPTED = (1 << 12),
EBPF_EVENT_NETWORK_CONNECTION_ATTEMPTED = (1 << 13),
EBPF_EVENT_NETWORK_CONNECTION_CLOSED = (1 << 14),
EBPF_EVENT_FILE_MEMFD_OPEN = (1 << 12),
EBPF_EVENT_FILE_SHMEM_OPEN = (1 << 13),
EBPF_EVENT_NETWORK_CONNECTION_ACCEPTED = (1 << 14),
EBPF_EVENT_NETWORK_CONNECTION_ATTEMPTED = (1 << 15),
EBPF_EVENT_NETWORK_CONNECTION_CLOSED = (1 << 16),
EBPF_EVENT_PROCESS_MEMFD_CREATE = (1 << 17),
EBPF_EVENT_PROCESS_SHMGET = (1 << 18),
EBPF_EVENT_PROCESS_PTRACE = (1 << 19),
EBPF_EVENT_PROCESS_LOAD_MODULE = (1 << 20),
};

struct ebpf_event_header {
Expand All @@ -57,6 +63,8 @@ enum ebpf_varlen_field_type {
EBPF_VL_FIELD_TTY_OUT,
EBPF_VL_FIELD_PIDS_SS_CGROUP_PATH,
EBPF_VL_FIELD_SYMLINK_TARGET_PATH,
EBPF_VL_FIELD_MOD_VERSION,
EBPF_VL_FIELD_MOD_SRCVERSION,
};

// Convenience macro to iterate all the variable length fields in an event
Expand Down Expand Up @@ -154,6 +162,7 @@ struct ebpf_file_delete_event {
struct ebpf_varlen_fields_start vl_fields;
} __attribute__((packed));

// reused by memfd_open and shmem_open events
struct ebpf_file_create_event {
struct ebpf_event_header hdr;
struct ebpf_pid_info pids;
Expand Down Expand Up @@ -210,12 +219,18 @@ struct ebpf_process_fork_event {
struct ebpf_varlen_fields_start vl_fields;
} __attribute__((packed));

#define EXEC_F_SETUID (1 << 0)
#define EXEC_F_SETGID (1 << 1)
#define EXEC_F_MEMFD (1 << 2)

struct ebpf_process_exec_event {
struct ebpf_event_header hdr;
struct ebpf_pid_info pids;
struct ebpf_cred_info creds;
struct ebpf_tty_dev ctty;
char comm[TASK_COMM_LEN];
uint32_t inode_nlink;
uint32_t flags;

// Variable length fields: cwd, argv, env, filename, pids_ss_cgroup_path
struct ebpf_varlen_fields_start vl_fields;
Expand Down Expand Up @@ -271,6 +286,56 @@ struct ebpf_process_setgid_event {
uint32_t new_euid;
} __attribute__((packed));

// from linux/memfd.h:
//
/* flags for memfd_create(2) (unsigned int) */
#ifndef MFD_CLOEXEC
#define MFD_CLOEXEC 0x0001U
#endif
#ifndef MFD_ALLOW_SEALING
#define MFD_ALLOW_SEALING 0x0002U
#endif
#ifndef MFD_HUGETLB
#define MFD_HUGETLB 0x0004U
#endif
/* not executable and sealed to prevent changing to executable. */
#ifndef MFD_NOEXEC_SEAL
#define MFD_NOEXEC_SEAL 0x0008U
#endif
/* executable */
#ifndef MFD_EXEC
#define MFD_EXEC 0x0010U
#endif
struct ebpf_process_memfd_create_event {
struct ebpf_event_header hdr;
struct ebpf_pid_info pids;
uint32_t flags; // memfd_create flags
// Variable length fields: memfd name
struct ebpf_varlen_fields_start vl_fields;
} __attribute__((packed));

struct ebpf_process_shmget_event {
struct ebpf_event_header hdr;
struct ebpf_pid_info pids;
int64_t key;
uint64_t size;
int64_t shmflg; // shmget() flags
} __attribute__((packed));

struct ebpf_process_ptrace_event {
struct ebpf_event_header hdr;
struct ebpf_pid_info pids;
uint32_t child_pid;
int64_t request;
} __attribute__((packed));

struct ebpf_process_load_module_event {
struct ebpf_event_header hdr;
struct ebpf_pid_info pids;
// Variable length fields: filename, mod version, mod srcversion
struct ebpf_varlen_fields_start vl_fields;
} __attribute__((packed));

enum ebpf_net_info_transport {
EBPF_NETWORK_EVENT_TRANSPORT_TCP = 1,
};
Expand Down
125 changes: 91 additions & 34 deletions GPL/Events/File/Probe.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,60 @@ int BPF_KPROBE(kprobe__vfs_unlink)
return vfs_unlink__enter(de);
}

// prepare a file event and send it to ringbuf.
// if path_prefix is non-NULL then event will only be sent to ringbuf if file path has that prefix
static void prepare_and_send_file_event(struct file *f,
enum ebpf_event_type type,
const char *path_prefix,
int path_prefix_len)
{
struct ebpf_file_create_event *event = get_event_buffer();
if (!event)
return;

event->hdr.type = type;
event->hdr.ts = bpf_ktime_get_ns();

struct task_struct *task = (struct task_struct *)bpf_get_current_task();
struct path p = BPF_CORE_READ(f, f_path);
ebpf_pid_info__fill(&event->pids, task);
ebpf_cred_info__fill(&event->creds, task);
event->mntns = mntns(task);
bpf_get_current_comm(event->comm, TASK_COMM_LEN);
ebpf_file_info__fill(&event->finfo, p.dentry);

// Variable length fields
ebpf_vl_fields__init(&event->vl_fields);
struct ebpf_varlen_field *field;
long size;

// symlink_target_path
field = ebpf_vl_field__add(&event->vl_fields, EBPF_VL_FIELD_SYMLINK_TARGET_PATH);
char *link = BPF_CORE_READ(p.dentry, d_inode, i_link);
size = read_kernel_str_or_empty_str(field->data, PATH_MAX, link);
ebpf_vl_field__set_size(&event->vl_fields, field, size);

// pids ss cgroup path
field = ebpf_vl_field__add(&event->vl_fields, EBPF_VL_FIELD_PIDS_SS_CGROUP_PATH);
size = ebpf_resolve_pids_ss_cgroup_path_to_string(field->data, task);
ebpf_vl_field__set_size(&event->vl_fields, field, size);

// path
field = ebpf_vl_field__add(&event->vl_fields, EBPF_VL_FIELD_PATH);
size = ebpf_resolve_path_to_string(field->data, &p, task);
ebpf_vl_field__set_size(&event->vl_fields, field, size);

// skip event if prefix is specified and file path does not start with it
if (path_prefix) {
if ((path_prefix_len > 0) && (size >= path_prefix_len)) {
if (is_equal_prefix(field->data, path_prefix, path_prefix_len))
bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0);
}
} else {
bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0);
}
}

static int do_filp_open__exit(struct file *f)
{
/*
Expand All @@ -232,43 +286,46 @@ static int do_filp_open__exit(struct file *f)

fmode_t fmode = BPF_CORE_READ(f, f_mode);
if (fmode & (fmode_t)0x100000) { // FMODE_CREATED
struct ebpf_file_create_event *event = get_event_buffer();
if (!event)
// generate a file creation event
prepare_and_send_file_event(f, EBPF_EVENT_FILE_CREATE, NULL, 0);
} else {
// check if memfd file is being opened
struct path p = BPF_CORE_READ(f, f_path);
struct dentry *curr_dentry = BPF_CORE_READ(&p, dentry);
struct qstr component = BPF_CORE_READ(curr_dentry, d_name);
char buf_filename[8] = {0};
int ret =
bpf_probe_read_kernel_str(buf_filename, sizeof(MEMFD_STRING), (void *)component.name);
if (ret <= 0) {
bpf_printk("could not read d_name at %p\n", component.name);
goto out;
}
// check if file name starts with "memfd:"
int is_memfd = is_equal_prefix(MEMFD_STRING, buf_filename, sizeof(MEMFD_STRING) - 1);
if (is_memfd) {
// generate a memfd file open event
prepare_and_send_file_event(f, EBPF_EVENT_FILE_MEMFD_OPEN, NULL, 0);
goto out;
}

event->hdr.type = EBPF_EVENT_FILE_CREATE;
event->hdr.ts = bpf_ktime_get_ns();

struct task_struct *task = (struct task_struct *)bpf_get_current_task();
struct path p = BPF_CORE_READ(f, f_path);
ebpf_pid_info__fill(&event->pids, task);
ebpf_cred_info__fill(&event->creds, task);
event->mntns = mntns(task);
bpf_get_current_comm(event->comm, TASK_COMM_LEN);
ebpf_file_info__fill(&event->finfo, p.dentry);

// Variable length fields
ebpf_vl_fields__init(&event->vl_fields);
struct ebpf_varlen_field *field;
long size;

// path
field = ebpf_vl_field__add(&event->vl_fields, EBPF_VL_FIELD_PATH);
size = ebpf_resolve_path_to_string(field->data, &p, task);
ebpf_vl_field__set_size(&event->vl_fields, field, size);

// symlink_target_path
field = ebpf_vl_field__add(&event->vl_fields, EBPF_VL_FIELD_SYMLINK_TARGET_PATH);
char *link = BPF_CORE_READ(p.dentry, d_inode, i_link);
size = read_kernel_str_or_empty_str(field->data, PATH_MAX, link);
ebpf_vl_field__set_size(&event->vl_fields, field, size);

// pids ss cgroup path
field = ebpf_vl_field__add(&event->vl_fields, EBPF_VL_FIELD_PIDS_SS_CGROUP_PATH);
size = ebpf_resolve_pids_ss_cgroup_path_to_string(field->data, task);
ebpf_vl_field__set_size(&event->vl_fields, field, size);
struct vfsmount *curr_vfsmount = BPF_CORE_READ(&p, mnt);
const char *fs_type_name = BPF_CORE_READ(curr_vfsmount, mnt_sb, s_type, name);

bpf_ringbuf_output(&ringbuf, event, EVENT_SIZE(event), 0);
// check if /dev/shm shared memory file is being opened
// first check if fs is tmpfs
char buf_fsname[8] = {0};
ret = bpf_probe_read_kernel_str(buf_fsname, sizeof(TMPFS_STRING), (void *)fs_type_name);
if (ret <= 0) {
bpf_printk("could not read fsname at %p\n", fs_type_name);
goto out;
}

int is_tmpfs = is_equal_prefix(buf_fsname, TMPFS_STRING, sizeof(TMPFS_STRING) - 1);
if (is_tmpfs) {
// now filter for /dev/shm prefix, if there is match - send an SHMEM file open event
prepare_and_send_file_event(f, EBPF_EVENT_FILE_SHMEM_OPEN, DEVSHM_STRING,
sizeof(DEVSHM_STRING) - 1);
}
}

out:
Expand Down
56 changes: 56 additions & 0 deletions GPL/Events/Helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,48 @@
* You may choose either one of them if you use this software.
*/

/* $OpenBSD: strncmp.c,v 1.11 2014/06/10 04:16:57 deraadt Exp $ */

/*
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/

#ifndef EBPF_EVENTPROBE_HELPERS_H
#define EBPF_EVENTPROBE_HELPERS_H

#include "EbpfEventProto.h"

const volatile int consumer_pid = 0;

#define MEMFD_STRING "memfd:"
#define TMPFS_STRING "tmpfs"
#define DEVSHM_STRING "/dev/shm"

#if BPF_DEBUG_TRACE == 0
#undef bpf_printk
#define bpf_printk(fmt, ...)
Expand Down Expand Up @@ -279,4 +314,25 @@ static bool is_consumer()
return consumer_pid == pid;
}

static int strncmp(const char *s1, const char *s2, size_t n)
{

if (n == 0)
return (0);
do {
if (*s1 != *s2++)
return (*(unsigned char *)s1 - *(unsigned char *)--s2);
if (*s1++ == 0)
break;
} while (--n != 0);
return (0);
}

// compares first 'len' characters of str1 and str2, returns 1 if equal
// NOTE: no bounds check, assumes use under eBPF verifier
static int is_equal_prefix(const char *str1, const char *str2, int len)
{
return !strncmp(str1, str2, len);
}

#endif // EBPF_EVENTPROBE_HELPERS_H
Loading

0 comments on commit 61bdb7f

Please sign in to comment.