fix string leak in cli args_free (#2220)

args_parse zeroed every option's value pointer right before returning, so args_free's NULL check always skipped the free. Even without that, args_free was freeing the address of the caller's static variable (e.g. &ext_vars), not the string it points to.

On Windows (_UNICODE builds) each ARGS_OPT_STRING value is a heap copy from unicode_to_ansi(), so -d, -x, -i, atom-quality-table and module-data args leaked on every run. On non-Windows the value just aliases argv, nothing to free.

Removed the value-clearing loop in args_parse (nothing reads .value after parsing except args_free) and rewrote args_free to walk options->count entries and free the actual string pointers, gated on _UNICODE so it's a no-op elsewhere.
This commit is contained in:
Cole Munz
2026-07-06 09:43:47 +02:00
committed by GitHub
parent c125ac7fa1
commit 8a5a88e463
+14 -7
View File
@@ -257,9 +257,6 @@ int args_parse(args_option_t* options, int argc, const char_t** argv)
i++;
}
// Initialize to NULL the value pointers for all options.
for (; options->type != ARGS_OPT_END; options++) options->value = NULL;
return o;
}
@@ -298,11 +295,21 @@ void args_print_usage(args_option_t* options, int help_alignment)
void args_free(args_option_t* options)
{
#ifdef _UNICODE
// On Windows the string values stored by args_parse_option are ANSI
// copies allocated by unicode_to_ansi(), so they must be released here.
// On non-Windows builds the string values point directly into argv,
// there's nothing to free.
for (; options->type != ARGS_OPT_END; options++)
{
if (options->type == ARGS_OPT_STRING && options->value != NULL)
{
free(options->value);
}
if (options->type != ARGS_OPT_STRING || options->value == NULL)
continue;
char** strings = (char**) options->value;
for (int i = 0; i < options->count; i++) free(strings[i]);
}
#else
(void) options;
#endif
}