Skip to content

Commit

Permalink
8.5: cherry-pick bafa987 and aea0f04 from main (#160)
Browse files Browse the repository at this point in the history
* Move BPF support-detection logic from endpoint to this repository (#151)

* De-generify feature logic

Since it's existed, the "features" argument passed into
ebpf_event_ctx__create via the opts struct has been used for the sole
purpose of bpf trampoline detection. There's also no scenario currently
where we have to force kprobes over trampolines (i.e. the library always
performs the detection logic), meaning having to go through the two-step
process of ebpf_detect_system_features and ebpf_event_ctx__new is an
unncecessary level of indirection.

There is one use case for which *knowing* (but not setting) the features
is useful, the TestFeaturesCorrect multi-kernel test case. To ensure we
can still test the feature detection logic, add
ebpf_event_ctx__get_features to expose the features detected by the
library, while removing the ability to set them.

If in the future, more features come along and it becomes necessary to
toggle them on a rather granular basis, we can revert this, but for the
time being, it simplifies the code nicely.

* Move support detection logic to this repository

Since the very beginning, the logic to detect if we support BPF has been
done in endpoint instead of in the ebpf events library. This is wonky.
Logically, this logic belongs here, the library should be performing all
necessary checks and returning -ENOTSUP if it can't load the probes.
library users shouldn't have to divine the specific system
characteristics needed for the library to successfully run themselves.

* Change stale docs -- minimum kernel is 5.10.16

* Remove tty_write prototype detection logic

Since our minimum supported version is now 5.10.16, this is unneeded
(prototype change was made in 5.10.11)

* Remove --features-autodetect from testrunner

* Make verbose log more explicit

Co-authored-by: Mattia Meleleo <[email protected]>

* Add missing newline

Co-authored-by: Nicholas Berlin <[email protected]>

* Fix typo buf -> bug

Co-authored-by: Nicholas Berlin <[email protected]>

* Move BPF_CORE_READ calls to tty_write__enter

Co-Authored-By: Mattia Meleleo <[email protected]>

* Cut down support detection logic

This reduces the stuff we check for to just
bpf_support && (kernel_version > 5.10.16).

While this doesn't cover 100% of cases, it will likely cover 99.9% of
cases and give us a easy to read error message in those cases. Probe
loading will ultimately fail if we're e.g. on a 5.10.16+ kernel with BTF
that doesn't have kprobes or ftrace enabled, which will cause endpoint
to not use BPF.

* Fix incorrect boolean return

* Print full un.release on kernel version error

* Fix breakage on Amazonlinux2 kernels

Too much logic was removed in the tty_write logic removal. We should
still be detecting if tty_write exists in BTF and falling back to a
kprobe if so.

* Fix incorrect version detection on Ubuntu

Ubuntu provides /proc/version_signature so that the true upstream
source version can be obtained. Use it instead of utsname.release.

* Fix kernel version detection on Debian

See comments, we need to use un.version on Debian, as that's the only
reliable way to get the upstream source version.

Co-authored-by: Mattia Meleleo <[email protected]>
Co-authored-by: Nicholas Berlin <[email protected]>

* EventProbe: iterate iovec segs (#158)

* EventProbe: iterate iovec segs

* Update GPL/Events/Process/Probe.bpf.c

Co-authored-by: Nicholas Berlin <[email protected]>

Co-authored-by: Rhys Rustad-Elliott <[email protected]>
Co-authored-by: Nicholas Berlin <[email protected]>
  • Loading branch information
3 people authored Oct 21, 2022
1 parent 20d902b commit b2e9659
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 173 deletions.
2 changes: 1 addition & 1 deletion EXCLUSIONS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
elastic/ebpf is tested against a matrix of kernels. The code contained in this
repository is intended for use with linux kernel version 5.10.10 or higher,
repository is intended for use with linux kernel version 5.10.16 or higher,
with BTF (and other requisite configs) enabled.

The following is a list of kernels where the ebpf programs fail to load or
Expand Down
141 changes: 49 additions & 92 deletions GPL/Events/Process/Probe.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@
#include "Helpers.h"
#include "PathResolver.h"

/* tty_write */
DECL_FUNC_ARG(redirected_tty_write, iter);
DECL_FUNC_ARG(redirected_tty_write, buf);
DECL_FUNC_ARG(redirected_tty_write, count);
DECL_FUNC_ARG_EXISTS(redirected_tty_write, iter);

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 @@ -241,18 +235,14 @@ int BPF_KPROBE(kprobe__commit_creds, struct cred *new)
return commit_creds__enter(new);
}

static int tty_write__enter(const char *buf, ssize_t count, struct file *f)
#define MAX_NR_SEGS 8

static int tty_write__enter(struct kiocb *iocb, struct iov_iter *from)
{
if (is_consumer())
goto out;

if (count <= 0)
goto out;

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

struct file *f = BPF_CORE_READ(iocb, ki_filp);
struct tty_file_private *tfp = (struct tty_file_private *)BPF_CORE_READ(f, private_data);
struct tty_struct *tty = BPF_CORE_READ(tfp, tty);

Expand All @@ -263,108 +253,75 @@ static int tty_write__enter(const char *buf, ssize_t count, struct file *f)
// https://elixir.bootlin.com/linux/v5.19.9/source/drivers/tty/tty_io.c#L2643
bool is_master = false;
struct ebpf_tty_dev master = {};
struct ebpf_tty_dev slave = {};
if (BPF_CORE_READ(tty, driver, type) == TTY_DRIVER_TYPE_PTY &&
BPF_CORE_READ(tty, driver, subtype) == PTY_TYPE_MASTER) {
struct tty_struct *tmp = BPF_CORE_READ(tty, link);
ebpf_tty_dev__fill(&master, tty);
ebpf_tty_dev__fill(&event->tty, tmp);
ebpf_tty_dev__fill(&slave, tmp);
is_master = true;
} else {
ebpf_tty_dev__fill(&event->tty, tty);
ebpf_tty_dev__fill(&slave, tty);
}

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

u64 len = count > TTY_OUT_MAX ? TTY_OUT_MAX : count;
event->tty_out_len = len;
event->tty_out_truncated = count > TTY_OUT_MAX ? count - TTY_OUT_MAX : 0;

const struct task_struct *task = (struct task_struct *)bpf_get_current_task();
ebpf_pid_info__fill(&event->pids, task);
ebpf_ctty__fill(&event->ctty, task);
bpf_get_current_comm(event->comm, TASK_COMM_LEN);

if (event->tty.major == 0 && event->tty.minor == 0) {
bpf_ringbuf_discard(event, 0);
if (slave.major == 0 && slave.minor == 0) {
goto out;
}

if (bpf_probe_read_user(event->tty_out, len, (void *)buf)) {
bpf_printk("tty_write__enter: error reading buf\n");
bpf_ringbuf_discard(event, 0);
if ((is_master && !(master.termios.c_lflag & ECHO)) && !(slave.termios.c_lflag & ECHO)) {
goto out;
}

if ((is_master && !(master.termios.c_lflag & ECHO)) && !(event->tty.termios.c_lflag & ECHO)) {
bpf_printk("tty_write__enter: discarding %s\n", event->tty_out);
bpf_ringbuf_discard(event, 0);
goto out;
}
const struct task_struct *task = (struct task_struct *)bpf_get_current_task();
u64 nr_segs = BPF_CORE_READ(from, nr_segs);
nr_segs = nr_segs > MAX_NR_SEGS ? MAX_NR_SEGS : nr_segs;
const struct iovec *iov = BPF_CORE_READ(from, iov);

bpf_ringbuf_submit(event, 0);
for (u8 seg = 0; seg < nr_segs; seg++) {
struct ebpf_process_tty_write_event *event =
bpf_ringbuf_reserve(&ringbuf, sizeof(*event), 0);
if (!event)
goto out;

struct iovec *cur_iov = (struct iovec *)&iov[seg];
const char *base = BPF_CORE_READ(cur_iov, iov_base);
size_t len = BPF_CORE_READ(cur_iov, iov_len);
if (len <= 0) {
bpf_ringbuf_discard(event, 0);
continue;
}

event->hdr.type = EBPF_EVENT_PROCESS_TTY_WRITE;
event->hdr.ts = bpf_ktime_get_ns();
u64 len_cap = len > TTY_OUT_MAX ? TTY_OUT_MAX : len;
event->tty_out_len = len_cap;
event->tty_out_truncated = len > TTY_OUT_MAX ? len - TTY_OUT_MAX : 0;
event->tty = slave;
ebpf_pid_info__fill(&event->pids, task);
ebpf_ctty__fill(&event->ctty, task);
bpf_get_current_comm(event->comm, TASK_COMM_LEN);

if (bpf_probe_read_user(event->tty_out, len_cap, (void *)base)) {
bpf_printk("tty_write__enter: error reading base\n");
bpf_ringbuf_discard(event, 0);
goto out;
}

bpf_ringbuf_submit(event, 0);
}

out:
return 0;
}

SEC("fentry/tty_write")
int BPF_PROG(fentry__tty_write)
int BPF_PROG(fentry__tty_write, struct kiocb *iocb, struct iov_iter *from)
{
const char *buf;
ssize_t count;
struct file *f;

if (FUNC_ARG_EXISTS(redirected_tty_write, iter)) {
struct iov_iter *ii = FUNC_ARG_READ(___type(ii), redirected_tty_write, iter);
buf = BPF_CORE_READ(ii, iov, iov_base);
count = BPF_CORE_READ(ii, iov, iov_len);

struct kiocb *iocb = (struct kiocb *)ctx[0];
f = BPF_CORE_READ(iocb, ki_filp);
} else {
buf = FUNC_ARG_READ(___type(buf), redirected_tty_write, buf);
count = FUNC_ARG_READ(___type(count), redirected_tty_write, count);

f = (struct file *)ctx[0];
}

return tty_write__enter(buf, count, f);
return tty_write__enter(iocb, from);
}

SEC("kprobe/tty_write")
int BPF_KPROBE(kprobe__tty_write)
int BPF_KPROBE(kprobe__tty_write, struct kiocb *iocb, struct iov_iter *from)
{
const char *buf;
ssize_t count;
struct file *f;

if (FUNC_ARG_EXISTS(redirected_tty_write, iter)) {
struct iov_iter ii;
if (FUNC_ARG_READ_PTREGS(ii, redirected_tty_write, iter)) {
bpf_printk("kprobe__tty_write: error reading iov_iter\n");
goto out;
}
buf = BPF_CORE_READ(ii.iov, iov_base);
count = BPF_CORE_READ(ii.iov, iov_len);

struct kiocb *iocb = (struct kiocb *)PT_REGS_PARM1(ctx);
f = BPF_CORE_READ(iocb, ki_filp);
} else {
if (FUNC_ARG_READ_PTREGS(buf, redirected_tty_write, buf)) {
bpf_printk("kprobe__tty_write: error reading buf\n");
goto out;
}
if (FUNC_ARG_READ_PTREGS(count, redirected_tty_write, count)) {
bpf_printk("kprobe__tty_write: error reading count\n");
goto out;
}

f = (struct file *)PT_REGS_PARM1(ctx);
}

return tty_write__enter(buf, count, f);

out:
return 0;
return tty_write__enter(iocb, from);
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ located under the `GPL/` directory while all non-GPL code is located under the

## Event Sourcing

On newer kernels (5.10.10+), Elastic endpoint uses eBPF to source the various
On newer kernels (5.10.16+), Elastic endpoint uses eBPF to source the various
security events it ultimately sends up to an Elasticsearch cluster (e.g.
process execution, file creation, file rename). On older kernels, this data is
sourced via
Expand Down
40 changes: 4 additions & 36 deletions non-GPL/Events/EventsTrace/EventsTrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include <sys/time.h>

#include <arpa/inet.h>
#include <linux/termios.h>
#include <netinet/in.h>

#include <EbpfEvents.h>
Expand Down Expand Up @@ -54,10 +53,6 @@ enum cmdline_opts {
NETWORK_CONNECTION_ATTEMPTED,
NETWORK_CONNECTION_ACCEPTED,
NETWORK_CONNECTION_CLOSED,

// Features
BPF_TRAMP,

CMDLINE_MAX
};

Expand All @@ -78,10 +73,6 @@ static uint64_t cmdline_to_lib[CMDLINE_MAX] = {
x(NETWORK_CONNECTION_ACCEPTED)
x(NETWORK_CONNECTION_CLOSED)
#undef x

#define x(name) [name] = EBPF_FEATURE_##name,
x(BPF_TRAMP)
#undef x
// clang-format on
};

Expand All @@ -105,17 +96,13 @@ static const struct argp_option opts[] = {
"Print network connection closed events", 0},
{"print-features-on-init", 'i', NULL, false,
"Print a message with feature information when probes have been successfully loaded", 1},
{"features-autodetect", 'd', NULL, false, "Autodetect features based on running kernel", 1},
{"set-bpf-tramp", EBPF_FEATURE_BPF_TRAMP, NULL, false, "Set feature supported: bpf trampoline",
1},
{"unbuffer-stdout", 'u', NULL, false, "Disable userspace stdout buffering", 2},
{"libbpf-verbose", 'v', NULL, false, "Log verbose libbpf logs to stderr", 2},
{},
};

uint64_t g_events_env = 0;
uint64_t g_features_env = 0;
uint64_t g_features_autodetect = 0;
uint64_t g_events_env = 0;
uint64_t g_features_env = 0;

bool g_print_features_init = 0;
bool g_unbuffer_stdout = 0;
Expand All @@ -136,9 +123,6 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
case 'a':
g_events_env = UINT64_MAX;
break;
case 'd':
g_features_autodetect = 1;
break;
case FILE_DELETE:
case FILE_CREATE:
case FILE_RENAME:
Expand All @@ -154,9 +138,6 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
case NETWORK_CONNECTION_CLOSED:
g_events_env |= cmdline_to_lib[key];
break;
case BPF_TRAMP:
g_features_env |= cmdline_to_lib[key];
break;
case ARGP_KEY_ARG:
argp_usage(state);
break;
Expand Down Expand Up @@ -218,11 +199,6 @@ static void out_int(const char *name, const long value)
printf("\"%s\":%ld", name, value);
}

static void out_bool(const char *name, const bool value)
{
printf("\"%s\":\"%s\"", name, value ? "TRUE" : "FALSE");
}

static void out_string(const char *name, const char *value)
{
printf("\"%s\":\"", name);
Expand Down Expand Up @@ -269,9 +245,6 @@ static void out_tty_dev(const char *name, struct ebpf_tty_dev *tty_dev)
out_int("winsize_rows", tty_dev->winsize.rows);
out_comma();
out_int("winsize_cols", tty_dev->winsize.cols);
out_comma();
out_bool("ECHO", tty_dev->termios.c_lflag & ECHO);

out_object_end();
}

Expand Down Expand Up @@ -720,20 +693,15 @@ int main(int argc, char **argv)
if (g_libbpf_verbose)
ebpf_set_verbose_logging();

struct ebpf_event_ctx_opts opts = {.events = g_events_env, .features = g_features_env};

if (g_features_autodetect)
ebpf_detect_system_features(&opts.features);

err = ebpf_event_ctx__new(&ctx, event_ctx_callback, opts);
err = ebpf_event_ctx__new(&ctx, event_ctx_callback, g_events_env);

if (err < 0) {
fprintf(stderr, "Could not create event context: %d %s\n", err, strerror(-err));
goto out;
}

if (g_print_features_init)
print_init_msg(opts.features);
print_init_msg(ebpf_event_ctx__get_features(ctx));

while (!exiting) {
err = ebpf_event_ctx__next(ctx, 10);
Expand Down
Loading

0 comments on commit b2e9659

Please sign in to comment.