Last active
March 2, 2020 03:40
-
-
Save ancat/919ab9831db9a36589e5a50afa4bbef8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Question: how hook into the creation of new processes /and/ threads? | |
Attempt #1: Attach a kprobe to the `execve` syscall | |
- This works, but will not catch threads created via clone. | |
Attempt #2: Add an additional kretprobe to the `clone` syscall | |
- On success, clone returns the newly created thread id. This works fine | |
until you start dealing with pid namespaces. If clone is called from a | |
different pid namespace, the return value will only be valid for that | |
namespace, making it useless outside (eg can't use that as a pid to | |
query for more information, for logging, etc) | |
Attempt #3: Attach a kprobe to the `sched_fork` function. | |
- This function is used internally by both clone() and fork(), so at | |
first glance this should catch both code paths. On top of this, the | |
task_struct is made available as the second parameter, so getting the | |
pid/tgid should be straightforward, right? This turned out not to be | |
the case. | |
the function prototype: | |
int sched_fork(unsigned long clone_flags, struct task_struct *p) | |
For some reason, p->pid and p->tgid were zero every time. I thought | |
perhaps sched_fork will set these values internally, but attaching | |
a kretprobe proved this to be false as well. I think this might be a | |
PEBKAC type situation, so if you know something I could be doing | |
wrong, please let me know. | |
Attempt #4: Attach a tracepoint to sched:sched_process_fork. | |
- This worked! I couldn't find many examples on how to properly | |
interact with tracepoints so I had to turn to the kernel headers that | |
came with my system. | |
1. Prototype with bpftrace: | |
sudo bpftrace -e \ | |
'tracepoint:sched:sched_process_fork { printf("%s %ld\n", args->child_comm, args->child_pid) }' | |
2. skim /usr/src/linux-headers-<kernel-version>/include/trace/events/sched.h | |
1. cat /sys/kernel/debug/tracing/events/sched/sched_process_fork/format | |
2. turn this into a struct | |
3. ??? | |
4. profit! (it's passed as the first arg to the tracepoint handler) | |
I don't really trust this method since anything requiring me to write | |
a struct out manually is probably not going to be super portable. I | |
also haven't used tracepoints much ever, so I don't really know what I'm | |
doing. | |
Sample code below... | |
*/ | |
struct sched_process_fork { | |
unsigned short common_type; | |
unsigned char common_flags; | |
unsigned char common_preempt_count; | |
int common_pid; | |
char parent_comm[16]; | |
u32 parent_pid; | |
char child_comm[16]; | |
u32 child_pid; | |
}; | |
int sched_process_fork(struct sched_process_fork *ctx) { | |
bpf_trace_printk("pid = %ld\n", ctx->child_pid); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment