Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,6 @@ Changes from 0.5 to 0.6
Changes from 0.6 to 0.7
~~~~~~~~~~~~~~~~~~~~~~~
o BPF_CC is gone, just use CLANG='zig cc' now.
o Added mprotect() syscall events (QUARK_EV_MPROTECT, -u on quark-mon).
The eBPF probe emits successful calls requesting execute permission
(X, RX, WX, or RWX); non-executable masks and failed calls are suppressed.
23 changes: 23 additions & 0 deletions bpf_queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,24 @@ ebpf_events_to_raw(struct quark_queue *qq, struct ebpf_event_header *ev)

break;
}
case EBPF_EVENT_PROCESS_MPROTECT: {
struct ebpf_process_mprotect_event *mprotect;
struct quark_mprotect *qmprotect;

mprotect = (struct ebpf_process_mprotect_event *)ev;
if ((raw = raw_event_alloc(RAW_MPROTECT)) == NULL)
goto bad;

raw->pid = mprotect->pids.tgid;
raw->time = ev->ts;

qmprotect = &raw->mprotect.quark_mprotect;
qmprotect->addr = mprotect->addr;
qmprotect->len = mprotect->len;
qmprotect->prot = mprotect->prot;

break;
}
case EBPF_EVENT_PROCESS_LOAD_MODULE: {
struct ebpf_process_load_module_event *module;
struct quark_module_load *qml;
Expand Down Expand Up @@ -1255,6 +1273,11 @@ bpf_queue_open1(struct quark_queue *qq, int use_fentry)
if (qq->flags & QQ_MODULE_LOAD)
bpf_program__set_autoload(p->progs.module_load, 1);

if (qq->flags & QQ_MPROTECT) {
bpf_program__set_autoload(p->progs.tracepoint_syscalls_sys_enter_mprotect, 1);
bpf_program__set_autoload(p->progs.tracepoint_syscalls_sys_exit_mprotect, 1);
}

if (qq->flags & QQ_GETPID)
bpf_program__set_autoload(p->progs.tracepoint_syscalls_sys_exit_getpid, 1);

Expand Down
9 changes: 9 additions & 0 deletions elastic-ebpf/GPL/Events/EbpfEventProto.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ enum ebpf_event_type {
EBPF_EVENT_PROCESS_LOAD_MODULE = (1 << 19),
EBPF_EVENT_NETWORK_DNS_PKT = (1 << 20),
EBPF_EVENT_PROCESS_GETPID = (1 << 21),
EBPF_EVENT_PROCESS_MPROTECT = (1 << 22),
};

struct ebpf_event_header {
Expand Down Expand Up @@ -380,6 +381,14 @@ struct ebpf_process_load_module_event {
struct ebpf_varlen_fields_start vl_fields;
} __attribute__((packed));

struct ebpf_process_mprotect_event {
struct ebpf_event_header hdr;
struct ebpf_pid_info pids;
uint64_t addr;
uint64_t len;
uint64_t prot;
} __attribute__((packed));

enum ebpf_net_info_transport {
EBPF_NETWORK_EVENT_TRANSPORT_TCP = 1,
EBPF_NETWORK_EVENT_TRANSPORT_UDP = 2,
Expand Down
80 changes: 80 additions & 0 deletions elastic-ebpf/GPL/Events/Process/Probe.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ DECL_FIELD_OFFSET(iov_iter, __iov);

#define S_ISUID 0004000
#define S_ISGID 0002000
#define PROT_EXEC 0x4

SEC("tp_btf/sched_process_fork")
int BPF_PROG(sched_process_fork, const struct task_struct *parent, const struct task_struct *child)
Expand Down Expand Up @@ -450,6 +451,85 @@ int BPF_KPROBE(kprobe__arch_ptrace,
return r;
}

SEC("tracepoint/syscalls/sys_enter_mprotect")
int tracepoint_syscalls_sys_enter_mprotect(struct syscall_trace_enter *ctx)
{
preempt_disable();
if (ebpf_events_is_trusted_pid())
goto out;

struct mprotect_args {
short common_type;
char common_flags;
char common_preempt_count;
int common_pid;
int __syscall_nr;
unsigned long start;
size_t len;
unsigned long prot;
};
struct mprotect_args *ex_args = (struct mprotect_args *)ctx;
const struct task_struct *task = (struct task_struct *)bpf_get_current_task();

if (is_kernel_thread(task))
goto out;

if (!(ex_args->prot & PROT_EXEC))
goto out;

struct ebpf_events_state state = {};
state.mprotect.addr = ex_args->start;
state.mprotect.len = ex_args->len;
state.mprotect.prot = ex_args->prot;
ebpf_events_state__set(EBPF_EVENTS_STATE_MPROTECT, &state);

out:
preempt_enable();
return 0;
}

SEC("tracepoint/syscalls/sys_exit_mprotect")
int tracepoint_syscalls_sys_exit_mprotect(struct syscall_trace_exit *args)
{
preempt_disable();

struct ebpf_events_state *state = ebpf_events_state__get(EBPF_EVENTS_STATE_MPROTECT);
if (!state)
goto out;

u64 addr = state->mprotect.addr;
u64 len = state->mprotect.len;
u64 prot = state->mprotect.prot;
ebpf_events_state__del(EBPF_EVENTS_STATE_MPROTECT);

if (BPF_CORE_READ(args, ret) != 0)
goto out;

if (ebpf_events_is_trusted_pid())
goto out;

const struct task_struct *task = (struct task_struct *)bpf_get_current_task();
if (is_kernel_thread(task))
goto out;

struct ebpf_process_mprotect_event *event = bpf_ringbuf_reserve(&ringbuf, sizeof(*event), 0);
if (!event)
goto out;

event->hdr.type = EBPF_EVENT_PROCESS_MPROTECT;
event->hdr.ts = bpf_ktime_get_boot_ns();
ebpf_pid_info__fill(&event->pids, task);

event->addr = addr;
event->len = len;
event->prot = prot;

bpf_ringbuf_submit(event, 0);
out:
preempt_enable();
return 0;
}

SEC("tracepoint/syscalls/sys_enter_shmget")
int tracepoint_syscalls_sys_enter_shmget(struct syscall_trace_enter *ctx)
{
Expand Down
8 changes: 8 additions & 0 deletions elastic-ebpf/GPL/Events/State.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ enum ebpf_events_state_op {
EBPF_EVENTS_STATE_CHOWN = 9,
EBPF_EVENTS_STATE_FS_CREATE = 10,
EBPF_EVENTS_STATE_MEMFD_CREATE = 11,
EBPF_EVENTS_STATE_MPROTECT = 12,
};

struct ebpf_events_key {
Expand Down Expand Up @@ -86,6 +87,12 @@ struct ebpf_events_memfd_create_state {
unsigned int flags;
};

struct ebpf_events_mprotect_state {
u64 addr;
u64 len;
u64 prot;
};

struct ebpf_events_state {
union {
struct ebpf_events_unlink_state unlink;
Expand All @@ -98,6 +105,7 @@ struct ebpf_events_state {
struct ebpf_events_writev_state writev;
struct ebpf_events_chown_state chown;
struct ebpf_events_memfd_create_state memfd;
struct ebpf_events_mprotect_state mprotect;
/* struct ebpf_events_fs_create fs_create; nada */
};
};
Expand Down
4 changes: 3 additions & 1 deletion quark-mon.8
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
.Nd monitor and print quark events
.Sh SYNOPSIS
.Nm quark-mon
.Op Fl BbDEeFGgHhkMNnSsTtv
.Op Fl BbDEeFGgHhkMNnSsTtuv
.Op Fl C Ar filename
.Op Fl K Ar kubeconfig
.Op Fl l Ar maxlength
Expand Down Expand Up @@ -130,6 +130,8 @@ Enable ptrace event tracing.
.It Fl t
Don't supress thread events, this is only useful for debugging and will likely
be zapped in the future.
.It Fl u
Enable mprotect events.
.It Fl v
Increase verbosity, can be specified multiple times for more verbosity.
.It Fl V
Expand Down
7 changes: 5 additions & 2 deletions quark-mon.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ static void
usage(void)
{
fprintf(stderr, "usage: %s -h\n", program_invocation_short_name);
fprintf(stderr, "usage: %s [-BbDeFGgHhkLMNnSsTtv]\n",
fprintf(stderr, "usage: %s [-BbDeFGgHhkLMNnSsTtuv]\n",
program_invocation_short_name);
fprintf(stderr, "%16c [-C filename ] [-K kubeconfig] "
"[-l maxlength] [-m maxnodes]\n", ' ');
Expand Down Expand Up @@ -185,7 +185,7 @@ main(int argc, char *argv[])
!strcmp(argv[1], "help")))
display_man();

while ((ch = getopt(argc, argv, "BbC:DEeFGgHhK:kLl:Mm:NnP:Ttr:SsvV")) != -1) {
while ((ch = getopt(argc, argv, "BbC:DEeFGgHhK:kLl:Mm:NnP:Ttr:SsuvV")) != -1) {
const char *errstr;

switch (ch) {
Expand Down Expand Up @@ -309,6 +309,9 @@ main(int argc, char *argv[])
case 'T':
qa.flags |= QQ_PTRACE;
break;
case 'u':
qa.flags |= QQ_MPROTECT;
break;
case 't':
qa.flags |= QQ_THREAD_EVENTS;
break;
Expand Down
90 changes: 90 additions & 0 deletions quark-test.c
Original file line number Diff line number Diff line change
Expand Up @@ -1376,6 +1376,95 @@ t_shmget(const struct test *t, struct quark_queue_attr *qa)
return (0);
}

static int
t_mprotect(const struct test *t, struct quark_queue_attr *qa)
{
struct quark_queue qq;
const struct quark_event *qev;
const struct quark_mprotect *qmprotect;
void *addr[4];
void *suppressed_addr;
const size_t len = 4096;
const int expected[] = {
PROT_EXEC,
PROT_READ | PROT_EXEC,
PROT_WRITE | PROT_EXEC,
PROT_READ | PROT_WRITE | PROT_EXEC,
};
const int suppressed[] = {
PROT_NONE,
PROT_READ,
PROT_WRITE,
PROT_READ | PROT_WRITE,
};
int saw[nitems(expected)] = { 0 };
size_t i, j;
int seen = 0;

qa->flags |= QQ_MPROTECT;

if (quark_queue_open(&qq, qa) != 0)
err(1, "quark_queue_open");

suppressed_addr = mmap(NULL, len, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (suppressed_addr == MAP_FAILED)
err(1, "mmap");

/* Non-executable protections must not enter the ring buffer. */
for (i = 0; i < nitems(suppressed); i++) {
if (mprotect(suppressed_addr, len, suppressed[i]) == -1)
err(1, "mprotect");
}

/* Failed executable requests must not enter the ring buffer. */
if (mprotect((void *)(uintptr_t)1, len, PROT_EXEC) != -1)
errx(1, "unaligned mprotect unexpectedly succeeded");

for (i = 0; i < nitems(expected); i++) {
addr[i] = mmap(NULL, len, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (addr[i] == MAP_FAILED)
err(1, "mmap");
if (mprotect(addr[i], len, expected[i]) == -1)
err(1, "mprotect");
}

while (seen < (int)nitems(expected)) {
qev = drain_for_pid(&qq, getpid());
if (!(qev->events & QUARK_EV_MPROTECT))
continue;
qmprotect = &qev->mprotect;
for (i = 0; i < nitems(expected); i++) {
if (qmprotect->prot == (u64)expected[i] &&
qmprotect->addr == (u64)(uintptr_t)addr[i])
break;
}
if (i == nitems(expected)) {
errx(1, "unexpected mprotect prot 0x%llx",
(unsigned long long)qmprotect->prot);
}
assert(qmprotect->len == len);
assert(!saw[i]);
saw[i] = 1;
seen++;
}

for (i = 0; i < nitems(saw); i++)
assert(saw[i]);

if (munmap(suppressed_addr, len) == -1)
err(1, "munmap");
for (j = 0; j < nitems(addr); j++) {
if (munmap(addr[j], len) == -1)
err(1, "munmap");
}

quark_queue_close(&qq);

return (0);
}

static int
t_shm_open(const struct test *t, struct quark_queue_attr *qa)
{
Expand Down Expand Up @@ -2441,6 +2530,7 @@ struct test all_tests[] = {
T_EBPF(t_memfd),
T_EBPF(t_memfd_exec),
T_EBPF(t_shmget),
T_EBPF(t_mprotect),
T_EBPF(t_shm_open),
T_EBPF(t_tty_load),
T_EBPF(t_tty),
Expand Down
Loading