|
2 | 2 | /* |
3 | 3 | * branch_tracker.bpf.c — BPF LSM program for branch process tracking. |
4 | 4 | * |
5 | | - * Hooks task_alloc, task_free, and task_kill to inescapably track all |
6 | | - * descendants of registered branch root PIDs. Supports atomic |
7 | | - * fork-denial during branch teardown and cross-branch signal isolation. |
| 5 | + * Hooks task_alloc, task_free, task_kill, and ptrace_access_check to |
| 6 | + * inescapably track all descendants of registered branch root PIDs. |
| 7 | + * Supports atomic fork-denial during branch teardown, cross-branch |
| 8 | + * signal isolation, and cross-branch ptrace denial. |
8 | 9 | * |
9 | 10 | * Compile: |
10 | 11 | * clang -g -O2 -target bpf -D__TARGET_ARCH_x86 \ |
@@ -99,6 +100,42 @@ int BPF_PROG(branch_task_kill, struct task_struct *target, |
99 | 100 | return 0; |
100 | 101 | } |
101 | 102 |
|
| 103 | +/* |
| 104 | + * LSM hook: ptrace_access_check — fires on ptrace attach/peek/poke. |
| 105 | + * |
| 106 | + * Same boundary logic as task_kill: a tracked process can only ptrace |
| 107 | + * processes within its own branch. Untracked processes (the parent |
| 108 | + * orchestrator) are unrestricted. |
| 109 | + * |
| 110 | + * Without this, a malicious child could ptrace the parent or a sibling |
| 111 | + * branch process and read/write its memory, bypassing all other |
| 112 | + * isolation. |
| 113 | + */ |
| 114 | +SEC("lsm/ptrace_access_check") |
| 115 | +int BPF_PROG(branch_ptrace_access_check, struct task_struct *child, |
| 116 | + unsigned int mode) |
| 117 | +{ |
| 118 | + __u32 tracer_pid = bpf_get_current_pid_tgid() >> 32; |
| 119 | + __u64 *tracer_bid = bpf_map_lookup_elem(&branch_pids, &tracer_pid); |
| 120 | + |
| 121 | + /* Untracked tracer (parent/orchestrator) — always allow. */ |
| 122 | + if (!tracer_bid) |
| 123 | + return 0; |
| 124 | + |
| 125 | + __u32 target_pid = BPF_CORE_READ(child, tgid); |
| 126 | + __u64 *target_bid = bpf_map_lookup_elem(&branch_pids, &target_pid); |
| 127 | + |
| 128 | + /* Target is not tracked — deny (protects parent). */ |
| 129 | + if (!target_bid) |
| 130 | + return -1; /* -EPERM */ |
| 131 | + |
| 132 | + /* Both tracked — allow only within the same branch. */ |
| 133 | + if (*tracer_bid != *target_bid) |
| 134 | + return -1; /* -EPERM */ |
| 135 | + |
| 136 | + return 0; |
| 137 | +} |
| 138 | + |
102 | 139 | /* |
103 | 140 | * LSM hook: task_free — fires when a process exits. |
104 | 141 | * |
|
0 commit comments