Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions internal/cbm/cbm.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "foundation/compat_fs.h" // cbm_fopen — crash-supervisor per-file marker write
#include "foundation/hash_table.h" // CBMHashTable — crash-supervisor quarantine set
#include "tree_sitter/api.h" // TSParser, TSNode, TSTree, TSInput, TSLanguage, TSPoint, TSParseOptions, TSParseState
#include "foundation/platform.h"
#include "foundation/constants.h"
#include "mimalloc.h" // mi_malloc/mi_calloc/mi_realloc/mi_free/mi_usable_size — bind 3rd-party allocators (#424)
#if defined(CBM_BIND_TS_ALLOCATOR) && CBM_BIND_TS_ALLOCATOR
Expand Down Expand Up @@ -969,7 +970,9 @@ static int count_params_from_signature(const char *sig) {
* unlucky in-flight file is never quarantined alone. The env var is set
* solely by the supervisor during recovery — a no-op on normal runs. */
static void cbm_index_mark(const char *rel_path, char event) {
const char *mf = getenv("CBM_INDEX_MARKER_FILE");
char marker_path[CBM_SZ_4K];
const char *mf =
cbm_safe_getenv("CBM_INDEX_MARKER_FILE", marker_path, sizeof(marker_path), NULL);
if (!mf || !mf[0] || !rel_path || !rel_path[0]) {
return;
}
Expand Down Expand Up @@ -1006,7 +1009,9 @@ enum { CBM_QSET_UNINIT = 0, CBM_QSET_INITING = 1, CBM_QSET_INITED = 2 };
static atomic_int g_quarantine_state = CBM_QSET_UNINIT;

static void cbm_quarantine_load(void) {
const char *qf = getenv("CBM_INDEX_QUARANTINE_FILE");
char quarantine_path[CBM_SZ_4K];
const char *qf = cbm_safe_getenv("CBM_INDEX_QUARANTINE_FILE", quarantine_path,
sizeof(quarantine_path), NULL);
if (!qf || !qf[0]) {
return; /* normal path: empty set */
}
Expand Down
4 changes: 3 additions & 1 deletion src/cli/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,9 @@ static bool cli_activation_production_context_init(cli_activation_production_con
context->cleanup_ok = true;
context->deadline_ms = cli_activation_deadline_after(CLI_ACTIVATION_DRAIN_TIMEOUT_MS);
context->control_deadline_ms = cli_activation_deadline_after(CLI_ACTIVATION_CONTROL_TIMEOUT_MS);
const char *original_cache_environment = getenv("CBM_CACHE_DIR");
char original_cache_buffer[CLI_BUF_4K];
const char *original_cache_environment = cbm_safe_getenv("CBM_CACHE_DIR", original_cache_buffer,
sizeof(original_cache_buffer), NULL);
context->original_cache_environment_present = original_cache_environment != NULL;
if (context->original_cache_environment_present) {
context->original_cache_environment = strdup(original_cache_environment);
Expand Down
8 changes: 3 additions & 5 deletions src/foundation/compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,9 @@ static inline int cbm_setenv(const char *name, const char *value, int overwrite)
free(wide_value);
return EINVAL;
}
/* Keep the CRT's narrow environment useful for legacy getenv callers,
* then repair the process-wide Windows environment with the actual UTF-16
* value. _putenv_s alone routes UTF-8 path bytes through the active ANSI
* code page, which corrupts non-ASCII cache roots inherited by children. */
int status = _putenv_s(name, value);
/* _putenv_s can reject UTF-8 bytes outside the active ANSI code page.
* Narrow getenv returns bytes in that code page after this wide update. */
int status = _wputenv_s(wide_name, wide_value);
if (status == 0 && !SetEnvironmentVariableW(wide_name, wide_value)) {
status = EINVAL;
}
Expand Down
36 changes: 36 additions & 0 deletions tests/test_extraction.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/
#include "test_framework.h"
#include "cbm.h"
#include "foundation/platform.h"
#include "foundation/constants.h" /* CBM_SZ_* */
#include "preprocessor.h" /* cbm_export_macro_candidates (#1989) */
#include "../src/foundation/compat.h" /* cbm_clock_gettime (wide-flat scaling guard) */
Expand Down Expand Up @@ -8157,7 +8158,42 @@ TEST(extract_walk_truncated_when_a_node_budget_is_set) {
PASS();
}

#ifdef _WIN32
/* The supervisor sets this path through cbm_setenv. Exercise the actual
* journal writer, which must read it as UTF-8 before calling cbm_fopen. */
TEST(extraction_marker_round_trips_non_ascii_environment_path) {
char dir[512] = "/tmp/cbm-marker-XXXXXX";
ASSERT_NOT_NULL(cbm_mkdtemp(dir));
char marker[640];
snprintf(marker, sizeof(marker), "%s/marker-\xce\x94-\xe4\xb8\x81.log", dir);
char saved[4096];
const char *previous = cbm_safe_getenv("CBM_INDEX_MARKER_FILE", saved, sizeof(saved), NULL);
ASSERT_EQ(cbm_setenv("CBM_INDEX_MARKER_FILE", marker, 1), 0);
cbm_index_mark_start("synthetic.cpp");
cbm_index_mark_done("synthetic.cpp");
if (previous) {
(void)cbm_setenv("CBM_INDEX_MARKER_FILE", saved, 1);
} else {
(void)cbm_unsetenv("CBM_INDEX_MARKER_FILE");
}
FILE *file = cbm_fopen(marker, "rb");
char contents[128] = {0};
if (file) {
(void)fread(contents, 1, sizeof(contents) - 1, file);
(void)fclose(file);
}
(void)cbm_unlink(marker);
(void)cbm_rmdir(dir);
ASSERT_NOT_NULL(file);
ASSERT_STR_EQ(contents, "S synthetic.cpp\nD synthetic.cpp\n");
PASS();
}
#endif

SUITE(extraction) {
#ifdef _WIN32
RUN_TEST(extraction_marker_round_trips_non_ascii_environment_path);
#endif
RUN_TEST(extract_compact_keeps_every_field_and_shrinks_the_arena);
RUN_TEST(extract_compact_is_idempotent_and_survives_empty_results);
RUN_TEST(extract_spill_round_trip_keeps_every_field);
Expand Down
12 changes: 10 additions & 2 deletions tests/test_platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -869,15 +869,23 @@ TEST(platform_env_long_refuses_what_it_cannot_read) {
* setter must update that same wide environment; _putenv_s alone interprets
* UTF-8 path bytes through the active ANSI code page. */
TEST(platform_setenv_preserves_utf8_in_wide_environment) {
static const char utf8[] = "C:/cbm-cache-\xce\x94-\xe6\x97\xa5\xe6\x9c\xac";
static const wchar_t wide[] = L"C:/cbm-cache-\u0394-\u65e5\u672c";
static const char utf8[] = "C:/cbm-cache-\xce\x94-\xe6\x97\xa5\xe6\x9c\xac-\xe4\xb8\x81";
static const wchar_t wide[] = L"C:/cbm-cache-\u0394-\u65e5\u672c-\u4e01";
ASSERT_EQ(cbm_setenv("CBM_CACHE_DIR", utf8, 1), 0);
wchar_t observed_wide[128];
ASSERT_EQ(GetEnvironmentVariableW(L"CBM_CACHE_DIR", observed_wide, 128), (DWORD)(wcslen(wide)));
ASSERT_EQ(wcscmp(observed_wide, wide), 0);
char observed_utf8[128];
ASSERT_NOT_NULL(cbm_safe_getenv("CBM_CACHE_DIR", observed_utf8, sizeof(observed_utf8), NULL));
ASSERT_STR_EQ(observed_utf8, utf8);
/* Narrow getenv must agree with the CRT's active ANSI code page. */
char expected_narrow[128];
ASSERT_GT(WideCharToMultiByte(CP_ACP, 0, wide, -1, expected_narrow,
(int)sizeof(expected_narrow), NULL, NULL),
0);
const char *observed_narrow = getenv("CBM_CACHE_DIR");
ASSERT_NOT_NULL(observed_narrow);
ASSERT_STR_EQ(observed_narrow, expected_narrow);
(void)cbm_unsetenv("CBM_CACHE_DIR");
PASS();
}
Expand Down
Loading