-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Move from BCC to libbpf with CO-RE #6027
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
/* | ||
Copyright 2021 Gravitational, Inc. | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
#include "vmlinux.h" | ||
eyakubovich marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#include <bpf/bpf_helpers.h> /* most used helpers: SEC, __always_inline, etc */ | ||
#include <bpf/bpf_core_read.h> /* for BPF CO-RE helpers */ | ||
#include <bpf/bpf_tracing.h> /* for getting kprobe arguments */ | ||
|
||
#include "helpers.h" | ||
|
||
#define ARGSIZE 128 | ||
#define MAXARGS 20 | ||
|
||
// Size, in bytes, of the ring buffer used to report | ||
// audit events to userspace. This is the default, | ||
// the userspace can adjust this value based on config. | ||
#define EVENTS_BUF_SIZE (4096*8) | ||
|
||
char LICENSE[] SEC("license") = "Dual BSD/GPL"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this be Apache License Version 2? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it has the same license requirements as a kernel module: https://elixir.bootlin.com/linux/v5.8/source/include/linux/module.h#L182 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @klizhentas Is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you tell me more about how we distribute it? Are we using dual licensed components linking from here and deriving license of this file from those? How are we going to distribute the program? |
||
|
||
enum event_type { | ||
EVENT_ARG, | ||
EVENT_RET, | ||
}; | ||
|
||
struct data_t { | ||
// pid as in the userspace term (i.e. task->tgid in kernel). | ||
u64 pid; | ||
// ppid is the userspace term (i.e task->real_parent->tgid in kernel). | ||
u64 ppid; | ||
char comm[TASK_COMM_LEN]; | ||
enum event_type type; | ||
char argv[ARGSIZE]; | ||
int retval; | ||
u64 cgroup; | ||
}; | ||
|
||
BPF_RING_BUF(execve_events, EVENTS_BUF_SIZE); | ||
|
||
BPF_COUNTER(lost); | ||
|
||
static int __submit_arg(void *ptr, struct data_t *data) | ||
{ | ||
bpf_probe_read_user(data->argv, sizeof(data->argv), ptr); | ||
if (bpf_ringbuf_output(&execve_events, data, sizeof(struct data_t), 0) != 0) | ||
INCR_COUNTER(lost); | ||
return 1; | ||
} | ||
|
||
static int submit_arg(void *ptr, struct data_t *data) | ||
{ | ||
const char *argp = 0; | ||
bpf_probe_read_user(&argp, sizeof(argp), ptr); | ||
if (argp) { | ||
return __submit_arg((void *)(argp), data); | ||
} | ||
return 0; | ||
} | ||
|
||
static int enter_execve(const char *filename, | ||
const char *const *argv, | ||
const char *const *envp) | ||
{ | ||
// create data here and pass to submit_arg to save stack space (#555) | ||
struct data_t data = {}; | ||
struct task_struct *task; | ||
|
||
data.pid = bpf_get_current_pid_tgid() >> 32; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment is about how we report PID and TGID. I'm looking at @eyakubovich @klizhentas I'm thinking it's probably better for visibility to have both and PID and TGID be reported and switch the PID value to true PID in our events. What do you two think? https://github.com/iovisor/bcc/blob/master/libbpf-tools/execsnoop.bpf.c#L46-L57 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you should just report TGID (userspace PID). That's what an average user would expect. Unless you're debugging something, which thread performed an action is usually not interesting. |
||
data.cgroup = bpf_get_current_cgroup_id(); | ||
|
||
task = (struct task_struct *)bpf_get_current_task(); | ||
data.ppid = BPF_CORE_READ(task, real_parent, tgid); | ||
|
||
bpf_get_current_comm(&data.comm, sizeof(data.comm)); | ||
data.type = EVENT_ARG; | ||
|
||
__submit_arg((void *)filename, &data); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you do a little refactoring around There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here and elsewhere I wasn't trying to do much refactoring or improving. Mostly just enough to get it ported to libbpf reliably. Technically with a ring buffer, this split is no longer needed. We can use |
||
|
||
// skip first arg, as we submitted filename | ||
for (int i = 1; i < MAXARGS; i++) { | ||
if (submit_arg((void *)&argv[i], &data) == 0) | ||
goto out; | ||
} | ||
|
||
// handle truncated argument list | ||
char ellipsis[] = "..."; | ||
__submit_arg((void *)ellipsis, &data); | ||
out: | ||
return 0; | ||
} | ||
|
||
static int exit_execve(int ret) | ||
{ | ||
struct data_t data = {}; | ||
struct task_struct *task; | ||
|
||
data.pid = bpf_get_current_pid_tgid() >> 32; | ||
data.cgroup = bpf_get_current_cgroup_id(); | ||
|
||
task = (struct task_struct *)bpf_get_current_task(); | ||
data.ppid = BPF_CORE_READ(task, real_parent, tgid); | ||
|
||
bpf_get_current_comm(&data.comm, sizeof(data.comm)); | ||
data.type = EVENT_RET; | ||
data.retval = ret; | ||
|
||
if (bpf_ringbuf_output(&execve_events, &data, sizeof(data), 0) != 0) | ||
INCR_COUNTER(lost); | ||
|
||
return 0; | ||
} | ||
|
||
SEC("tp/syscalls/sys_execve") | ||
int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter *tp) | ||
{ | ||
const char *filename = (const char *)tp->args[0]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dumb question, but here (and elsewhere) we're guaranteed to always be able to index into There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe so (unless there's a kernel bug). The length of the args will match the number of args to the syscall. |
||
const char *const *argv = (const char *const *)tp->args[1]; | ||
const char *const *envp = (const char *const *)tp->args[2]; | ||
|
||
return enter_execve(filename, argv, envp); | ||
} | ||
|
||
SEC("tp/syscalls/sys_exit_execve") | ||
int tracepoint__syscalls__sys_exit_execve(struct trace_event_raw_sys_exit *tp) | ||
{ | ||
return exit_execve(tp->ret); | ||
} | ||
|
||
SEC("tp/syscalls/sys_execveat") | ||
int tracepoint__syscalls__sys_enter_execveat(struct trace_event_raw_sys_enter *tp) | ||
{ | ||
const char *filename = (const char *)tp->args[1]; | ||
const char *const *argv = (const char *const *)tp->args[2]; | ||
const char *const *envp = (const char *const *)tp->args[3]; | ||
|
||
return enter_execve(filename, argv, envp); | ||
} | ||
|
||
SEC("tp/syscalls/sys_exit_execveat") | ||
int tracepoint__syscalls__sys_exit_execveat(struct trace_event_raw_sys_exit *tp) | ||
{ | ||
return exit_execve(tp->ret); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* | ||
Copyright 2021 Gravitational, Inc. | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
#include "vmlinux.h" | ||
#include <bpf/bpf_helpers.h> /* most used helpers: SEC, __always_inline, etc */ | ||
#include <bpf/bpf_core_read.h> /* for BPF CO-RE helpers */ | ||
#include <bpf/bpf_tracing.h> /* for getting kprobe arguments */ | ||
|
||
#include "helpers.h" | ||
|
||
char LICENSE[] SEC("license") = "Dual BSD/GPL"; | ||
|
||
BPF_COUNTER(test_counter); | ||
|
||
SEC("tp/syscalls/sys_close") | ||
int tracepoint__syscalls__sys_enter_close(struct trace_event_raw_sys_enter *tp) | ||
{ | ||
int fd = (int)tp->args[0]; | ||
|
||
// Special bad FD we trigger upon | ||
if (fd == 1234) { | ||
INCR_COUNTER(test_counter); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
SEC("tp/syscalls/sys_exit_close") | ||
int tracepoint__syscalls__sys_exit_close(struct trace_event_raw_sys_exit *tp) | ||
{ | ||
return 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CO-RE requires Clang 10+ so that's what buildbox installs. Here we look for regular clang or clang-10. This is not ideal for folks building from source outside of buildbox. However it would require a more elaborate search to look for a working clang.
Same logic is also used below for
clang-format
andllvm-strip