fix: address F2/F4 review findings (leaks, qsort guard, err contract, compiler-default layering)

This commit is contained in:
2026-08-29 01:28:54 -04:00
parent b94a540673
commit 02c40fa7fa
6 changed files with 48 additions and 14 deletions
-1
View File
@@ -133,7 +133,6 @@ static const char CACHE_FUNCS[] =
"# snippets read results via st_cache_get and record them via\n" "# snippets read results via st_cache_get and record them via\n"
"# st_cache_set. Cache key: ac_cv_<checkname>_<compiler-id>.\n" "# st_cache_set. Cache key: ac_cv_<checkname>_<compiler-id>.\n"
"\n" "\n"
": \"${CC:=cc}\"\n"
": \"${CFLAGS:=}\"\n" ": \"${CFLAGS:=}\"\n"
": \"${st_cc_id:=unknown}\"\n" ": \"${st_cc_id:=unknown}\"\n"
": \"${cross_compiling:=}\"\n" ": \"${cross_compiling:=}\"\n"
+8 -1
View File
@@ -167,7 +167,13 @@ read_module_source(const char *path, char **out, size_t *out_len)
return st_error_internal("extension load failed: out of memory"); return st_error_internal("extension load failed: out of memory");
} }
got = fread(buf, 1, (size_t)size, f); got = fread(buf, 1, (size_t)size, f);
if (got != (size_t)size || fclose(f) != 0) { if (got != (size_t)size) {
/* short read: close BEFORE the error return (no leak) */
(void)fclose(f);
free(buf);
goto io_fail;
}
if (fclose(f) != 0) {
free(buf); free(buf);
goto io_fail; goto io_fail;
} }
@@ -336,6 +342,7 @@ discover_dir(struct st_lua_rt *rt, const char *dir)
free(names); free(names);
return err; return err;
} }
if (n > 1)
qsort(names, n, sizeof *names, cmp_str); qsort(names, n, sizeof *names, cmp_str);
for (i = 0; i < n && err == NULL; i++) { for (i = 0; i < n && err == NULL; i++) {
char *path; char *path;
+3 -3
View File
@@ -388,8 +388,8 @@ static const char ARGS_PRE[] =
"# Full argument parsing (todo 18): --help/-h, --version, --prefix/\n" "# Full argument parsing (todo 18): --help/-h, --version, --prefix/\n"
"# --exec-prefix/--host/--build in both '=' and space forms, the\n" "# --exec-prefix/--host/--build in both '=' and space forms, the\n"
"# per-option --enable-<name>/--disable-<name> flags, and generic\n" "# per-option --enable-<name>/--disable-<name> flags, and generic\n"
"# VAR=VALUE overrides (positional, e.g. `./configure CC=clang`, or\n" "# VAR=VALUE overrides (positional, e.g. `./configure VAR=VALUE`, or\n"
"# exported by the invoking shell, e.g. `CC=clang ./configure`).\n" "# exported by the invoking shell, e.g. `VAR=VALUE ./configure`).\n"
"# Unknown --* options error. The loop only READS \"$@\", so the\n" "# Unknown --* options error. The loop only READS \"$@\", so the\n"
"# config.log init right after this section still sees the complete\n" "# config.log init right after this section still sees the complete\n"
"# original invocation.\n" "# original invocation.\n"
@@ -414,7 +414,7 @@ static const char USAGE_HEAD[] =
" printf '%s\\n' ' differs from --build -> cross-compiling'\n" " printf '%s\\n' ' differs from --build -> cross-compiling'\n"
" printf '%s\\n' ' --build=TRIPLET, --build TRIPLET'\n" " printf '%s\\n' ' --build=TRIPLET, --build TRIPLET'\n"
" printf '%s\\n' ' the platform the build is running on'\n" " printf '%s\\n' ' the platform the build is running on'\n"
" printf '%s\\n' ' VAR=VALUE override the variable VAR (e.g. CC=clang);'\n" " printf '%s\\n' ' VAR=VALUE override a variable before substitution;'\n"
" printf '%s\\n' ' both VAR=VALUE ./configure and'\n" " printf '%s\\n' ' both VAR=VALUE ./configure and'\n"
" printf '%s\\n' ' ./configure VAR=VALUE work'\n"; " printf '%s\\n' ' ./configure VAR=VALUE work'\n";
+7 -3
View File
@@ -91,9 +91,13 @@ struct st_kdl_document {
/* Parse `src` (a NUL-terminated buffer, borrowed) into an AST. On success /* Parse `src` (a NUL-terminated buffer, borrowed) into an AST. On success
* returns the document — possibly with zero nodes — and leaves `*err` * returns the document — possibly with zero nodes — and leaves `*err`
* untouched. On failure returns NULL and, if `err != NULL`, sets `*err` * untouched. On failure returns NULL and sets `*err` to an owned st_error
* to an owned st_error of category ST_ERR_KDL_PARSE with a stable span * of category ST_ERR_KDL_PARSE with a stable span (see the header
* (see the header comment). `filename` is borrowed into every span. */ * comment). `err` is REQUIRED: it must point to a valid
* `struct st_error *` — every caller passes `&err`. NULL is tolerated
* only as a last-resort fallback (the diagnostic is then discarded, and
* the document is still NULL on failure), but no new caller should rely
* on it. `filename` is borrowed into every span. */
struct st_kdl_document *st_kdl_parse(const char *src, const char *filename, struct st_kdl_document *st_kdl_parse(const char *src, const char *filename,
struct st_error **err); struct st_error **err);
+9 -2
View File
@@ -621,10 +621,14 @@ st_kdl_parse(const char *src, const char *filename, struct st_error **err)
struct st_kdl_document *doc; struct st_kdl_document *doc;
struct st_kdl_node *head = NULL; struct st_kdl_node *head = NULL;
struct st_kdl_node *tail = NULL; struct st_kdl_node *tail = NULL;
struct st_error *local_err = NULL;
if (err != NULL) { if (err == NULL) {
*err = NULL; /* last-resort fallback: the documented contract requires a
* non-NULL err; a NULL caller discards the diagnostic */
err = &local_err;
} }
*err = NULL;
parser.lx = st_lexer_new(src, filename); parser.lx = st_lexer_new(src, filename);
if (parser.lx == NULL) { if (parser.lx == NULL) {
@@ -684,6 +688,9 @@ fail:
st_lexer_free(parser.lx); st_lexer_free(parser.lx);
nodes_free(head); nodes_free(head);
free(doc); free(doc);
if (err == &local_err && local_err != NULL) {
st_error_free(local_err);
}
return NULL; return NULL;
} }
+20 -3
View File
@@ -109,7 +109,21 @@ read_file(const char *path, char **out)
return st_error_internal("out of memory"); return st_error_internal("out of memory");
} }
got = fread(buf, 1, (size_t)size, f); got = fread(buf, 1, (size_t)size, f);
if (got != (size_t)size || fclose(f) != 0) { if (got != (size_t)size) {
/* short read: close BEFORE the error return (no leak) */
(void)fclose(f);
free(buf);
need = strlen(path) + 64;
buf = malloc(need);
if (buf == NULL)
return st_error_internal("out of memory");
(void)snprintf(buf, need, "cannot read build file '%s': %s",
path, strerror(errno));
err = st_error_io(buf);
free(buf);
return err;
}
if (fclose(f) != 0) {
free(buf); free(buf);
need = strlen(path) + 64; need = strlen(path) + 64;
buf = malloc(need); buf = malloc(need);
@@ -205,8 +219,11 @@ main(int argc, char **argv)
{ {
const char *cc = st_registry_get_var(st_ext_var_registry(ctx), const char *cc = st_registry_get_var(st_ext_var_registry(ctx),
"CC"); "CC");
printf("wrote ./configure (CC=%s)\n",
cc != NULL ? cc : "cc"); if (cc != NULL)
printf("wrote ./configure (CC=%s)\n", cc);
else
printf("wrote ./configure\n");
} }
rc = CLI_EXIT_OK; rc = CLI_EXIT_OK;