ADR-0438: CLI parser short-option handler coverage invariant¶
- Status: Accepted
- Date: 2026-05-15
- Deciders: lusoris, Claude (Anthropic)
- Tags:
cli,lint,testing,correctness
Context¶
An audit of the vmaf CLI (slice A, 2026-05-15) found that core/tools/cli_parse.c declared short_opts[] = "r:d:w:h:p:b:m:c:o:nvq", advertising -c as the short form of --cpumask, but the switch statement in cli_parse() had only case ARG_CPUMASK: (the long-option enum value 264) with no corresponding case 'c':. As a result getopt_long correctly consumed -c <value> from the command line but the switch fell into default: and discarded the parsed value silently. docs/usage/cli.md:158 documented -c as functional; the code did not implement it.
The same file already contained a complementary pattern for --dnn-ep / --tiny-device where case ARG_TINY_DEVICE: /* fall through */ case ARG_DNN_EP: routes both codes to the same handler block. The fix applies the same fall-through pattern for 'c' → ARG_CPUMASK.
SEI CERT C MSC11-C requires that every declared code path produces an observable effect when exercised. A silently-dropped argument violates that requirement and constitutes an observable correctness defect (user passes -c 0xff; the ISA restriction never applies).
Decision¶
We will:
- Add a
case 'c':arm beforecase ARG_CPUMASK:incli_parse(), using a fall-through so both the short and long forms execute the sameparse_unsignedcall with theARG_CPUMASKenum key (ensuringerror()reports the long-option name on invalid input, matching the ADR-0316 pattern). - Add a code comment near the switch documenting the invariant: every short option declared in
short_opts[]must have acasearm in the switch. - Add regression tests
test_cpumask_short_optincore/test/test_cli_parse.cthat assert-c 0xffsetscpumask == 255and-c 3setscpumask == 3.
Alternatives considered¶
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
Remove c: from short_opts[] and drop the short-form from docs | Zero new code; docs become accurate | Removes a documented and useful short flag with no user benefit; breaks scripts that already use -c | Unhelpful regression |
Add the case arm with a duplicate parse_unsigned call (no fall-through) | Marginally clearer to a reader who does not know the fall-through idiom | Duplicates code; error() would need a different enum or separate error() branch for the short-form path | Code duplication; already established fall-through idiom in the same switch |
Consequences¶
- Positive:
-c <bitmask>works as documented. The ISA restriction is applied when users pass it. The invariant comment prevents a future recurrence. - Negative: None — the change is additive.
- Neutral / follow-ups: The
test_cpumask_short_optregression test joins thetest_cli_parsebinary (no new binary; no change to meson.build required because the test file is already included in the existing executable).
References¶
- Audit slice A finding F1 (
.workingdir/audit-2026-05-15/A-cli-and-ffmpeg.md). - ADR-0316 (
0316-cli-parse-long-only-error-fix.md) — prior cpumask-adjacent fix (SIGABRT on bad--cpumaskvalue); established theARG_CPUMASKenum key pattern. - SEI CERT C MSC11-C — ensure proper alignment/handling of all declared paths.
docs/usage/cli.md:158— the user-facing documentation that claimed-cworked.