• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/procd/jail/seccomp-trace.c

  1 /*
  2  * seccomp syscall tracer for ujail
  3  *
  4  * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
  5  *
  6  * This program is free software; you can redistribute it and/or modify
  7  * it under the terms of the GNU Lesser General Public License version 2.1
  8  * as published by the Free Software Foundation
  9  *
 10  * This program is distributed in the hope that it will be useful,
 11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13  * GNU General Public License for more details.
 14  */
 15 #define _GNU_SOURCE
 16 
 17 #include <errno.h>
 18 #include <fcntl.h>
 19 #include <signal.h>
 20 #include <stdint.h>
 21 #include <stdio.h>
 22 #include <stdlib.h>
 23 #include <string.h>
 24 #include <unistd.h>
 25 #include <sys/ptrace.h>
 26 #include <sys/syscall.h>
 27 #include <sys/types.h>
 28 #include <sys/wait.h>
 29 
 30 #include <libubox/blob.h>
 31 #include <libubox/blobmsg.h>
 32 #include <libubox/blobmsg_json.h>
 33 #include <libubox/list.h>
 34 #include <libubox/utils.h>
 35 
 36 #include <udebug.h>
 37 
 38 #include "log.h"
 39 #include "seccomp-inject.h"
 40 #include "seccomp-trace.h"
 41 #include "../syscall-names.h"
 42 
 43 #ifndef __WALL
 44 #define __WALL          0x40000000
 45 #endif
 46 
 47 #ifndef PTRACE_EVENT_EXEC
 48 #define PTRACE_EVENT_EXEC       4
 49 #endif
 50 
 51 #ifndef PTRACE_EVENT_SECCOMP
 52 #define PTRACE_EVENT_SECCOMP    7
 53 #endif
 54 
 55 #define SECCOMP_TRACE_KILL_DATA 0xffffU
 56 
 57 enum trace_phase {
 58         PHASE_LINKER = 0,
 59         PHASE_INIT,
 60         PHASE_APP,
 61 };
 62 
 63 #define TRACE_NSYS      1024
 64 
 65 struct trace_proc {
 66         struct list_head list;
 67         pid_t pid;
 68         int phase;
 69         int in_syscall;
 70         int expect_main;
 71         int main_argidx;
 72         int errno_pending;
 73         int pending_errno;
 74         char comm[24];
 75         struct seccomp_bp bp_at_entry;
 76         struct seccomp_bp bp_lsm;
 77         struct seccomp_bp bp_main;
 78 };
 79 
 80 static LIST_HEAD(trace_procs);
 81 static int trace_nprocs;
 82 
 83 static struct udebug ud;
 84 static struct udebug_buf udb;
 85 static struct udebug_buf_meta ring_meta;
 86 static char ring_name[64];
 87 static int udebug_ready;
 88 
 89 static struct blob_buf event_b;
 90 static int trace_mode;
 91 static int trace_log_fd = -1;
 92 static int trace_dedup;
 93 static int trace_main_boundary;
 94 
 95 static uint32_t seen[3][TRACE_NSYS / 32];
 96 
 97 static const char *phase_name(int phase)
 98 {
 99         switch (phase) {
100         case PHASE_LINKER:
101                 return "linker";
102         case PHASE_INIT:
103                 return "init";
104         default:
105                 return "app";
106         }
107 }
108 
109 static int dedup_seen(int phase, long nr)
110 {
111         uint32_t bit;
112 
113         if (!trace_dedup)
114                 return 0;
115         if (nr < 0 || nr >= TRACE_NSYS || phase < 0 || phase > 2)
116                 return 0;
117 
118         bit = 1u << (nr & 31);
119         if (seen[phase][nr / 32] & bit)
120                 return 1;
121 
122         seen[phase][nr / 32] |= bit;
123         return 0;
124 }
125 
126 static void read_comm(pid_t pid, char *buf, size_t len)
127 {
128         char path[32];
129         ssize_t rd;
130         int fd;
131 
132         buf[0] = '\0';
133         snprintf(path, sizeof(path), "/proc/%d/comm", (int)pid);
134         fd = open(path, O_RDONLY);
135         if (fd < 0)
136                 return;
137 
138         rd = read(fd, buf, len - 1);
139         close(fd);
140         if (rd <= 0) {
141                 buf[0] = '\0';
142                 return;
143         }
144 
145         buf[rd] = '\0';
146         if (buf[rd - 1] == '\n')
147                 buf[rd - 1] = '\0';
148 }
149 
150 static void trace_emit(void)
151 {
152         char *json;
153 
154         if (udebug_ready) {
155                 udebug_entry_init(&udb);
156                 udebug_entry_append(&udb, blob_data(event_b.head),
157                                     blob_len(event_b.head));
158                 udebug_entry_add(&udb);
159         }
160 
161         if (trace_log_fd >= 0) {
162                 json = blobmsg_format_json(event_b.head, true);
163                 if (json) {
164                         dprintf(trace_log_fd, "%s\n", json);
165                         free(json);
166                 }
167         }
168 }
169 
170 static void emit_marker(struct trace_proc *p, const char *event)
171 {
172         blob_buf_init(&event_b, 0);
173         blobmsg_add_string(&event_b, "event", event);
174         blobmsg_add_u32(&event_b, "pid", p->pid);
175         if (p->comm[0])
176                 blobmsg_add_string(&event_b, "comm", p->comm);
177         trace_emit();
178 }
179 
180 static void emit_event(struct trace_proc *p, long nr, long *args,
181                        const char *action, int errnoval)
182 {
183         const char *name;
184         void *arr;
185         int i;
186 
187         blob_buf_init(&event_b, 0);
188         blobmsg_add_string(&event_b, "event", "syscall");
189         blobmsg_add_u32(&event_b, "pid", p->pid);
190         if (p->comm[0])
191                 blobmsg_add_string(&event_b, "comm", p->comm);
192         blobmsg_add_string(&event_b, "phase", phase_name(p->phase));
193         blobmsg_add_u32(&event_b, "nr", (uint32_t)nr);
194 
195         name = syscall_name((unsigned)nr);
196         if (name)
197                 blobmsg_add_string(&event_b, "syscall", name);
198 
199         arr = blobmsg_open_array(&event_b, "args");
200         for (i = 0; i < 6; i++)
201                 blobmsg_add_u64(&event_b, NULL, (uint64_t)(unsigned long)args[i]);
202         blobmsg_close_array(&event_b, arr);
203 
204         blobmsg_add_string(&event_b, "action", action);
205         if (errnoval >= 0)
206                 blobmsg_add_u32(&event_b, "errno", (uint32_t)errnoval);
207         trace_emit();
208 }
209 
210 static void emit_syscall(struct trace_proc *p)
211 {
212         long nr, args[6];
213 
214         if (seccomp_read_syscall(p->pid, &nr, args))
215                 return;
216         if (dedup_seen(p->phase, nr))
217                 return;
218 
219         emit_event(p, nr, args, "allow", -1);
220 }
221 
222 static struct trace_proc *proc_get(pid_t pid)
223 {
224         struct trace_proc *p;
225 
226         list_for_each_entry(p, &trace_procs, list)
227                 if (p->pid == pid)
228                         return p;
229 
230         return NULL;
231 }
232 
233 static struct trace_proc *proc_new(pid_t pid)
234 {
235         struct trace_proc *p;
236 
237         p = calloc(1, sizeof(*p));
238         if (!p)
239                 return NULL;
240 
241         p->pid = pid;
242         p->phase = PHASE_APP;
243         list_add_tail(&p->list, &trace_procs);
244         trace_nprocs++;
245 
246         return p;
247 }
248 
249 static void proc_del(struct trace_proc *p)
250 {
251         list_del(&p->list);
252         free(p);
253         trace_nprocs--;
254 }
255 
256 static void proc_arm_markers(struct trace_proc *p)
257 {
258         unsigned long at_entry, lsm;
259 
260         p->expect_main = 0;
261         p->main_argidx = 0;
262         p->bp_at_entry.armed = 0;
263         p->bp_lsm.armed = 0;
264         p->bp_main.armed = 0;
265 
266         if (seccomp_marker_addrs(p->pid, &at_entry, &lsm, &p->main_argidx))
267                 return;
268 
269         if (at_entry)
270                 seccomp_bp_arm(p->pid, at_entry, &p->bp_at_entry);
271 }
272 
273 static void proc_setopts(struct trace_proc *p)
274 {
275         unsigned long opt = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK |
276                             PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE |
277                             PTRACE_O_TRACEEXEC;
278 
279         if (trace_mode != SECCOMP_MODE_TRACE)
280                 opt |= PTRACE_O_TRACESECCOMP;
281 
282         ptrace(PTRACE_SETOPTIONS, p->pid, 0, (void *)opt);
283 }
284 
285 static void proc_start_root(struct trace_proc *p)
286 {
287         proc_setopts(p);
288         read_comm(p->pid, p->comm, sizeof(p->comm));
289         p->in_syscall = 0;
290 
291         if (trace_mode == SECCOMP_MODE_TRACE) {
292                 p->phase = PHASE_LINKER;
293                 proc_arm_markers(p);
294         } else {
295                 p->phase = PHASE_APP;
296         }
297 }
298 
299 static void proc_start_child(struct trace_proc *p)
300 {
301         proc_setopts(p);
302         read_comm(p->pid, p->comm, sizeof(p->comm));
303         p->phase = PHASE_APP;
304         p->in_syscall = 0;
305 }
306 
307 static void proc_exec(struct trace_proc *p)
308 {
309         read_comm(p->pid, p->comm, sizeof(p->comm));
310         p->in_syscall = 0;
311 
312         if (trace_mode == SECCOMP_MODE_TRACE) {
313                 p->phase = PHASE_LINKER;
314                 proc_arm_markers(p);
315         }
316 }
317 
318 static void handle_bp(struct trace_proc *p)
319 {
320         struct seccomp_bp *bps[3];
321         long nr, args[6];
322         unsigned long mainaddr, at_entry, lsm;
323         int hit;
324 
325         bps[0] = &p->bp_at_entry;
326         bps[1] = &p->bp_lsm;
327         bps[2] = &p->bp_main;
328 
329         hit = seccomp_bp_match(p->pid, bps, 3);
330         switch (hit) {
331         case 0:
332                 emit_marker(p, "at_entry");
333                 if (trace_main_boundary &&
334                     !seccomp_marker_addrs(p->pid, &at_entry, &lsm, &p->main_argidx) &&
335                     lsm && !seccomp_bp_arm(p->pid, lsm, &p->bp_lsm))
336                         p->expect_main = 1;
337                 p->phase = p->expect_main ? PHASE_INIT : PHASE_APP;
338                 break;
339         case 1:
340                 if (seccomp_read_syscall(p->pid, &nr, args))
341                         break;
342                 mainaddr = (unsigned long)args[p->main_argidx];
343                 if (mainaddr)
344                         seccomp_bp_arm(p->pid, mainaddr, &p->bp_main);
345                 break;
346         case 2:
347                 emit_marker(p, "main");
348                 p->phase = PHASE_APP;
349                 break;
350         default:
351                 break;
352         }
353 }
354 
355 enum seccomp_resume {
356         SECCOMP_RESUME_NORMAL = 0,
357         SECCOMP_RESUME_STEP_EXIT,
358 };
359 
360 static int handle_seccomp(struct trace_proc *p)
361 {
362         long nr, args[6];
363         unsigned long data = 0;
364         int err, rc;
365 
366         ptrace(PTRACE_GETEVENTMSG, p->pid, 0, &data);
367         if (seccomp_read_syscall(p->pid, &nr, args))
368                 return SECCOMP_RESUME_NORMAL;
369 
370         if (data == SECCOMP_TRACE_KILL_DATA) {
371                 emit_event(p, nr, args, "kill", -1);
372                 if (trace_mode == SECCOMP_MODE_AUDIT)
373                         kill(p->pid, SIGKILL);
374                 return SECCOMP_RESUME_NORMAL;
375         }
376 
377         err = (int)data;
378         emit_event(p, nr, args, "deny", err);
379 
380         if (trace_mode != SECCOMP_MODE_AUDIT)
381                 return SECCOMP_RESUME_NORMAL;
382 
383         rc = seccomp_force_errno(p->pid, err);
384         if (rc < 0) {
385                 kill(p->pid, SIGKILL);
386                 return SECCOMP_RESUME_NORMAL;
387         }
388         if (rc == 0)
389                 return SECCOMP_RESUME_NORMAL;
390 
391         p->errno_pending = 1;
392         p->pending_errno = err;
393 
394         return SECCOMP_RESUME_STEP_EXIT;
395 }
396 
397 static void trace_resume(pid_t pid, int sig)
398 {
399         if (trace_mode == SECCOMP_MODE_TRACE)
400                 ptrace(PTRACE_SYSCALL, pid, 0, (void *)(long)sig);
401         else
402                 ptrace(PTRACE_CONT, pid, 0, (void *)(long)sig);
403 }
404 
405 static int udebug_setup(const char *name)
406 {
407         snprintf(ring_name, sizeof(ring_name), "ujail:%s", name ? name : "trace");
408         ring_meta.name = ring_name;
409         ring_meta.format = UDEBUG_FORMAT_BLOBMSG;
410 
411         udebug_init(&ud);
412         udebug_auto_connect(&ud, NULL);
413         if (udebug_buf_init(&udb, 1024, 256 * 1024))
414                 return -1;
415         if (udebug_buf_add(&ud, &udb, &ring_meta))
416                 return -1;
417 
418         return 0;
419 }
420 
421 static void udebug_teardown(void)
422 {
423         if (!udebug_ready)
424                 return;
425 
426         udebug_buf_free(&udb);
427         udebug_free(&ud);
428         udebug_ready = 0;
429 }
430 
431 int seccomp_trace_run(pid_t pid, const struct seccomp_trace_opts *o)
432 {
433         struct trace_proc *p, *root;
434         int status;
435         pid_t wpid;
436         int sig, ev;
437 
438         trace_mode = o->mode;
439         trace_log_fd = o->log_fd;
440         trace_dedup = o->dedup;
441         trace_main_boundary = o->main_boundary;
442         memset(seen, 0, sizeof(seen));
443 
444         if (!udebug_setup(o->name))
445                 udebug_ready = 1;
446 
447         root = proc_new(pid);
448         if (!root) {
449                 udebug_teardown();
450                 return -1;
451         }
452 
453         proc_start_root(root);
454         trace_resume(pid, 0);
455 
456         while (trace_nprocs > 0) {
457                 wpid = waitpid(-1, &status, __WALL);
458                 if (wpid < 0) {
459                         if (errno == EINTR)
460                                 continue;
461                         break;
462                 }
463 
464                 p = proc_get(wpid);
465                 if (!p) {
466                         p = proc_new(wpid);
467                         if (!p)
468                                 continue;
469                         proc_start_child(p);
470                         trace_resume(wpid, 0);
471                         continue;
472                 }
473 
474                 if (WIFEXITED(status) || WIFSIGNALED(status)) {
475                         proc_del(p);
476                         continue;
477                 }
478 
479                 if (!WIFSTOPPED(status)) {
480                         trace_resume(wpid, 0);
481                         continue;
482                 }
483 
484                 sig = WSTOPSIG(status);
485                 ev = (status >> 16) & 0xff;
486 
487                 if (ev) {
488                         if (ev == PTRACE_EVENT_SECCOMP) {
489                                 if (handle_seccomp(p) == SECCOMP_RESUME_STEP_EXIT) {
490                                         ptrace(PTRACE_SYSCALL, wpid, 0, 0);
491                                         continue;
492                                 }
493                         } else if (ev == PTRACE_EVENT_EXEC) {
494                                 proc_exec(p);
495                         }
496                         trace_resume(wpid, 0);
497                         continue;
498                 }
499 
500                 if (sig == (SIGTRAP | 0x80)) {
501                         if (p->errno_pending) {
502                                 if (seccomp_force_errno_exit(wpid, p->pending_errno))
503                                         kill(wpid, SIGKILL);
504                                 p->errno_pending = 0;
505                                 trace_resume(wpid, 0);
506                                 continue;
507                         }
508                         if (!p->in_syscall)
509                                 emit_syscall(p);
510                         p->in_syscall = !p->in_syscall;
511                         trace_resume(wpid, 0);
512                         continue;
513                 }
514 
515                 if (sig == SIGTRAP) {
516                         handle_bp(p);
517                         trace_resume(wpid, 0);
518                         continue;
519                 }
520 
521                 trace_resume(wpid, (int)sig);
522         }
523 
524         while (!list_empty(&trace_procs)) {
525                 p = list_first_entry(&trace_procs, struct trace_proc, list);
526                 proc_del(p);
527         }
528 
529         udebug_teardown();
530 
531         return 0;
532 }
533 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt