mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
fix(cli): keep config follow-roots off the static TLS block (#2110)
#1954's config-symlink follow support stored its opted-in roots in a _Thread_local char[4][4096] -- 16 KiB in the binary's static TLS block. On glibc/x86-64 that block is carved out of every thread's stack, including the parent-death watchdog thread, created with a deliberately tiny 64 KiB stack (PARENT_WATCHDOG_STACK_SIZE, main.c). The extra 16 KiB pushed that thread's pthread_create past the static-TLS + TCB + guard budget, so it failed with EINVAL: client_start_parent_watchdog() returned false and the stdio server exited before answering initialize. The parent-death watchdog regression test (#406/#407, scripts/test.sh Step 5) went red on every Linux x86-64 leg while macOS/arm64 stayed green -- its TLS is not stack-carved. Move the buffer off the static TLS block: keep a _Thread_local POINTER (8 bytes) and calloc the 16 KiB lazily on first add, freeing it in follow_clear(). Per-thread isolation and the exact API/semantics are unchanged; the watchdog thread's static-TLS budget is restored. main.c and the watchdog path are untouched. Evidence (local, Linux x86-64 gcc under QEMU + Linux arm64): - PT_TLS MemSiz 59,889 B -> 43,505 B (-16,384 B, exactly the array). - parent-watchdog test 10/10 red -> 10/10 green; red again on revert. - origin/main 10/10 green on the same env (regression is PR-specific). - config editor + cli suites: 522/522 ASan/UBSan (macOS), 523/523 ASan/LeakSanitizer (Linux arm64), no leak. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
1 parent
a3c24a71b4
commit
91c63be5d4
1 file changed
+19
-1
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
@@ -66,7 +67,16 @@ void cbm_config_edit_target_close(cbm_config_edit_target_t *target) {
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
static CBM_TLS char edit_path_roots[EDIT_PATH_MAX_ROOTS][CBM_CONFIG_EDIT_PATH_MAX];
|
||||
/* The opted-in follow-roots are per-thread (test isolation), but the storage
|
||||
* is a heap buffer behind a thread-local POINTER, not a thread-local array.
|
||||
* A `_Thread_local char[4][4096]` sits in the binary's static TLS block, and
|
||||
* glibc carves that block out of every thread's stack allocation — including
|
||||
* the parent-death watchdog's deliberately tiny 64 KiB stack (main.c). The
|
||||
* 16 KiB array pushed the watchdog's pthread_create over the static-TLS +
|
||||
* guard budget, so it failed with EINVAL on glibc/x86-64 (green on macOS,
|
||||
* whose TLS is not stack-carved): #2110 CI. The pointer keeps the static TLS
|
||||
* block at 8 bytes; the buffer is allocated on first add and freed by clear. */
|
||||
static CBM_TLS char (*edit_path_roots)[CBM_CONFIG_EDIT_PATH_MAX] = NULL;
|
||||
static CBM_TLS size_t edit_path_root_count = 0U;
|
||||
|
||||
#ifdef CBM_CONFIG_EDIT_PATH_ENABLE_TEST_API
|
||||
@@ -101,6 +111,12 @@ int cbm_config_edit_path_follow_add_root(const char *root) {
|
||||
if (!cbm_canonical_path(root, canonical, sizeof(canonical))) {
|
||||
return -1;
|
||||
}
|
||||
if (!edit_path_roots) {
|
||||
edit_path_roots = calloc(EDIT_PATH_MAX_ROOTS, sizeof(*edit_path_roots));
|
||||
if (!edit_path_roots) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (edit_path_copy(canonical, edit_path_roots[edit_path_root_count],
|
||||
sizeof(edit_path_roots[0])) != 0) {
|
||||
return -1;
|
||||
@@ -111,6 +127,8 @@ int cbm_config_edit_path_follow_add_root(const char *root) {
|
||||
|
||||
void cbm_config_edit_path_follow_clear(void) {
|
||||
edit_path_root_count = 0U;
|
||||
free(edit_path_roots);
|
||||
edit_path_roots = NULL;
|
||||
}
|
||||
|
||||
static int edit_path_under_root(const char *canonical) {
|
||||
|
||||
Reference in new issue
Block a user