Skip to content

Parse glibc guests' options the way glibc's getopt does - #15

Open
buyemune-sys wants to merge 2 commits into
pg83:mainfrom
buyemune-sys:fix/glibc-getopt
Open

buyemune-sys wants to merge 2 commits into
pg83:mainfrom
buyemune-sys:fix/glibc-getopt

Conversation

@buyemune-sys

Copy link
Copy Markdown

The problem

The getopt family went straight to musl, apart from the optind/optarg shuttle around it (lib/glibc_shim.cpp:1603). glibc's getopt behaves differently in ways programs notice:

  • Permutation. glibc's getopt permutes argv just like getopt_long. Only a string starting with + or -, POSIXLY_CORRECT, or __posix_getopt keep it in order. musl's getopt never permutes, so prog file -v loses -v:

    $ ./getopt_permute file1 -a -b x file2      # glibc
    opt a / opt b x / operand file1 / operand file2
    $ solo ./getopt_permute file1 -a -b x file2
    operand file1 / operand -a / operand -b / operand x / operand file2
    
  • argv corruption. In musl's getopt_long, when the short option's argument is missing at the end of argv after a permuted operand (prog file -n), getopt leaves optind = argc + 1. The permutation step (ext/musl/src/misc/getopt_long.c:47) then rotates argv's NULL terminator into the vector: argv[2] = NULL, argv[3] = "file". glibc stops at argc and keeps argv terminated.

  • Diagnostics. glibc prints invalid option -- 'Z', option requires an argument -- 'n', unrecognized option '--bogus=1', option '--ver' is ambiguous; possibilities: '--verbose' '--version', option '--verbose' doesn't allow an argument. musl prints unrecognized option: Z, option requires an argument: n, and so on. Tarantool's own test suite matches glibc's wording (app-luatest/args_test.lua, app-luatest/gh_8613_centralized_config_options_test.lua).

  • __posix_getopt. musl exports a __posix_getopt of its own, so the name resolved straight to musl. That bypassed the shuttle: in a COPY-relocated executable the guest's optind never advanced, and a parse loop spun on the same argument.

The fix

sh_getopt_run (lib/glibc_shim.cpp:1710) backs getopt, __posix_getopt, getopt_long and getopt_long_only. The order of work:

  1. The bridge itself finds the next option element.
  2. musl parses only that element, with its own messages off.
  3. The bridge clamps optind at argc and rotates what musl consumed in front of the skipped operands, the order glibc's exchange leaves (sh_getopt_rotate, :1617).
  4. It prints glibc's message (sh_getopt_complain, :1631).

The element is classified exactly as musl's parser classified it. __posix_getopt joins overrideNames.

Two glibc rules stay out of reach without replacing musl's parser:

  • an abbreviation that matches several options with identical effect is not ambiguous to glibc;
  • getopt_long_only skips an unknown -xyz as a whole.

Test

  • The conformance battery (tst/glibc_shim_test.c, options()) checks:

    • permutation and argv layout;
    • + and __posix_getopt keeping order;
    • argv integrity and optind after a missing argument;
    • each diagnostic, with stderr captured.

    All of these values were first checked against real glibc.

  • The guest executable calls __posix_getopt through its own copy of optind. Needle: guest getopt q optind=2.

On main: 13 battery checks fail, and the guest prints guest getopt q optind=1. With the fix: ./build test passes. rootfs_smoke is skipped here because user namespaces are unavailable.

This conflicts with #9 in lib/glibc_shim.cpp and tst/run_smoke.py, both near the getopt wrappers. The resolution keeps both sides. Once both are in, POSIXLY_CORRECT should be read through sh_getenv.

buyemune-sys and others added 2 commits October 20, 2026 15:42
Security updates replaced libapparmor1 4.0.1really4.0.1-0ubuntu0.24.04.7
with .9 and libexpat1 2.6.1-2ubuntu0.4 with ubuntu0.6, and the pool kept
only the new files, so the cold-cache Fetch x86_64 job answers 404 and
every x86-64 job behind it is skipped. Both pins move to the versions
noble-updates ships now; their SHA256s match the archive's Packages index.
The other twelve pins are still served.
glibc's getopt family differs from musl's in ways programs notice:

- glibc's getopt permutes argv just like getopt_long, so `prog file -v`
  still sees -v; musl's getopt stops at the first operand. Only a string
  starting with '+' or '-', POSIXLY_CORRECT, or __posix_getopt keep
  glibc in order.
- musl's getopt_long, permuting past a short option whose argument is
  missing at the end of argv, leaves optind at argc + 1 and rotates
  argv's NULL terminator into the vector. glibc stops at argc and keeps
  argv terminated.
- The diagnostics: "invalid option -- 'Z'" and "option requires an
  argument -- 'n'" against musl's "unrecognized option: Z" and "option
  requires an argument: n", and likewise for every long-option error.
  Tarantool's test suite matches glibc's wording, and so do users'
  scripts.
- __posix_getopt resolved straight to musl's export of that name, which
  never touches the executable's copy of optind: a guest parsing with it
  in a COPY-relocated executable looped on the same argument forever.

The bridge now finds the next option element itself and has musl parse
only that element, with musl's messages off. It then rotates what musl
consumed in front of the operands the scan stepped over, the order
glibc's exchange leaves, and prints glibc's message. __posix_getopt
joins the overridden names.

Two glibc rules stay out of reach without replacing musl's parser: an
abbreviation that matches several options with identical effect is not
ambiguous to glibc, and getopt_long_only skips an unknown "-xyz" as a
whole rather than trying it as short options.

The conformance battery checks permutation, argv integrity after a
missing argument, and each diagnostic. The guest executable parses with
__posix_getopt through its own copy of optind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant