Skip to content

nvx-rs: kernel command-line checks split parameters differently from Linux #723

Open

Description

Summary

The host's checks for reserved parameters in the kernel command lines that nvx-rs builds, such as nvx_* and tsc=, split the command line at whitespace characters. Linux splits it at bytes, honors double quotes, and skips a parameter's leading quote. Byte 0xA0 occurs inside many UTF-8 characters, such as à (C3 A0), so a value can carry a parameter that the checks never see and that the kernel applies.

The host's caller is trusted (doc/design/1.4-trust-boundaries.md), so this is a validation bug rather than a vulnerability. But command lines cross a validation boundary, and invalid configurations must be refused rather than reach the guest.

How Linux splits the command line

In Linux 6.18, whose 6.18.38 release NVX's guests run:

  • next_arg skips a parameter's leading ", toggles quoting at every ", and ends the parameter at a byte for which isspace() is true outside quotes.
  • isspace() is true for bytes 0x09 to 0x0D, 0x20, and 0xA0.
  • parse_args stops at --, and leaves the rest of the command line to init.

What nvx-rs checks

  • nvx::host::launch::kernel_parameters_problem, which LaunchSpec::check and aci_edge_sandboxes apply to extra kernel parameters, splits them with str::split_whitespace, and refuses quotes, --, and nvx_*, tsc=, and hostname= tokens.
  • SandboxLaunch::kernel_command_line splits the --cmdline of nvx-rs sandbox with Python's str.split(), and refuses nvx_* and virtfs_* tokens. It accepts quotes, which the kernel honors, as in a="b c".
  • A sandbox's entrypoint, arguments, and mount targets, which become nvx_entrypoint=, nvx_arg=, and nvx_share= tokens, may not hold Python's whitespace characters, but may hold ", and byte 0xA0 inside a character.
  • In the guest, the time ABI's check of /proc/cmdline splits it at ASCII whitespace, and the guest init splits it at spaces, tabs, and newlines.

Reproduction

A program that calls the library at 8b585d7, and splits the command lines that it builds as next_arg does:

Input Verdict Parameters that the kernel sees
LaunchSpec parameters quietàtsc=unstable accepted quiet\xC3, tsc, hostname
LaunchSpec parameters quietà-- accepted quiet\xC3, --, hostname
LaunchSpec parameters quiet tsc=unstable refused
sandbox --cmdline quietànvx_entrypoint=/bin/other accepted quiet\xC3, nvx_entrypoint, nvx_sandbox, ..., nvx_entrypoint, nvx_hostname
sandbox --cmdline "nvx_entrypoint=/bin/other" accepted nvx_entrypoint, nvx_sandbox, ..., nvx_entrypoint, nvx_hostname
sandbox --cmdline quiet nvx_entrypoint=/bin/other refused
sandbox argument xàtsc=unstable accepted nvx_sandbox, ..., nvx_arg, tsc

validate_mount_target also accepts /data/àx and /data/a"b.

As a result:

  • The kernel applies parameters that LaunchSpec reserves, such as tsc=unstable, which time ABI v1 forbids, and the guest's check of the time ABI doesn't see them either.
  • A hidden -- ends the kernel's parameters, so the kernel no longer applies the hostname= parameter that NVX appends after the extra parameters.
  • The kernel splits a sandbox value that NVX meant as one token, and a " in one joins the tokens after it into one parameter, so the kernel and the guest see different tokens.

The last two follow from the kernel's source; they weren't observed in a guest.

Expected behavior

This is the behavior that #722 settles on:

  • Values that become one kernel token, a sandbox's entrypoint, arguments, and mount targets, refuse every byte that the kernel's isspace() accepts, and ".
  • Command lines that callers write, --cmdline and LaunchSpec::kernel_parameters, are split as the kernel splits them, leading quotes included, before reserved parameters are looked for.
  • The guest's check of the time ABI splits /proc/cmdline as the kernel does.
  • Tests derived from the kernel's rules: a model of next_arg in the tests, and properties that a value meant as one token is one parameter for the kernel, and that no reserved parameter passes, whatever separates it.
  • Docs: the option rules in doc/usage.md and doc/run.md, and the nvx::host::launch row of the README's module table.

Acceptance criteria

  • Every input that the reproduction accepts is refused, and so are the two mount targets.
  • Quoted values in --cmdline, such as a="b c" d, which tests/fixtures/cli.json records, are still accepted.

Activity

  1. added
    bugSomething isn't working
    documentationImprovements or additions to documentation
    confirmedIssue affects multiple people.
    and removed
    bugSomething isn't working
    documentationImprovements or additions to documentation
    on Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    confirmedIssue affects multiple people.

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions