Merge branch 'main' into fix/review-hardening

This commit is contained in:
Kurt Himebauch authored and GitHub committed 2026-09-27 12:27:22 -04:00
commit 6b5bfddf2f
7 files changed
+782

No files matched your search

+25
View File
@@ -19,3 +19,28 @@ Ported from WinNative (`feature/proot-enhancements`, e7af0f24):
workaround is active.
- `0003-clone3-flags-read-guard.patch` - a thread created with `clone3` keeps the flags it
inherited when `struct clone_args` cannot be read, instead of being tracked as a fork.
Ported from WinNative (`main`, 53836ca9, "Fix/performance and vac"):
- `0004-seccomp-filter-by-argument.patch` - the seccomp filter traces `prctl` only for
`PR_SET_DUMPABLE`, `setrlimit` only for `RLIMIT_STACK` and `prlimit64` only when it sets a new
`RLIMIT_STACK`, the only cases proot acts on; every other call runs without a stop. `uname` is
traced by the core only on x86_64, the one arch it rewrites; kompat still adds it for
`--kernel-release`.
- `0005-seccomp-skip-unneeded-sysexit.patch` - a seccomp stop fetches the registers once and
looks the filter flags up by syscall number, instead of a `PTRACE_GETEVENTMSG` and a second
register fetch. The flags table is built from proot's list merged with the enabled extensions'
(kompat and fake_id0 here), so it is exactly what the filter reports. `wait4`/`waitpid` left to
the kernel and `accept`/`accept4` without a sockaddr skip their exit stop, unless an extension
replaced the syscall (kompat turning `accept4` into `accept`).
- `0006-clone3-exit-signal.patch` - `clone3` flags and exit signal are read as the 64-bit fields
of `struct clone_args`, so a `clone3` fork is told apart from a thread when proot decides how the
child is traced.
- `0007-proc-self-thread-group.patch` - tracees track their thread group, `/proc/self` names it
instead of the calling thread, and `/proc/thread-self` resolves to `/proc/<tgid>/task/<tid>`.
- `0008-fchmodat2-openat2.patch` - `fchmodat2` paths are translated (honouring
`AT_SYMLINK_NOFOLLOW`), `openat2` answers `ENOSYS` so callers fall back to the translated
`openat`, and syscall numbers past the end of a table are rejected instead of read.
- `0009-tracee-relatives-sweep.patch` - tracees count their children, so a terminating thread
with no children or ptracees no longer walks every tracee; the per-stop memory collector is
emptied instead of freed and reallocated.
@@ -0,0 +1,209 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: maxjivi05 <maxjividen@gmail.com>
Date: Sun, 27 Sep 2026 07:56:54 -0400
Subject: [PATCH] seccomp: trace prctl, setrlimit and prlimit64 only for the
arguments PRoot acts on
---
src/syscall/seccomp.c | 106 +++++++++++++++++++++++++++++++++++-------
1 file changed, 89 insertions(+), 17 deletions(-)
diff --git a/src/syscall/seccomp.c b/src/syscall/seccomp.c
index 6d1ba47..8557d4a 100644
--- a/src/syscall/seccomp.c
+++ b/src/syscall/seccomp.c
@@ -37,6 +37,7 @@
#include <string.h> /* memcpy(3), */
#include <stddef.h> /* offsetof(3), */
#include <stdint.h> /* uint*_t, UINT*_MAX, */
+#include <sys/resource.h>
#include <assert.h> /* assert(3), */
#include "syscall/seccomp.h"
@@ -121,13 +122,86 @@ static int add_trace_syscall(struct sock_fprog *program, word_t syscall, int fla
return 0;
}
+static int add_trace_syscall_if_arg(struct sock_fprog *program, word_t syscall,
+ size_t arg, uint32_t value, int flag)
+{
+ const size_t arg_offset = offsetof(struct seccomp_data, args[arg]);
+
+ #define LENGTH_TRACE_SYSCALL_IF_ARG 5
+ struct sock_filter statements[LENGTH_TRACE_SYSCALL_IF_ARG] = {
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, syscall, 0, 4),
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, arg_offset),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, value, 0, 1),
+ BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE + flag),
+ BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)
+ };
+
+ DEBUG_FILTER("FILTER: trace if syscall == %ld and arg %zd == %u\n",
+ syscall, arg, value);
+
+ return add_statements(program, LENGTH_TRACE_SYSCALL_IF_ARG, statements);
+}
+
+static int add_trace_prlimit_stack(struct sock_fprog *program, word_t syscall, int flag)
+{
+ const size_t resource_offset = offsetof(struct seccomp_data, args[1]);
+ const size_t limit_offset = offsetof(struct seccomp_data, args[2]);
+
+ #define LENGTH_TRACE_PRLIMIT_STACK 9
+ struct sock_filter statements[LENGTH_TRACE_PRLIMIT_STACK] = {
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, syscall, 0, 8),
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, resource_offset),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, RLIMIT_STACK, 0, 5),
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, limit_offset),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 2),
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, limit_offset + sizeof(uint32_t)),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0),
+ BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE + flag),
+ BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)
+ };
+
+ DEBUG_FILTER("FILTER: trace if syscall == %ld sets RLIMIT_STACK\n", syscall);
+
+ return add_statements(program, LENGTH_TRACE_PRLIMIT_STACK, statements);
+}
+
+static size_t trace_length(Sysnum sysnum)
+{
+ switch (sysnum) {
+ case PR_prctl:
+ case PR_setrlimit:
+ return LENGTH_TRACE_SYSCALL_IF_ARG;
+ case PR_prlimit64:
+ return LENGTH_TRACE_PRLIMIT_STACK;
+ default:
+ return LENGTH_TRACE_SYSCALL;
+ }
+}
+
+static int add_trace(struct sock_fprog *program, Sysnum sysnum, word_t syscall, int flag)
+{
+ if (syscall > UINT32_MAX)
+ return -ERANGE;
+
+ switch (sysnum) {
+ case PR_prctl:
+ return add_trace_syscall_if_arg(program, syscall, 0, PR_SET_DUMPABLE, flag);
+ case PR_setrlimit:
+ return add_trace_syscall_if_arg(program, syscall, 0, RLIMIT_STACK, flag);
+ case PR_prlimit64:
+ return add_trace_prlimit_stack(program, syscall, flag);
+ default:
+ return add_trace_syscall(program, syscall, flag);
+ }
+}
+
/**
* Append to @program->filter the statements that allow anything (if
- * unfiltered). Note that @nb_traced_syscalls is used to make a
- * sanity check. This function returns -errno if an error occurred,
+ * unfiltered). Note that @section_length is used to make a sanity
+ * check. This function returns -errno if an error occurred,
* otherwise 0.
*/
-static int end_arch_section(struct sock_fprog *program, size_t nb_traced_syscalls)
+static int end_arch_section(struct sock_fprog *program, size_t section_length)
{
int status;
@@ -143,8 +217,7 @@ static int end_arch_section(struct sock_fprog *program, size_t nb_traced_syscall
return status;
/* Sanity check, see start_arch_section(). */
- if ( talloc_array_length(program->filter) - program->len
- != LENGTH_END_SECTION + nb_traced_syscalls * LENGTH_TRACE_SYSCALL)
+ if (talloc_array_length(program->filter) - program->len != section_length)
return -ERANGE;
return 0;
@@ -152,16 +225,14 @@ static int end_arch_section(struct sock_fprog *program, size_t nb_traced_syscall
/**
* Append to @program->filter the statements that check the current
- * @architecture. Note that @nb_traced_syscalls is used to make a
+ * @architecture. Note that @section_length is used to make a
* sanity check. This function returns -errno if an error occurred,
* otherwise 0.
*/
-static int start_arch_section(struct sock_fprog *program, uint32_t arch, size_t nb_traced_syscalls)
+static int start_arch_section(struct sock_fprog *program, uint32_t arch, size_t section_length)
{
const size_t arch_offset = offsetof(struct seccomp_data, arch);
const size_t syscall_offset = offsetof(struct seccomp_data, nr);
- const size_t section_length = LENGTH_END_SECTION +
- nb_traced_syscalls * LENGTH_TRACE_SYSCALL;
int status;
/* Sanity checks. */
@@ -191,7 +262,7 @@ static int start_arch_section(struct sock_fprog *program, uint32_t arch, size_t
};
DEBUG_FILTER("FILTER: if arch == %ld, up to %zdth statement\n",
- arch, nb_traced_syscalls);
+ arch, section_length);
status = add_statements(program, LENGTH_START_SECTION, statements);
if (status < 0)
@@ -256,7 +327,7 @@ static int set_seccomp_filters(const FilteredSysnum *sysnums)
size_t nb_archs = sizeof(seccomp_archs) / sizeof(SeccompArch);
struct sock_fprog program = { .len = 0, .filter = NULL };
- size_t nb_traced_syscalls;
+ size_t section_length;
size_t i, j, k;
int status;
@@ -268,19 +339,18 @@ static int set_seccomp_filters(const FilteredSysnum *sysnums)
for (i = 0; i < nb_archs; i++) {
word_t syscall;
- nb_traced_syscalls = 0;
+ section_length = LENGTH_END_SECTION;
- /* Pre-compute the number of traced syscalls for this architecture. */
for (j = 0; j < seccomp_archs[i].nb_abis; j++) {
for (k = 0; sysnums[k].value != PR_void; k++) {
syscall = detranslate_sysnum(seccomp_archs[i].abis[j], sysnums[k].value);
if (syscall != SYSCALL_AVOIDER)
- nb_traced_syscalls++;
+ section_length += trace_length(sysnums[k].value);
}
}
/* Filter: if handled architecture */
- status = start_arch_section(&program, seccomp_archs[i].value, nb_traced_syscalls);
+ status = start_arch_section(&program, seccomp_archs[i].value, section_length);
if (status < 0)
goto end;
@@ -292,14 +362,14 @@ static int set_seccomp_filters(const FilteredSysnum *sysnums)
continue;
/* Filter: trace if handled syscall */
- status = add_trace_syscall(&program, syscall, sysnums[k].flags);
+ status = add_trace(&program, sysnums[k].value, syscall, sysnums[k].flags);
if (status < 0)
goto end;
}
}
/* Filter: allow untraced syscalls for this architecture */
- status = end_arch_section(&program, nb_traced_syscalls);
+ status = end_arch_section(&program, section_length);
if (status < 0)
goto end;
}
@@ -411,7 +481,9 @@ static FilteredSysnum proot_sysnums[] = {
{ PR_truncate64, 0 },
{ PR_umount, 0 },
{ PR_umount2, 0 },
+#if defined(ARCH_X86_64)
{ PR_uname, FILTER_SYSEXIT },
+#endif
{ PR_unlink, 0 },
{ PR_unlinkat, 0 },
{ PR_uselib, 0 },
@@ -0,0 +1,242 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: maxjivi05 <maxjividen@gmail.com>
Date: Sun, 27 Sep 2026 07:57:46 -0400
Subject: [PATCH] seccomp: answer a stop's flags from its registers and skip
the sysexit stop when nothing is left for it
---
src/syscall/enter.c | 3 +++
src/syscall/seccomp.c | 51 +++++++++++++++++++++++++++++++++++++------
src/syscall/seccomp.h | 1 +
src/syscall/syscall.c | 17 ++++++++++++---
src/tracee/event.c | 14 +++++++-----
src/tracee/tracee.h | 2 ++
6 files changed, 73 insertions(+), 15 deletions(-)
diff --git a/src/syscall/enter.c b/src/syscall/enter.c
index 8c3eb2e..b84fdc3 100644
--- a/src/syscall/enter.c
+++ b/src/syscall/enter.c
@@ -149,6 +149,8 @@ int translate_syscall_enter(Tracee *tracee)
case PR_wait4:
case PR_waitpid:
status = translate_wait_enter(tracee);
+ tracee->sysexit_unneeded = status >= 0
+ && tracee->as_ptracer.waits_in == WAITS_IN_KERNEL;
break;
case PR_brk:
@@ -271,6 +273,7 @@ int translate_syscall_enter(Tracee *tracee)
case PR_accept4:
/* Nothing special to do if no sockaddr was specified. */
if (peek_reg(tracee, ORIGINAL, SYSARG_2) == 0) {
+ tracee->sysexit_unneeded = true;
status = 0;
break;
}
diff --git a/src/syscall/seccomp.c b/src/syscall/seccomp.c
index 8557d4a..cec4330 100644
--- a/src/syscall/seccomp.c
+++ b/src/syscall/seccomp.c
@@ -37,6 +37,7 @@
#include <string.h> /* memcpy(3), */
#include <stddef.h> /* offsetof(3), */
#include <stdint.h> /* uint*_t, UINT*_MAX, */
+#include <stdbool.h>
#include <sys/resource.h>
#include <assert.h> /* assert(3), */
@@ -542,6 +543,9 @@ static int merge_filtered_sysnums(TALLOC_CTX *context, FilteredSysnum **sysnums,
return 0;
}
+static int collect_filtered_sysnums(TALLOC_CTX *context, const Tracee *tracee,
+ FilteredSysnum **filtered_sysnums);
+
/**
* Tell the kernel to trace only syscalls handled by PRoot and its
* extensions. This filter will be enabled for the given @tracee and
@@ -551,14 +555,30 @@ static int merge_filtered_sysnums(TALLOC_CTX *context, FilteredSysnum **sysnums,
int enable_syscall_filtering(const Tracee *tracee)
{
FilteredSysnum *filtered_sysnums = NULL;
- Extension *extension;
int status;
assert(tracee != NULL && tracee->ctx != NULL);
+ status = collect_filtered_sysnums(tracee->ctx, tracee, &filtered_sysnums);
+ if (status < 0)
+ return status;
+
+ status = set_seccomp_filters(filtered_sysnums);
+ if (status < 0)
+ return status;
+
+ return 0;
+}
+
+static int collect_filtered_sysnums(TALLOC_CTX *context, const Tracee *tracee,
+ FilteredSysnum **filtered_sysnums)
+{
+ Extension *extension;
+ int status;
+
/* Add the sysnums required by PRoot to the list of filtered
* sysnums. TODO: only if path translation is required. */
- status = merge_filtered_sysnums(tracee->ctx, &filtered_sysnums, proot_sysnums);
+ status = merge_filtered_sysnums(context, filtered_sysnums, proot_sysnums);
if (status < 0)
return status;
@@ -569,20 +589,37 @@ int enable_syscall_filtering(const Tracee *tracee)
if (extension->filtered_sysnums == NULL)
continue;
- status = merge_filtered_sysnums(tracee->ctx, &filtered_sysnums,
+ status = merge_filtered_sysnums(context, filtered_sysnums,
extension->filtered_sysnums);
if (status < 0)
return status;
}
}
- status = set_seccomp_filters(filtered_sysnums);
- if (status < 0)
- return status;
-
return 0;
}
+word_t filtered_sysnum_flags(const Tracee *tracee, Sysnum sysnum)
+{
+ static word_t flags[PR_NB_SYSNUM];
+ static bool initialized = false;
+ FilteredSysnum *filtered_sysnums = NULL;
+ size_t i;
+
+ if (!initialized) {
+ if (collect_filtered_sysnums(NULL, tracee, &filtered_sysnums) < 0) {
+ TALLOC_FREE(filtered_sysnums);
+ return FILTER_SYSEXIT;
+ }
+ for (i = 0; filtered_sysnums[i].value != PR_void; i++)
+ flags[filtered_sysnums[i].value] |= filtered_sysnums[i].flags;
+ TALLOC_FREE(filtered_sysnums);
+ initialized = true;
+ }
+
+ return sysnum < PR_NB_SYSNUM ? flags[sysnum] : FILTER_SYSEXIT;
+}
+
#else
#include "tracee/tracee.h"
diff --git a/src/syscall/seccomp.h b/src/syscall/seccomp.h
index 3d5ba40..67053b5 100644
--- a/src/syscall/seccomp.h
+++ b/src/syscall/seccomp.h
@@ -44,5 +44,6 @@ typedef struct {
#define FILTER_SYSEXIT 0x1
extern int enable_syscall_filtering(const Tracee *tracee);
+extern word_t filtered_sysnum_flags(const Tracee *tracee, Sysnum sysnum);
#endif /* SECCOMP_H */
diff --git a/src/syscall/syscall.c b/src/syscall/syscall.c
index 0761a26..9ab063c 100644
--- a/src/syscall/syscall.c
+++ b/src/syscall/syscall.c
@@ -107,9 +107,13 @@ void translate_syscall(Tracee *tracee)
assert(tracee->exe != NULL);
- status = fetch_regs(tracee);
- if (status < 0)
- return;
+ if (tracee->regs_fresh)
+ tracee->regs_fresh = false;
+ else {
+ status = fetch_regs(tracee);
+ if (status < 0)
+ return;
+ }
int suppressed_syscall_status = 0;
@@ -136,9 +140,16 @@ void translate_syscall(Tracee *tracee)
* requested by the tracee, it is not a syscall
* chained by PRoot. */
if (tracee->chain.syscalls == NULL) {
+ tracee->sysexit_unneeded = false;
save_current_regs(tracee, ORIGINAL);
status = translate_syscall_enter(tracee);
save_current_regs(tracee, MODIFIED);
+
+ if (status >= 0 && tracee->sysexit_unneeded
+ && tracee->seccomp == ENABLED && !tracee->sysexit_pending
+ && !tracee->restore_original_regs_after_seccomp_event
+ && get_sysnum(tracee, MODIFIED) == get_sysnum(tracee, ORIGINAL))
+ tracee->restart_how = PTRACE_CONT;
}
else {
if (tracee->chain.sysnum_workaround_state != SYSNUM_WORKAROUND_PROCESS_REPLACED_CALL) {
diff --git a/src/tracee/event.c b/src/tracee/event.c
index ec79ef3..41afacd 100644
--- a/src/tracee/event.c
+++ b/src/tracee/event.c
@@ -38,6 +38,7 @@
#include "tracee/event.h"
#include "tracee/seccomp.h"
#include "tracee/mem.h"
+#include "tracee/reg.h"
#include "cli/note.h"
#include "path/path.h"
#include "path/binding.h"
@@ -576,18 +577,20 @@ int handle_tracee_event(Tracee *tracee, int tracee_status)
if (tracee->seccomp != ENABLED)
break;
- status = ptrace(PTRACE_GETEVENTMSG, tracee->pid, NULL, &flags);
+ status = fetch_regs(tracee);
if (status < 0)
break;
+ tracee->regs_fresh = true;
+ flags = filtered_sysnum_flags(tracee, get_sysnum(tracee, CURRENT));
/* Use the common ptrace flow when
* sysexit has to be handled. */
if ((flags & FILTER_SYSEXIT) != 0 || sysexit_necessary) {
- if (seccomp_after_ptrace_enter) {
- tracee->restart_how = PTRACE_SYSCALL;
- translate_syscall(tracee);
- }
tracee->restart_how = PTRACE_SYSCALL;
+ if (seccomp_after_ptrace_enter)
+ translate_syscall(tracee);
+ else
+ tracee->regs_fresh = false;
break;
}
@@ -720,6 +723,7 @@ bool restart_tracee(Tracee *tracee, int signal)
tracee->last_restart_how = tracee->restart_how;
tracee->restart_how = 0;
+ tracee->regs_fresh = false;
tracee->running = true;
return true;
diff --git a/src/tracee/tracee.h b/src/tracee/tracee.h
index e4be3ff..c444a50 100644
--- a/src/tracee/tracee.h
+++ b/src/tracee/tracee.h
@@ -161,6 +161,8 @@ typedef struct tracee {
bool _regs_were_changed;
bool restore_original_regs;
bool restore_original_regs_after_seccomp_event;
+ bool regs_fresh;
+ bool sysexit_unneeded;
/* State for the special handling of SIGSTOP. */
enum {
@@ -0,0 +1,31 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: maxjivi05 <maxjividen@gmail.com>
Date: Sun, 27 Sep 2026 07:58:07 -0400
Subject: [PATCH] tracee: take a clone3 child's exit signal from struct
clone_args
---
src/tracee/tracee.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/src/tracee/tracee.c b/src/tracee/tracee.c
index 7c0c50c..25eeb57 100644
--- a/src/tracee/tracee.c
+++ b/src/tracee/tracee.c
@@ -424,9 +424,13 @@ int new_child(Tracee *parent, word_t clone_flags)
if (status >= 0 && get_sysnum(parent, CURRENT) == PR_clone)
clone_flags = peek_reg(parent, CURRENT, SYSARG_1);
else if (status >= 0 && get_sysnum(parent, CURRENT) == PR_clone3) {
- word_t flags = peek_word(parent, peek_reg(parent, CURRENT, SYSARG_1));
- if (errno == 0)
- clone_flags = flags;
+ word_t args = peek_reg(parent, CURRENT, SYSARG_1);
+ word_t flags = peek_uint64(parent, args);
+ if (errno == 0) {
+ word_t exit_signal = peek_uint64(parent, args + 4 * sizeof(uint64_t));
+ if (errno == 0)
+ clone_flags = (flags & ~(word_t) 0xFF) | (exit_signal & 0xFF);
+ }
}
/* Get the pid of the parent's new child. */
@@ -0,0 +1,72 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: maxjivi05 <maxjividen@gmail.com>
Date: Sun, 27 Sep 2026 07:58:16 -0400
Subject: [PATCH] proc: name a thread's /proc/self by its thread group and
resolve /proc/thread-self
---
src/path/proc.c | 7 +++++--
src/tracee/tracee.c | 3 +++
src/tracee/tracee.h | 1 +
3 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/src/path/proc.c b/src/path/proc.c
index 1c816df..62650f6 100644
--- a/src/path/proc.c
+++ b/src/path/proc.c
@@ -58,10 +58,13 @@ Action readlink_proc(const Tracee *tracee, char result[PATH_MAX],
switch (comparison) {
case PATHS_ARE_EQUAL:
/* Substitute "/proc/self" with "/proc/<PID>". */
- if (strcmp(component, "self") != 0)
+ if (strcmp(component, "self") == 0)
+ status = snprintf(result, PATH_MAX, "/proc/%d", tracee->tgid);
+ else if (strcmp(component, "thread-self") == 0)
+ status = snprintf(result, PATH_MAX, "/proc/%d/task/%d", tracee->tgid, tracee->pid);
+ else
return DEFAULT;
- status = snprintf(result, PATH_MAX, "/proc/%d", tracee->pid);
if (status < 0 || status >= PATH_MAX)
return -EPERM;
diff --git a/src/tracee/tracee.c b/src/tracee/tracee.c
index 25eeb57..d6e414d 100644
--- a/src/tracee/tracee.c
+++ b/src/tracee/tracee.c
@@ -234,6 +234,7 @@ static Tracee *new_tracee(pid_t pid)
talloc_set_destructor(tracee, remove_tracee);
tracee->pid = pid;
+ tracee->tgid = pid;
tracee->vpid = next_vpid++;
LIST_INSERT_HEAD(&tracees, tracee, link);
@@ -355,6 +356,7 @@ Tracee *get_tracee(const Tracee *current_tracee, pid_t pid, bool create)
void set_tracee_pid(Tracee *tracee, pid_t pid)
{
tracee->pid = pid;
+ tracee->tgid = pid;
LIST_REMOVE(tracee, bucket_link);
LIST_INSERT_HEAD(bucket_of(pid), tracee, bucket_link);
}
@@ -511,6 +513,7 @@ int new_child(Tracee *parent, word_t clone_flags)
* specificity (ie. when a thread calls execve(2), its pid
* gets replaced by the pid of its thread group leader). */
child->clone = ((clone_flags & CLONE_THREAD) != 0);
+ child->tgid = child->clone ? parent->tgid : child->pid;
/* Depending on how the new process is created, it may be
* automatically traced by the parent's tracer. */
diff --git a/src/tracee/tracee.h b/src/tracee/tracee.h
index c444a50..527f428 100644
--- a/src/tracee/tracee.h
+++ b/src/tracee/tracee.h
@@ -83,6 +83,7 @@ typedef struct tracee {
/* Process identifier. */
pid_t pid;
+ pid_t tgid;
/* Unique tracee identifier. */
uint64_t vpid;
@@ -0,0 +1,120 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: maxjivi05 <maxjividen@gmail.com>
Date: Sun, 27 Sep 2026 07:58:30 -0400
Subject: [PATCH] syscall: translate fchmodat2, send openat2 callers to openat,
bound sysnum lookups by the table length
---
src/syscall/enter.c | 18 ++++++++++++++++++
src/syscall/seccomp.c | 2 ++
src/syscall/sysnum.c | 2 +-
src/syscall/sysnums-arm.h | 2 ++
src/syscall/sysnums-arm64.h | 2 ++
src/syscall/sysnums.list | 2 ++
6 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/src/syscall/enter.c b/src/syscall/enter.c
index b84fdc3..1d3b3fd 100644
--- a/src/syscall/enter.c
+++ b/src/syscall/enter.c
@@ -447,6 +447,24 @@ int translate_syscall_enter(Tracee *tracee)
status = translate_path2(tracee, dirfd, path, SYSARG_2, REGULAR);
break;
+ case PR_fchmodat2:
+ dirfd = peek_reg(tracee, CURRENT, SYSARG_1);
+ flags = peek_reg(tracee, CURRENT, SYSARG_4);
+
+ status = get_sysarg_path(tracee, path, SYSARG_2);
+ if (status < 0)
+ break;
+
+ if ((flags & AT_SYMLINK_NOFOLLOW) != 0)
+ status = translate_path2(tracee, dirfd, path, SYSARG_2, SYMLINK);
+ else
+ status = translate_path2(tracee, dirfd, path, SYSARG_2, REGULAR);
+ break;
+
+ case PR_openat2:
+ status = -ENOSYS;
+ break;
+
case PR_inotify_add_watch:
flags = peek_reg(tracee, CURRENT, SYSARG_3);
diff --git a/src/syscall/seccomp.c b/src/syscall/seccomp.c
index cec4330..0b381bb 100644
--- a/src/syscall/seccomp.c
+++ b/src/syscall/seccomp.c
@@ -419,6 +419,7 @@ static FilteredSysnum proot_sysnums[] = {
{ PR_faccessat2, FILTER_SYSEXIT },
{ PR_fchdir, FILTER_SYSEXIT },
{ PR_fchmodat, 0 },
+ { PR_fchmodat2, 0 },
{ PR_fchownat, 0 },
{ PR_fstatat64, 0 },
{ PR_futimesat, 0 },
@@ -455,6 +456,7 @@ static FilteredSysnum proot_sysnums[] = {
{ PR_oldstat, 0 },
{ PR_open, 0 },
{ PR_openat, 0 },
+ { PR_openat2, 0 },
{ PR_pivot_root, 0 },
{ PR_prctl, 0 },
{ PR_prlimit64, FILTER_SYSEXIT },
diff --git a/src/syscall/sysnum.c b/src/syscall/sysnum.c
index b5440a6..785593e 100644
--- a/src/syscall/sysnum.c
+++ b/src/syscall/sysnum.c
@@ -92,7 +92,7 @@ static Sysnum translate_sysnum(Abi abi, word_t sysnum)
index = sysnum - sysnums.offset;
/* Sanity checks. */
- if (index > sysnums.length)
+ if (index >= sysnums.length)
return PR_void;
return sysnums.table[index];
diff --git a/src/syscall/sysnums-arm.h b/src/syscall/sysnums-arm.h
index 0503fdd..e3f6f6b 100644
--- a/src/syscall/sysnums-arm.h
+++ b/src/syscall/sysnums-arm.h
@@ -358,4 +358,6 @@ static const Sysnum sysnums_arm[] = {
[ 397 ] = PR_statx,
[ 439 ] = PR_faccessat2,
[ 435 ] = PR_clone3,
+ [ 437 ] = PR_openat2,
+ [ 452 ] = PR_fchmodat2,
};
diff --git a/src/syscall/sysnums-arm64.h b/src/syscall/sysnums-arm64.h
index 751382f..b257339 100644
--- a/src/syscall/sysnums-arm64.h
+++ b/src/syscall/sysnums-arm64.h
@@ -279,5 +279,7 @@ static const Sysnum sysnums_arm64[] = {
[ 290 ] = PR_pkey_free,
[ 291 ] = PR_statx,
[ 435 ] = PR_clone3,
+ [ 437 ] = PR_openat2,
[ 439 ] = PR_faccessat2,
+ [ 452 ] = PR_fchmodat2,
};
diff --git a/src/syscall/sysnums.list b/src/syscall/sysnums.list
index 8170b6e..c221ddd 100644
--- a/src/syscall/sysnums.list
+++ b/src/syscall/sysnums.list
@@ -72,6 +72,7 @@ SYSNUM(fanotify_mark)
SYSNUM(fchdir)
SYSNUM(fchmod)
SYSNUM(fchmodat)
+SYSNUM(fchmodat2)
SYSNUM(fchown)
SYSNUM(fchown32)
SYSNUM(fchownat)
@@ -221,6 +222,7 @@ SYSNUM(olduname)
SYSNUM(open)
SYSNUM(open_by_handle_at)
SYSNUM(openat)
+SYSNUM(openat2)
SYSNUM(pause)
SYSNUM(pciconfig_iobase)
SYSNUM(pciconfig_read)
@@ -0,0 +1,83 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: maxjivi05 <maxjividen@gmail.com>
Date: Sun, 27 Sep 2026 07:58:52 -0400
Subject: [PATCH] tracee: sweep relatives only for tracees that have some,
reuse the memory collector
---
src/path/path.h | 2 +-
src/tracee/tracee.c | 16 ++++++++++------
src/tracee/tracee.h | 1 +
3 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/src/path/path.h b/src/path/path.h
index d455894..7d0e68d 100644
--- a/src/path/path.h
+++ b/src/path/path.h
@@ -48,9 +48,9 @@ typedef enum {
/* Path with cached attributes. */
typedef struct {
- char path[PATH_MAX];
size_t length;
Side side;
+ char path[PATH_MAX];
} Path;
/* Path ending type. */
diff --git a/src/tracee/tracee.c b/src/tracee/tracee.c
index d6e414d..8bb2929 100644
--- a/src/tracee/tracee.c
+++ b/src/tracee/tracee.c
@@ -105,12 +105,16 @@ static int remove_tracee(Tracee *tracee)
* span. */
talloc_report_depth_cb(tracee->life_context, 0, 100, clean_life_span_object, tracee);
- /* This could be optimize by using a dedicated list of
- * children and ptracees. */
+ if (tracee->parent != NULL)
+ tracee->parent->nb_children--;
+
+ if (tracee->nb_children != 0 || tracee->as_ptracer.nb_ptracees != 0)
LIST_FOREACH(relative, &tracees, link) {
/* Its children are now orphan. */
- if (relative->parent == tracee)
+ if (relative->parent == tracee) {
relative->parent = NULL;
+ tracee->nb_children--;
+ }
/* Its tracees are now free. */
if (relative->as_ptracee.ptracer == tracee) {
@@ -342,9 +346,7 @@ Tracee *get_tracee(const Tracee *current_tracee, pid_t pid, bool create)
LIST_FOREACH(tracee, bucket_of(pid), bucket_link) {
if (tracee->pid == pid) {
- /* Flush then allocate a new memory collector. */
- TALLOC_FREE(tracee->ctx);
- tracee->ctx = talloc_new(tracee);
+ talloc_free_children(tracee->ctx);
return tracee;
}
@@ -506,6 +508,8 @@ int new_child(Tracee *parent, word_t clone_flags)
child->parent = parent->parent;
else
child->parent = parent;
+ if (child->parent != NULL)
+ child->parent->nb_children++;
/* Remember if this child belongs to the same thread group as
* its parent. This is currently useful for ptrace emulation
diff --git a/src/tracee/tracee.h b/src/tracee/tracee.h
index 527f428..1fca85f 100644
--- a/src/tracee/tracee.h
+++ b/src/tracee/tracee.h
@@ -101,6 +101,7 @@ typedef struct tracee {
/* Parent of this tracee, NULL if none. */
struct tracee *parent;
+ size_t nb_children;
/* Is it a "clone", i.e has the same parent as its creator. */
bool clone;