Repository navigation
openvmm: refuse kernel command lines that the guest kernel would truncate - #661
Merged
Pedro Henrique Penna (ppenna) merged 1 commit intoOct 21, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
The review-only OpenVMM revision still requires upstream promotion, repinning, equivalence verification, and final CI validation before merge.
No open issues 🎉
What changed in this PR
Adds NVX regression coverage for OpenVMM’s guest-kernel command-line limit, addressing #570 without changing launcher budgets.
Changes:
- Tests successful boot at 2,047 bytes and rejection at 2,048 bytes.
- Verifies the complete guest command line and IPv6 default route.
- Adds unit coverage and CI documentation.
| File | Description |
|---|---|
| scripts/test_microvm_tests.py | Tests scenario commands, validation, and dispatch. |
| scripts/nvx_tools/microvm_tests.py | Adds the default command-line boundary scenario. |
| scripts/nvx_tools/microvm_test_scripts/command-line-limit.sh | Reports the command line and checks the IPv6 route. |
| doc/ci.md | Documents boundary-test behavior. |
Review effort: Balanced
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a4-command-line-limit-20261020
branch
from
October 20, 2026 21:20
7758db4 to
d12fd4e
Compare
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a4-command-line-limit-20261020
branch
from
October 20, 2026 21:41
d12fd4e to
c3d2cb8
Compare
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a4-command-line-limit-20261020
branch
from
October 20, 2026 22:02
c3d2cb8 to
6df67ba
Compare
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a4-command-line-limit-20261020
branch
from
October 20, 2026 22:02
6df67ba to
bddf5b0
Compare
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a4-command-line-limit-20261020
branch
from
October 20, 2026 22:43
bddf5b0 to
ec9a0ec
Compare
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a4-command-line-limit-20261020
branch
from
October 20, 2026 23:49
ec9a0ec to
7f7e963
Compare
Enrique Saurez (esaurez)
marked this pull request as ready for review
October 21, 2026 00:38
…cate Part of #622. Part of #570. Pin nanvix/openvmm#138 at 9f04d05c5441a9b31f7f81effa0ce7c4fc66a0d3, six commits on d0bdbed8. The x86 guest kernel keeps at most 2,048 bytes of the boot command line, including the terminating NUL, and silently drops the rest. OpenVMM accepted effective microVM command lines of up to 64 KiB, and it appends its own tokens after the caller's, so the machine profile's tokens were the first to be lost. OpenVMM now refuses, before boot, an effective command line longer than 2,047 bytes or one with an embedded NUL, with an error that names the length and the limit. A restore refuses a saved command line over the limit instead of rewriting it. Add the command-line-limit microVM scenario. With a NIC attached, it boots once with a one-byte padding token, a module parameter that the kernel passes to neither init nor its environment, to learn the effective command line from the guest. It then pads the caller's tokens until the effective command line holds 2,047 bytes, and checks that the guest's /proc/cmdline holds all of it and that the guest installed the IPv6 default route that the last token, virtnet_gw6=, names. OpenVMM must refuse a command line one byte longer. Describe the scenario in the CI guide. Every NVX launcher fits. nvx-rs keeps its 1,024-byte caller budget and its early refusals, and the worst-case effective command lines of its managed launches and sandboxes, and of nvx.py run with an aggregate share, stay more than 300 bytes under the limit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 54816c68-c749-4008-8ae9-b3da19544730
Pedro Henrique Penna (ppenna)
force-pushed
the
esaurez/a4-command-line-limit-20261020
branch
from
October 21, 2026 15:24
7f7e963 to
494ee98
Compare
Pedro Henrique Penna (ppenna)
approved these changes
Oct 21, 2026
Pedro Henrique Penna (ppenna)
deleted the
esaurez/a4-command-line-limit-20261020
branch
October 21, 2026 19:05
This was referenced Oct 21, 2026
Merged
Pedro Henrique Penna (ppenna)
added a commit
that referenced
this pull request
Oct 22, 2026
The default Alpine suite of test-microvm runs its 36 scenarios one after another: 457.6 seconds on KVM, 416.4 on MSHV, and 596.3 on WHP, as the medians of the scenario duration reports (#694) of the 13 CI runs of October 21 and 22 add up. On WHP its job bounds every run that skips both OpenVMM suites (#696). No scenario depends on another, so CI can run the suite in parallel shards, as long as every scenario runs in exactly one of them and the shards take about as long as each other, whatever number of scenarios each runs. scripts/nvx_tools/microvm_shards.json is that partition: for each backend, the median duration of every default scenario, the CI runs whose reports gave them, and each shard's scenarios with their total. The backends differ too much for one partition: console-snapshot takes 49 seconds on KVM but 1 on MSHV and 4 on WHP, and console-exit 25 on KVM but 87 on WHP. The best partition common to all three that a search found left the longest KVM and MSHV shards about 5% above an even split, where one per backend balances each to within a second, so each backend has its own. The new plan-microvm-shards command writes the file. It takes each backend's median of every default scenario over the reports that it is given, sharded or not, of the Alpine guest on the default kernel with the default processor counts, from runs that ended; a report counts for a scenario only when every execution of it passed. Each scenario starts in its current shard, and one without a shard goes, longest first, to the shortest. A shard that is still empty, as one that --shards adds can be, takes the longest scenario of the longest shard that has more than one, since moving a scenario that takes no time into it would shorten no shard. The plan then moves a scenario, or swaps two of different shards, while that shortens the longest shard by a second or more, so that new medians that move within the run-to-run noise leave the shards alone. A scenario that no report measures keeps its duration, or takes an estimate from --seconds, and --shards changes a backend's number of shards. The first plan started from the two shards that #696 proposed, the restore, snapshot, and time ABI scenarios and the network, filesystem, and lifecycle ones, which were up to 18% apart on KVM, and a single swap per backend balanced each: 228.9 and 228.7 seconds on KVM, 208.0 and 208.4 on MSHV, and 298.3 and 298.0 on WHP. test-microvm --shard INDEX/COUNT runs one shard of the default suite. It fails before it runs a scenario unless the file puts every default scenario in exactly one shard of every backend and splits the backend's suite into COUNT shards. A scenario that a later change adds, as #661 added command-line-limit, therefore cannot drop out of a sharded run, and a run that counts a different number of shards than the file fails. The option takes no --scenario, --debug-kernel, or other guest. Only the microVM tests use the new module and its data, so the classifier leaves them out of the platform jobs' inputs, and doc/ci.md says so. New tests check that the checked-in file puts each default scenario in exactly one shard of each backend and is the planner's own plan of its durations, and that the shards of each backend together run the default suite that an unsharded run selects, each scenario once. They check that --shard fails before any scenario runs for a new, removed, doubled, or missing scenario, an empty shard, or a count that differs from the file's, and that it rejects other selections. They also cover the planner's balancing, its threshold, its placement of new scenarios and added shards, which it fills even with scenarios that take no time, its determinism, which reports it counts, the command's options and errors, and malformed files. The classification tests cover the new files. doc/usage.md documents the command and the option, and doc/ci.md how the shards are planned and how to plan them again. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna)
added a commit
that referenced
this pull request
Oct 22, 2026
The default Alpine suite of test-microvm runs its 36 scenarios one after another: 457.7 seconds on KVM, 416.0 on MSHV, and 601.5 on WHP, as the medians of the scenario duration reports (#694) of the 18 CI runs of October 21 and 22 that the partition lists add up. On WHP its job bounds every run that skips both OpenVMM suites (#696). No scenario depends on another, so CI can run the suite in parallel shards, as long as every scenario runs in exactly one of them and the shards take about as long as each other, whatever number of scenarios each runs. scripts/nvx_tools/microvm_shards.json is that partition: for each backend, the median duration of every default scenario, the CI runs whose reports gave them, and each shard's scenarios with their total. The backends differ too much for one partition: console-snapshot takes 49 seconds on KVM but 1 on MSHV and 4 on WHP, and console-exit 25 on KVM but 87.5 on WHP. The best partition common to all three that a search found left the longest KVM and MSHV shards about 5% above an even split, where one per backend balances each to within a second, so each backend has its own. The new plan-microvm-shards command writes the file. It takes each backend's median of every default scenario over the reports that it is given, sharded or not, of the Alpine guest on the default kernel with the default processor counts, from runs that ended; a report counts for a scenario only when every execution of it passed. Each scenario starts in its current shard, and one without a shard goes, longest first, to the shortest. A shard that is still empty, as one that --shards adds can be, takes the longest scenario of the longest shard that has more than one, since moving a scenario that takes no time into it would shorten no shard. The plan then moves a scenario, or swaps two of different shards, while that shortens the longest shard by a second or more, so that new medians that move within the run-to-run noise leave the shards alone. A scenario that no report measures keeps its duration, or takes an estimate from --seconds, and --shards changes a backend's number of shards. The first plan, from 13 of the runs, started from the two shards that #696 proposed, the restore, snapshot, and time ABI scenarios and the network, filesystem, and lifecycle ones, which were up to 18% apart on KVM, and a single swap per backend balanced each. Planned again from all 18, which add two later dev pushes, another pull request's run, and two runs of this change, every scenario stayed in its shard: 228.8 and 228.9 seconds on KVM, 208.0 and 208.0 on MSHV, and 300.4 and 301.1 on WHP. test-microvm --shard INDEX/COUNT runs one shard of the default suite. It fails before it runs a scenario unless the file puts every default scenario in exactly one shard of every backend and splits the backend's suite into COUNT shards. A scenario that a later change adds, as #661 added command-line-limit, therefore cannot drop out of a sharded run, and a run that counts a different number of shards than the file fails. The option takes no --scenario, --debug-kernel, or other guest. Only the microVM tests use the new module and its data, so the classifier leaves them out of the platform jobs' inputs, and doc/ci.md says so. New tests check that the checked-in file puts each default scenario in exactly one shard of each backend and is the planner's own plan of its durations, and that the shards of each backend together run the default suite that an unsharded run selects, each scenario once. They check that --shard fails before any scenario runs for a new, removed, doubled, or missing scenario, an empty shard, or a count that differs from the file's, and that it rejects other selections. They also cover the planner's balancing, its threshold, its placement of new scenarios and added shards, which it fills even with scenarios that take no time, its determinism, which reports it counts, the command's options and errors, and malformed files. The classification tests cover the new files. doc/usage.md documents the command and the option, and doc/ci.md how the shards are planned and how to plan them again. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
The
openvmmgitlink points at9f04d05c5441a9b31f7f81effa0ce7c4fc66a0d3, the head of nanvix/openvmm#138. Before this PR merged,nanvix/openvmm
mainand microsoft/openvmmfeature/microvm/devfast-forwarded to it fromd0bdbed8, so it needed no repin.The x86 guest kernel keeps at most 2,048 bytes of the boot command line, including the terminating NUL, and silently drops the rest. OpenVMM appends the machine profile's own tokens after the caller's, so they were the first to be lost, while OpenVMM accepted effective command lines of up to 64 KiB (#570).
What changed
microVM kernel command line is 2048 bytes; the limit is 2047 bytes plus the terminating NUL. One shared check replaces the profile's and the snapshot contract's 64-KiB limits, and a restore refuses a saved command line over the limit instead of rewriting it.command-line-limitmicroVM scenario, in the defaulttest-microvmset that CI runs on every backend. With a NIC attached, it boots Alpine once with a one-byte padding token,cmdline_limit.padding=x, a module parameter that the kernel passes to neither init nor its environment, to learn the effective command line from the guest. It then pads the caller's tokens until the effective command line holds 2,047 bytes, and checks that the guest's/proc/cmdlineholds all of it, token for token, and that the guest installed the IPv6 default route that the profile's last token,virtnet_gw6=, names. OpenVMM must refuse a command line one byte longer, before boot and with its error. Unit tests cover the scenario's command, guest script, and dispatch, anddoc/ci.mddescribes it.COMMAND_LINE_BUDGET) and its early refusals, including of embedded NULs, and the CLI and launch goldens are unchanged.Every NVX launcher fits
launch.rs, whichaci_edge_sandboxesalso uses): the 1,024-byte caller budget, OpenVMM's tokens, and the fixed 85-byte virtio-fs fragmentnvx.py runwith an aggregate shareOpenVMM's own tokens add at most 43 bytes of base tokens, 10 for
nr_cpus=8, 56 for the workload identity, 23 for the lifecycle, 38 for the snapshot tier, and 336 for device discovery, of which the network bootstrap takes up to 201.nvx.py runwith a single directory share or a plain--cmdlineforwards the caller's tokens without a budget of its own, as before; OpenVMM is the authority there, and a command line that does not fit now fails before boot with OpenVMM's error instead of booting truncated.The boot protocol's design text,
doc/design/5.1-boot-protocol.md, still states 64 KiB; draft #591 rewrites that paragraph to the 2,048-byte rule, so this PR leaves it alone.Part of #622.
Part of #570.
OpenVMM revision
esaurez/microvm-command-line-limit-202610209f04d05c5441a9b31f7f81effa0ce7c4fc66a0d3, six commits that the nanvix/openvmm maintainer signed, directly ond0bdbed8f6e3b86271277dc22658109e2e633b2d, the commit of microsoft/openvmmfeature/microvm/devthatdevpins:f894fbdd1, the change, and five test-only commits from review,88d4832ba,452ab29a4,e6df0c3ea,d0a1b820c, and9f04d05c5, which derive the tests' expected errors, lengths, and effective command lines from the shared check, the limit, and the profile's own token functionsfee2eec7e,03171584c,c4bb0256b,c3c61d184,3eebb3976, and2a9556f3e, in order, on the same base, with the maintainer's signature. Each keeps its original's tree, author, author date, and message, andgit range-diffreports all six as identical, so the pinned tree,be9604c40855174ee0116bbd84f4db264b91624e, is byte-for-byte that of2a9556f3e, and the validation below, which names the original commits, holds for the trees of their re-created counterpartsValidation
The runtime runs used OpenVMM built from
fee2eec7e;03171584c,c4bb0256b,c3c61d184,3eebb3976, and2a9556f3echange only OpenVMM tests, which pass on Windows and Linux (nanvix/openvmm#138). Most NVX runs useda008a92f, this patch onf7882746, beforedevmoved. Atec9a0ecc, the same patch pinned to3eebb3976on401c95a3, whose newer commits change nvx-rs's guest spawn (#629), the adversarial executor (#657), and performance-baseline data, none of which this patch or its scenario uses (the scenario starts OpenVMM directly), I reranverify, the quality checks, the NVX CLI tests, the nvx-rs checks, and the new scenario on WHP. The previous head,7f7e963b, differed fromec9a0ecconly in2a9556f3e, a test-only OpenVMM commit, and two performance-baseline data commits ofdev; at it I reranverifyandtest_performance.py. The last paragraph covers this head.On Windows 11 (Windows/WHP):
python scripts\nvx.py verifypython scripts\nvx.py build-openvmmat the pin, thenpython scripts\nvx.py test-microvm --backend whp: the full default set, includingcommand-line-limit, passes with the guest artifacts ofv0.1.0-dev.e0398adc52afand nvx-rs built from the checkout;command-line-limitalso passes with--guest ubuntuand--guest azurelinux, and again atec9a0ecctest_hosts.pypasses; the four process-containment tests oftest_adversarial.pythat fail on this host fail identically ondev, and of its other 73 tests, 67 pass and 6 are skippedcargo fmt --all --check: cleanlxutil tests::stat_fsfails on this host without the change too), its doc tests, and the 29 VMM tests oftest-openvmm --backend whpOn Linux/MSHV (Azure Linux 3.0, a nested virtual machine), with OpenVMM built by
nvx.py build-openvmm --backend mshvat the pin:NVX_BUNDLE=skip, atec9a0ecc: both Clippy runs with-D warnings,cargo test --locked --all-features,cargo test --locked, andcargo docwith-D warningscommand-line-limit,guest-boot,virtio-net,snapshot-core, andnetwork-snapshotpass. This host cannot present the built-in CPU profile that--cpu-profile autoselects (amd.genoa.v1needstsa_l1_no_supportandtsa_sq_no_support), andtest-microvmtakes no profile, so they ran with OpenVMM wrapped to add its development--cpu-profile hostOn Linux/KVM (WSL2 Ubuntu 22.04, nested), with the pin's static musl OpenVMM:
command-line-limit,guest-boot,virtio-net,network-snapshot, andsnapshot-corepass on--cpu-profile host, for the same reason (intel.alderlake.v1needs MSR0x10abits that this KVM does not expose);snapshot-corefailed once with a 2-second clock skew under host load, then passed twice, as it did twice withdev's OpenVMMtest_microvm_tests.pywithNVX_RSnaming an nvx-rs built from the checkout;test_adversarial.py'ssigkillcontainment case fails here as it does ondevIn each backend's
command-line-limitrun, the guest's/proc/cmdlineheld exactly 2,047 bytes ending withvirtnet_gw6=fd00::a00:1, and OpenVMM refused the 2,048-byte command line before boot with its error.Left to CI: the full default
test-microvmset on KVM and MSHV with--cpu-profile auto; the other guests', debug-kernel, and public sandbox sets;test-openvmmon KVM and MSHV; the Linux OpenVMM unit tests; the guest artifact builds; and the benchmarks.This head,
494ee980, re-creates7f7e963bondevatf2e7c984, with the pin moved to the re-signed9f04d05c5. Its patch matches7f7e963b's apart from the gitlink and the pin line of its message, anddoc/ci.mdandscripts/test_microvm_tests.pymerge cleanly withdev's newer changes to them. On Windows 11, at this head,verifyandtest_performance.pypass. Atecf7dca7, the same commit on5418be9a, whose tree differs from this head's only indev's performance-baseline data, ruff check and format,compileall, pyright for Linux and Windows, and the Markdown link check are clean, and validate-nvx's unittest modules ran 1,090 tests, 35 of them skipped. Their only failures also fail ondevon this host: the same 18 tests oftest_setup_azure.py, and a 10-second timeout of Git for Windows'sh.exeintest_host_loopback_allow_script_checks_publication_and_traffic, which failed in two of three runs ondevas well.NVX head submitted to CI:
494ee980a70b31dcf09e1417d0188c0a7ce2d661.