Repository navigation
Conversation
…ils) New opt-in `wasm-coreutils` feature: uutils/coreutils 0.12 compiled to wasm32-wasip1 as one multicall guest (new `bashkit-coreutils-wasm` crate, 71 utilities, precompiled to Pulley at build time), run as sandboxed builtins over the VFS. Registers `coreutils <util>` plus the utilities with no native builtin; `wasm_coreutils_replace_native` routes every guest utility to uutils. The CPython WASI host moves to `builtins/wasi_host/` and is shared: optional read-only overlay, HTTP state only with `cpython`, one run loop for pool slots, deadline/budget polling, exit and trap mapping. Adds integration/security tests, a criterion bench + startup example with saved results, knowledge (runtimes/wasm-coreutils, TM-WCU-*, L-WCU-*), rustdoc guide, CI and publish wiring.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
bashkit | 9f12889 | Commit Preview URL Branch Preview URL |
Nov 03 2026, 05:45 AM |
- Enforce the request work budget mid-run: guest fuel starts at the remaining work units, so a long call traps instead of being charged after. - Re-check deadline and cancellation every 50 ms while waiting for a pool slot or tenant share (unwoken waits ran past the deadline). - Bound host copies of guest data before making them: fd_write gathers only what the output cap or file size limit admits, oversized paths are refused unread, directory listings are cached and charged, /dev/null is a sink. - Map FS limit errors to EFBIG/ENOSPC/ENAMETOOLONG and loops to ELOOP. - Apply builtin_filter to guest utilities and the multicall; embedder builtins win; report `coreutils` as a script-analysis command wrapper. - Serve the virtual clock to guests (wall time and file mtimes), coarse under the Hardened profile; per-call monotonic time. - Cap guests per Bash with max_concurrent (default 4) before the shared pool. - Mask exit status to 8 bits; release reservations on fd exhaustion. - Guest: panic hook prints `<util>: internal error`; drop dd (thread spawn traps on wasip1).
…-8q1c3m # Conflicts: # knowledge/log.md
There was a problem hiding this comment.
Reviewed the wasm boundary (wasi_host move, wasm_coreutils runner, guest build, analysis/threat-model updates). Two findings below; everything else I probed (fuel/deadline metering, exit masking, env scoping, filter checks, embedder-wins install order, output/file caps) holds up.
| let from = c.data().resolve(fd1 as u32, &old_raw)?; | ||
| let to = c.data().resolve(fd2 as u32, &new_raw)?; | ||
| if c.data().is_stdlib(&from) || c.data().is_stdlib(&to) { | ||
| if c.data().is_overlay(&from) || c.data().is_overlay(&to) { |
There was a problem hiding this comment.
[P1] path_rename only blocks the exact overlay path, so renaming an overlay parent escapes the read-only overlay. E.g. with overlay at /data/in.txt, mv /data /other passes this check (from=/data != overlay) and moves the directory; afterwards /other/in.txt is an ordinary writable file (stale overlay_path still points at /data/in.txt) and the guest can overwrite formerly read-only bytes. Evidence: wasi.rs:1331 checks is_overlay(&from) || is_overlay(&to), while path_remove_directory (~1290) and path_create_directory check is_overlay_ancestor(&p) too. Fix: reject rename when from/to is the overlay or an ancestor of it (and when the overlay is an ancestor of from/to, i.e. either side is == overlay, an ancestor of the overlay, or has the overlay as an ancestor). Same gap exists in path_filestat_set_times (exact-only check) for mtime of overlay parents — worth the same treatment.
There was a problem hiding this comment.
Traced this and it does not reach the read-only bytes. The overlay is never a VFS file: GuestState.overlay holds the embedded &'static [u8], and path_open (~1693) and filestat (~695) serve it purely by comparing the resolved path with the fixed overlay_path. Renaming an ancestor directory moves only that directory's VFS entries. The overlay keeps answering at its original path, and /other/in.txt would not exist with overlay contents, so nothing writable ever carries those bytes. The same applies to path_filestat_set_times on an ancestor: it touches only a VFS directory's mtime. Only the cpython guest has an overlay (the stdlib zip); wasm-coreutils passes none. Leaving this as is: a guard here would add code without protecting anything.
Generated by Claude Code
Review summary (head
|
…-8q1c3m # Conflicts: # .github/workflows/publish.yml # crates/bashkit/Cargo.toml # crates/bashkit/src/builtins/cpython/mod.rs # knowledge/log.md # knowledge/operations/release-process.md
Requested by Михайло · project thread
What changed
wasm-coreutils(off by default). Bashkit can now run real uutils/coreutils 0.12 programs. The 70 utilities are compiled towasm32-wasip1, ahead-of-time compiled to Pulley inbuild.rs, and embedded in the binary.Bash::builder().wasm_coreutils()registers acoreutils <util> [args...]multicall (pluscoreutils --list) and fills in the 7 utilities bashkit has no native builtin for:csplit,dir,dircolors,pathchk,ptx,shred,vdir.wasm_coreutils_replace_native(limits)routes every embedded utility to the uutils program. It gives GNU-compatible output (for examplesort -h) and is useful for differential testing.builtins/wasi_host/and is used by both CPython and coreutils:cpython.bashkit-coreutils-wasmships the precompiled module. Its reproducible guest build lives inguest/(build.sh, pinned rustc 1.95.0,MANIFESTwith hashes).benches/wasm_coreutils.rscompares native and wasm and checks that their outputs match before timing.examples/wasm_coreutils_startup.rsplusjust bench-wasm-coreutilsmeasure fresh-process startup.knowledge/runtimes/wasm-coreutils.md, threat model TM-WCU-001..008, limitations L-WCU-001..004;docs/wasm-coreutils.md;Security audit and hardening
The WASI host was audited and every limit was attacked with hostile scripts. No sandbox escape was found. These gaps were found and fixed:
writevwith many iovecs over one bufferreaddir/dev/nullEIOEFBIG,ENOSPC,ENAMETOOLONG,ELOOPbuiltin_filtercoreutils rmand guest fill-ins bypassed itcoreutils rm -rf /hidrmcoreutilsis reported as a command wrapperfixed_epoch), 100 ms resolution under Hardenedmax_concurrentperBash(default 4), waited for within the deadlineddpanicked (thread spawn); Rust panic text could reach stderrdddropped from the guest; panic hook prints<util>: internal errorAccepted and documented: the build-time
BASHKIT_COREUTILS_CWASMoverride (TM-WCU-008), and CPython still reads the host clock (TODO inbuiltins/cpython/mod.rs).Why
Users want real, GNU-compatible programs instead of reimplementations. Bashkit also has to keep many tenants isolated in one process and start fast.
Native ELF binaries in-process (microsoft/litebox) were evaluated and rejected, for three reasons:
Wasm keeps the memory boundary in-process on every platform and compiles nothing at run time.
Before
After
Measured on a 4-vCPU x86-64 VM with a release build (
crates/bashkit/benches/results/criterion-wasm-coreutils-vm-linux-x86_64-1791511758.md):echo/cat/wc/sha256sum, small, warmsort, 100 linescat | sort -r | headcat1 MiBwc/sha256sum1 MiBsort1 MiBInstantiation takes about 9 µs.
About 97% of the per-call floor is uucore's localization setup:
trueruns about 451k guest instructions, and only about 11.5k of them are its own work. Skipping the setup cuts a call to 0.14 ms but breaks--helpand some error messages, so the setup stays. The follow-up is to pre-parse the Fluent bundles in a Wizer snapshot.Compute is 40-50x slower than native on Pulley. Native builtins therefore stay the default.
Risk
cargo test -p bashkit --features cpython,http_client --lib --test integration -- cpython wasi_hostgave 18 lib + 137 integration tests.bashkit-coreutils-wasm) and a publish job thatbashkitdepends on.Checklist
cargo fmt, clippy clean (default, cpython, wasm-coreutils, combined)just check-okf,check-doc-links, doc routes, scripts unittestscrates/bashkit/benches/results/