Skip to content

Commit 83a4c9c

Browse files
committed
fix: address PR #19 review findings on argument ownership and UI errors
source_search.c: when a literal pattern containing whitespace is rewritten into a word-order regex ("foo bar" -> "foo.*bar"), the replacement buffer was allocated with raw malloc() but later released with cbm_operation_arg_free() on every cleanup path, which charges the operation-argument class for memory it never accounted and trips the ownership check when memory phases are enabled. Allocate it with cbm_alloc(CBM_MEM_CLASS_OPERATION_ARG, ...) so the pattern keeps one allocation class whichever branch produced it. The raw site count for source_search.c drops from 64 to 63 in scripts/memory-core-baseline.txt. project_arg.c: the sanitized-name, normalized-path and cache-tail branches freed the caller's project string before checking whether the replacement copy was allocated. On allocation failure they returned NULL, and callers then reported "project is required" for a request that did supply one. Each branch now keeps the original project when its copy fails and frees it only after the replacement exists. graph-ui native.ts: fetchRead parsed the body as JSON before checking the status, so a non-JSON error body (the server's plain-text 404, a proxy error page) surfaced as a JSON SyntaxError instead of "HTTP <status>". Check res.ok first and parse the error body defensively.
1 parent 39cf455 commit 83a4c9c

4 files changed

Lines changed: 16 additions & 7 deletions

File tree

‎graph-ui/src/api/native.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ export async function fetchRead<T>(
66
const query = new URLSearchParams();
77
for (const [key, value] of Object.entries(args)) query.set(key, String(value));
88
const res = await fetch(`/api/${endpoint}?${query}`);
9-
const json = await res.json();
109
if (!res.ok) {
11-
throw new Error(json.error ?? `HTTP ${res.status}: ${res.statusText}`);
10+
// Error bodies are not always JSON (plain-text 404, proxy error pages).
11+
const body = (await res.json().catch(() => null)) as { error?: string } | null;
12+
throw new Error(body?.error ?? `HTTP ${res.status}: ${res.statusText}`);
1213
}
13-
return json as T;
14+
return (await res.json()) as T;
1415
}

‎scripts/memory-core-baseline.txt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ src/operations/schema.c 3
7070
src/operations/search.c 5
7171
src/operations/session_state.c 12
7272
src/operations/snippet.c 12
73-
src/operations/source_search.c 64
73+
src/operations/source_search.c 63
7474
src/operations/store_host.c 11
7575
src/operations/trace.c 5
7676
src/pipeline/artifact.c 38

‎src/operations/project_arg.c‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ static char *project_arg_normalize(char *project) {
3333
bool usable = encoded && strcmp(encoded, "root") != 0 && cbm_validate_project_name(encoded);
3434
if (usable) {
3535
char *copy = project_arg_strdup(encoded);
36-
cbm_free(CBM_MEM_CLASS_OPERATION_ARG, project);
3736
free(encoded);
37+
if (!copy)
38+
return project; /* keep the supplied project on allocation failure */
39+
cbm_free(CBM_MEM_CLASS_OPERATION_ARG, project);
3840
return copy;
3941
}
4042
free(encoded);
@@ -54,6 +56,8 @@ static char *project_arg_normalize(char *project) {
5456
if (normalized) {
5557
char *copy = project_arg_strdup(normalized);
5658
free(normalized);
59+
if (!copy)
60+
return project; /* keep the supplied project on allocation failure */
5761
cbm_free(CBM_MEM_CLASS_OPERATION_ARG, project);
5862
return copy;
5963
}
@@ -101,8 +105,11 @@ static char *project_arg_resolve_tail(char *project) {
101105
}
102106
cbm_closedir(dir);
103107
if (matches == 1) {
108+
char *copy = project_arg_strdup(match);
109+
if (!copy)
110+
return project; /* keep the supplied project on allocation failure */
104111
cbm_free(CBM_MEM_CLASS_OPERATION_ARG, project);
105-
return project_arg_strdup(match);
112+
return copy;
106113
}
107114
return project;
108115
}

‎src/operations/source_search.c‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2088,7 +2088,8 @@ cbm_operation_result_t cbm_source_search_operation_execute(const char *args,
20882088
if (!use_regex && strchr(pattern, ' ')) {
20892089
size_t plen = strlen(pattern);
20902090
/* Worst case: every char is a space → ".*" between each char */
2091-
char *regex_pat = malloc(plen * 3 + 1);
2091+
/* Same class as the pattern it replaces: released via cbm_operation_arg_free(). */
2092+
char *regex_pat = cbm_alloc(CBM_MEM_CLASS_OPERATION_ARG, plen * 3 + 1);
20922093
if (regex_pat) {
20932094
char *dst = regex_pat;
20942095
const char *src = pattern;

0 commit comments

Comments
 (0)