options: reject an empty long option name

An argument of the form "--=VALUE" leaves an empty option name, which
startswith() treats as a prefix of every defined option. Depending on how many
long options a namespace defines that yields either an ambiguity error listing
every option, or, with exactly one option defined, a unique partial match that
silently parses "--=VALUE" as that option.
This commit is contained in:
Armaan Sandhu
2026-08-13 07:11:37 +09:00
committed by Yu Watanabe
parent 3ae53ab8f6
commit 5dc41f8283
2 changed files with 30 additions and 0 deletions
+10
View File
@@ -223,6 +223,16 @@ int option_parse(
/* argument (if any) is separate */
optname = state->argv[state->optind];
/* An empty name, i.e. "--=…", would be a prefix of every option, so reject it
* right away rather than let it match. */
if (isempty(optname + 2)) {
r = log_full_errno(LOG_ERR + state->log_level_shift,
SYNTHETIC_ERRNO(EINVAL),
"%s: unrecognized option '%s'",
program_invocation_short_name, optname);
goto fail;
}
const Option *last_partial = NULL;
unsigned n_partial_matches = 0; /* The commandline option matches a defined prefix. */
+20
View File
@@ -1580,4 +1580,24 @@ TEST(option_get_synopsis) {
test_option_get_synopsis_one(&(const Option) { 0, OPTION_POSITIONAL_ENTRY, 'u', "(fixed)", "unused" }, "/", true, "(fixed)");
}
TEST(option_empty_long_name) {
static const Option one_option[] = {
{ 1, .short_code = 'o', .long_code = "output", .metavar = "ARG" },
{}
};
static const Option two_options[] = {
{ 1, .short_code = 'o', .long_code = "output", .metavar = "ARG" },
{ 2, .long_code = "help" },
{}
};
/* "--=…" carries an empty long option name, which must not prefix-match every defined option.
* With a single option defined that would otherwise look like a unique partial match. */
test_option_invalid_one(STRV_MAKE("arg0", "--=foo"), one_option);
test_option_invalid_one(STRV_MAKE("arg0", "--="), one_option);
test_option_invalid_one(STRV_MAKE("arg0", "--=foo"), two_options);
test_option_invalid_one(STRV_MAKE("arg0", "--="), two_options);
}
DEFINE_TEST_MAIN(LOG_DEBUG);