From 33bb058232e909fee9b82469877449276e61b87d Mon Sep 17 00:00:00 2001 From: Pedro Ramos <131530838+pr9000@users.noreply.github.com> Date: Thu, 6 Aug 2026 03:19:40 +0100 Subject: [PATCH 1/3] fix(daemon): scope the /home log-path alias to FreeBSD only private_log_directory_path_copy shared the /home root-owned alias between __APPLE__ and __FreeBSD__. macOS never needed /home resolved in this security-sensitive step; split the alias list so Darwin keeps {/tmp, /var} and only FreeBSD gains /home -> /usr/home. Signed-off-by: Pedro Ramos <131530838+pr9000@users.noreply.github.com> --- src/daemon/ipc.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/daemon/ipc.c b/src/daemon/ipc.c index 5b0a76f80..76d840ebf 100644 --- a/src/daemon/ipc.c +++ b/src/daemon/ipc.c @@ -1337,12 +1337,18 @@ static bool private_log_base_name_valid(const char *base_name) { } static char *private_log_directory_path_copy(const char *directory_path) { -#ifdef __APPLE__ - /* Darwin exposes the trusted top-level aliases /tmp -> /private/tmp and - * /var -> /private/var. Resolve only those root-owned aliases before the +#if defined(__APPLE__) || defined(__FreeBSD__) + /* Resolve only those root-owned aliases before the * component-wise O_NOFOLLOW walk. Canonicalizing the complete caller path * would follow an attacker-controlled cache/log symlink and is forbidden. */ +#if defined(__APPLE__) + /* Darwin exposes the trusted top-level aliases /tmp -> /private/tmp and + * /var -> /private/var. */ static const char *const aliases[] = {"/tmp", "/var"}; +#else + /* FreeBSD additionally exposes /home -> /usr/home as a root-owned alias. */ + static const char *const aliases[] = {"/tmp", "/var", "/home"}; +#endif for (size_t index = 0; index < sizeof(aliases) / sizeof(aliases[0]); index++) { const char *alias = aliases[index]; size_t alias_length = strlen(alias); From 12b928a74e13f14cd5ae95a8323ce90cbfb21f99 Mon Sep 17 00:00:00 2001 From: Pedro Ramos <131530838+pr9000@users.noreply.github.com> Date: Thu, 6 Aug 2026 08:17:21 +0100 Subject: [PATCH 2/3] fix(daemon): add native FreeBSD process-image identity support Extends runtime_process_image_reference_* (self/peer process image acquisition and comparison) to FreeBSD, previously guarded only for _WIN32/__APPLE__/__linux__. Uses sysctl(KERN_PROC_PATHNAME) to resolve a process's executable path without depending on procfs. Renames runtime_linux_stat_same_image to runtime_posix_stat_same_image now that the same comparison logic serves Linux, macOS, and FreeBSD. Signed-off-by: Pedro Ramos <131530838+pr9000@users.noreply.github.com> --- src/daemon/runtime.c | 55 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/src/daemon/runtime.c b/src/daemon/runtime.c index bdbb2c56b..4572263f1 100644 --- a/src/daemon/runtime.c +++ b/src/daemon/runtime.c @@ -42,10 +42,14 @@ void cbm_daemon_runtime_force_peer_image_unverified_for_testing(bool force) { #include #include #include -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) #include #include #include +#if defined(__FreeBSD__) +#include +#include +#endif #endif enum { @@ -147,7 +151,7 @@ typedef struct { HANDLE file; BY_HANDLE_FILE_INFORMATION information; LARGE_INTEGER size; -#elif defined(__APPLE__) || defined(__linux__) +#elif defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__) int fd; struct stat status; #endif @@ -502,7 +506,7 @@ static bool runtime_activation_response_decode( static uint64_t runtime_current_process_id(void) { #ifdef _WIN32 return (uint64_t)GetCurrentProcessId(); -#elif defined(__APPLE__) || defined(__linux__) +#elif defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__) return (uint64_t)getpid(); #else return 0; @@ -516,7 +520,7 @@ static void runtime_process_image_reference_init(runtime_process_image_reference memset(reference, 0, sizeof(*reference)); #ifdef _WIN32 reference->file = INVALID_HANDLE_VALUE; -#elif defined(__APPLE__) || defined(__linux__) +#elif defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__) reference->fd = -1; #endif } @@ -530,7 +534,7 @@ static bool runtime_process_image_reference_release(runtime_process_image_refere if (reference->file != INVALID_HANDLE_VALUE && !CloseHandle(reference->file)) { ok = false; } -#elif defined(__APPLE__) || defined(__linux__) +#elif defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__) if (reference->fd >= 0 && close(reference->fd) != 0) { ok = false; } @@ -670,9 +674,9 @@ static bool runtime_mac_process_maps_file_executable(int process_id, const struc return false; } -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) -static bool runtime_linux_stat_same_image(const struct stat *first, const struct stat *second) { +static bool runtime_posix_stat_same_image(const struct stat *first, const struct stat *second) { return first && second && S_ISREG(first->st_mode) && S_ISREG(second->st_mode) && first->st_dev == second->st_dev && first->st_ino == second->st_ino && first->st_size == second->st_size && first->st_mtim.tv_sec == second->st_mtim.tv_sec && @@ -788,11 +792,11 @@ static bool runtime_process_image_reference_acquire( (!fingerprint || cbm_daemon_build_fingerprint_native_file((uintptr_t)image_fd, fingerprint)) && fstat(image_fd, &image_after) == 0 && - runtime_linux_stat_same_image(&image_before, &image_after); + runtime_posix_stat_same_image(&image_before, &image_after); int verify_fd = ok ? openat(process_fd, "exe", O_RDONLY | O_CLOEXEC) : -1; struct stat verify_status; ok = ok && verify_fd >= 0 && fstat(verify_fd, &verify_status) == 0 && - runtime_linux_stat_same_image(&image_after, &verify_status); + runtime_posix_stat_same_image(&image_after, &verify_status); if (verify_fd >= 0 && close(verify_fd) != 0) { ok = false; } @@ -806,6 +810,33 @@ static bool runtime_process_image_reference_acquire( } else if (image_fd >= 0) { (void)close(image_fd); } +#elif defined(__FreeBSD__) + if (process_id > INT_MAX) { + return false; + } + int pid = (int)process_id; + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, pid}; + char path[PATH_MAX]; + size_t path_length = sizeof(path); + bool ok = sysctl(mib, 4, path, &path_length, NULL, 0) == 0 && path_length > 0; + if (ok) { + path[path_length < sizeof(path) ? path_length : sizeof(path) - 1] = '\0'; + } + int image_fd = ok ? open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK) : -1; + struct stat image_before; + struct stat image_after; + ok = image_fd >= 0 && fstat(image_fd, &image_before) == 0 && S_ISREG(image_before.st_mode) && + (!fingerprint || + cbm_daemon_build_fingerprint_native_file((uintptr_t)image_fd, fingerprint)) && + fstat(image_fd, &image_after) == 0 && + runtime_posix_stat_same_image(&image_before, &image_after); + if (ok) { + reference->held = true; + reference->fd = image_fd; + reference->status = image_after; + } else if (image_fd >= 0) { + (void)close(image_fd); + } #else (void)process_id; bool ok = false; @@ -846,14 +877,14 @@ static bool runtime_process_image_reference_matches_process( runtime_mac_stat_same(&active->status, &peer.status); bool released = runtime_process_image_reference_release(&peer); return same && released; -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) runtime_process_image_reference_t peer; runtime_process_image_reference_init(&peer); bool same = runtime_process_image_reference_acquire(process_id, &peer, NULL); struct stat active_now; same = same && fstat(active->fd, &active_now) == 0 && - runtime_linux_stat_same_image(&active->status, &active_now) && - runtime_linux_stat_same_image(&active->status, &peer.status); + runtime_posix_stat_same_image(&active->status, &active_now) && + runtime_posix_stat_same_image(&active->status, &peer.status); bool released = runtime_process_image_reference_release(&peer); return same && released; #else From d6f0196a9c017ff9165a6c571b50e2d1a89555e0 Mon Sep 17 00:00:00 2001 From: Pedro Ramos <131530838+pr9000@users.noreply.github.com> Date: Sat, 25 Jul 2026 13:46:20 +0100 Subject: [PATCH 3/3] fix(tests): include in test_stack_overflow.c Signed-off-by: Pedro Ramos <131530838+pr9000@users.noreply.github.com> --- tests/test_stack_overflow.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_stack_overflow.c b/tests/test_stack_overflow.c index 6b2e8228c..9b646296c 100644 --- a/tests/test_stack_overflow.c +++ b/tests/test_stack_overflow.c @@ -16,6 +16,7 @@ #include #include #include +#include /* tree-sitter runtime allocator hooks (ts_runtime/src/alloc.h, TS_PUBLIC) and * mimalloc (vendored) — for the #424 allocator-binding regression test. */