Repository navigation
wasip2: build uucore and env standalone, don't trap on empty argv - #14562
Aditya1404Sal wants to merge 15 commits into
Conversation
…encoded-bytes conversions)
b7b4796 to
c4058d7
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Add WASI Preview 2 coverage and regression tests for the new conversion and empty-argv paths.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Enables standalone wasm32-wasip2 builds for uucore and env, while preventing empty-WASI-argv traps.
Changes:
- Enables
rustix/stdforuucore. - Adds WASI Preview 2 encoded-byte conversions for
env. - Restores
envto common/WASI features and adds an empty-argv fallback.
File summaries
| File | Description |
|---|---|
src/uucore/src/lib/lib.rs |
Prevents empty-argv indexing on WASI. |
src/uucore/Cargo.toml |
Enables required rustix standard-library APIs. |
src/uu/env/src/native_int_str.rs |
Adds WASI Preview 2 string conversions. |
Cargo.toml |
Includes env in common/WASI features. |
Review details
Suppressed comments (1)
Cargo.toml:140
- This move does not provide the wasip2 CI coverage claimed in the PR description:
.github/workflows/wasi.ymlgates thefeat_wasmbuild to wasip1 and excludesenvfrom the wasip2 package-test list. Add a wasip2 compile/test step or correct the description before relying on this feature move for regression protection.
"env",
- Files reviewed: 4/4 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
please address what copilot said |
Run env encoded-byte conversion tests under wasm32-wasip2, and factor the WASI argv fallback so its empty-input behavior is covered on host and target tests.
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
Review effort: Lite
Findings: 1
Open (3)
| "dircolors", | ||
| "dirname", | ||
| "echo", | ||
| "env", |
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
Review effort: Lite
Findings: 1
Open (3)
|
i am sorry but nobody will read such a long comment 0 |
| // On wasm32-wasip2 `std::os::wasi::ffi` is behind the unstable `wasip2` library feature, so the | ||
| // byte views come from the stable encoded-bytes API instead. The component model defines every | ||
| // string the host hands a guest as UTF-8, so on this target the encoded bytes *are* the bytes, and | ||
| // the reverse direction can go through `str` without `unsafe` — the same approach as | ||
| // `uucore::os_str_from_bytes` / `os_string_from_vec` on this target. |
There was a problem hiding this comment.
please make this comment shorter
| argv | ||
| } | ||
|
|
||
| // `std::env::args_os()` can be empty on wasi when the component is not invoked as a CLI |
There was a problem hiding this comment.
please make this comment shorter
|
GNU testsuite comparison: |
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
Review effort: Lite
Findings: 2
Open (4)
The PR description says commit 1 enables thestdfeature on uucore's optionalrustix… · New The first commit described in the PR—enablingrustix/stdfor the optionaluucoredependency—is… Please add a regression test for an empty WASI argv. This branch is the only protection against the… The new wasip2 conversion path is not exercised by the current tests:envis excluded from the…
| "dircolors", | ||
| "dirname", | ||
| "echo", | ||
| "env", |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The new Preview 2 CI step does not exercise the standalone uucore --features fs build that motivated the fix.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 2
Open (3)
| run: | | ||
| cargo test --target wasm32-wasip2 --no-default-features -p uu_env | ||
| cargo check --target wasm32-wasip2 --no-default-features -p uucore --features fs | ||
| cargo test --target wasm32-wasip2 --no-default-features -p uucore --lib \ |
There was a problem hiding this comment.
a dedicated CI step to run one test on a 4 lines helper seems a lot, the cargo check lines are the useful part here, no?
| match String::from_utf8(vec) { | ||
| Ok(s) => s.into(), | ||
| // Cannot have come from the host; keep going rather than abort the component. | ||
| Err(e) => String::from_utf8_lossy(e.as_bytes()).into_owned().into(), |
There was a problem hiding this comment.
this silently replaces bytes with U+FFFD, so env would hand a corrupted value to the child. i'd rather keep the bytes than mangle them. also the comment doesn't really explain the why.
| #[cfg(any(test, target_os = "wasi"))] | ||
| fn with_wasi_argv_fallback(mut argv: Vec<OsString>) -> Vec<OsString> { | ||
| if argv.is_empty() { | ||
| argv.push(OsString::from("uu")); |
There was a problem hiding this comment.
why "uu"? it shows up in error messages and --version, "coreutils" would be less cryptic.
Merging this PR will regress 1 benchmark
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Simulation | factor_five_38_bit_primes |
1.8 s | 2.1 s | -14.88% |
| ⚡ | Simulation | factor_three_39_bit_primes |
942.2 ms | 529.5 ms | +77.97% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing Aditya1404Sal:wasip2-fixes (b234415) with main (774cca5)2
Footnotes
-
70 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
-
No successful run was found on
main(fb68e6c) during the generation of this report, so 774cca5 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩
eduardomourar
left a comment
There was a problem hiding this comment.
Just very small suggestions.
| static UTIL_NAME: LazyLock<String> = LazyLock::new(|| { | ||
| let base_index = usize::from(get_utility_is_second_arg()); | ||
| // `ARGV` may be shorter than the multicall layout assumes (e.g. the WASI fallback). | ||
| let last = ARGV.len().saturating_sub(1); | ||
| let base_index = usize::from(get_utility_is_second_arg()).min(last); | ||
| let is_man = usize::from(ARGV[base_index].eq("manpage")); | ||
| let argv_index = base_index + is_man; | ||
| let argv_index = (base_index + is_man).min(last); |
There was a problem hiding this comment.
Previous fixes to this indexing (e.g. 6699eaad6) corrected the arithmetic to be exactly right rather than clamping around it. The .min(last) here means that if get_utility_is_second_arg() is true but ARGV only has one element (the WASI empty-argv fallback), base_index silently falls back to 0 instead of the intended 1 — i.e. a wrong-but-plausible value instead of a panic.
In the current call graph this looks unreachable: coreutils.rs's multicall path only sets get_utility_is_second_arg() after consuming an argv entry, so by the time it's true, ARGV already has ≥2 elements and the clamp is a no-op. But that's an invariant of the caller, not something enforced where the clamp lives — if that assumption ever changes, this would silently produce a misleading utility name instead of failing loudly. Would a comment noting why the clamp is always a no-op in practice (or asserting it) be worth adding here?
There was a problem hiding this comment.
Went one step further and removed the clamp instead of documenting it, since it was dead code. UTIL_NAME is only read inside a utility's uumain, which the multicall path reaches only after consuming argv[1]; with the flag set and no second argument it exits with "missing argument" without reading the name. The indexing is back to upstream's exact arithmetic.
| // args_os() can be expensive to call, it copies all of argv before iterating. | ||
| // So if we want only the first arg or so it's overkill. We cache it. | ||
| #[cfg(windows)] | ||
| static ARGV: LazyLock<Vec<OsString>> = LazyLock::new(|| wild::args_os().collect()); | ||
| #[cfg(not(windows))] | ||
| #[cfg(all(not(windows), not(target_os = "wasi")))] | ||
| static ARGV: LazyLock<Vec<OsString>> = LazyLock::new(|| std::env::args_os().collect()); | ||
|
|
||
| #[cfg(any(test, target_os = "wasi"))] | ||
| fn with_wasi_argv_fallback(mut argv: Vec<OsString>) -> Vec<OsString> { | ||
| if argv.is_empty() { | ||
| argv.push(OsString::from("coreutils")); | ||
| } | ||
| argv | ||
| } | ||
|
|
||
| // WASI embeddings may omit argv; provide a stable utility-name fallback. | ||
| #[cfg(all(not(windows), target_os = "wasi"))] | ||
| static ARGV: LazyLock<Vec<OsString>> = | ||
| LazyLock::new(|| with_wasi_argv_fallback(std::env::args_os().collect())); |
There was a problem hiding this comment.
with_wasi_argv_fallback guarantees ARGV is never empty on target_os = "wasi", which is what makes the existing unguarded ARGV[0] access in EXECUTION_PHRASE (L423-L433) safe on WASI too — a nice side benefit. But that "ARGV is always non-empty" guarantee isn't stated anywhere near the ARGV static itself, so a future change to with_wasi_argv_fallback (or a new platform cfg arm) could reintroduce the panic in EXECUTION_PHRASE or UTIL_NAME without anything pointing back at this invariant. A one-line comment on the ARGV static ("always non-empty — relied on by UTIL_NAME/EXECUTION_PHRASE") would make that dependency explicit for the next person touching this code.
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
6 open findings
with_wasi_argv_fallbackguaranteesARGVis non-empty, butUTIL_NAMEstill indexes… · New The PR description says commit 1 enables thestdfeature on uucore's optionalrustix… The first commit described in the PR—enablingrustix/stdfor the optionaluucoredependency—is… The Preview 2 path is selected vianot(target_env = \"p1\"), which will also match any WASI… · New The Preview 2 path is selected vianot(target_env = \"p1\"), which will also match any WASI… · New The fallback value\"coreutils\"is a hard-coded magic string that becomes observable in… · New
🧠 Review effort: Lite
| } | ||
|
|
||
| // Always non-empty on WASI (embeddings may omit argv); `UTIL_NAME`/`EXECUTION_PHRASE` rely on it. | ||
| #[cfg(all(not(windows), target_os = "wasi"))] |
There was a problem hiding this comment.
not(windows) is redundant here, target_os = "wasi" is enough
can be done in a different PR
There was a problem hiding this comment.
🟢 Approval recommended
The targeted changes have regression tests and WASI CI coverage, with no unresolved findings.
2 open findings
4 resolved since last review
with_wasi_argv_fallbackguaranteesARGVis non-empty, butUTIL_NAMEstill indexes… The fallback value\"coreutils\"is a hard-coded magic string that becomes observable in… The Preview 2 path is selected vianot(target_env = \"p1\"), which will also match any WASI… The Preview 2 path is selected vianot(target_env = \"p1\"), which will also match any WASI…
🧠 Review effort: Lite
|
|
||
| // `std::os::wasi::ffi` is still unstable on WASI Preview 2, so provide the same traits here. | ||
| // On WASI an `OsStr` is a plain byte string, so these conversions keep every byte. | ||
| #[cfg(all(target_os = "wasi", not(target_env = "p1")))] |
There was a problem hiding this comment.
uucore's os_str_from_bytes/os_string_from_vec still reject non-UTF-8 on p2, so env and uucore now disagree.
could this shim live in uucore so both use it? can be done in a different PR
There was a problem hiding this comment.
thats a fair point, I can migrate that in a follow-up PR very soon (today?)



Fixes #14561.
Use stable byte conversions for
envon WASI Preview 2 and restore it tofeat_common_core. Handle empty WASI argv in embedded use without panicking, with regression tests and a Preview 2 CI step.Upstream commit 17f1a5d already enables
rustix/stdthroughuucore'sfsfeature, so this PR no longer needs a dependency change.Verified native
uucore/envtests, Preview 2 conversion and argv tests,feat_wasmchecks for both WASI targets, and a standalone Preview 2uucore --features fscheck.