Stream: wasmtime

Topic: Reading module from stdin


view this post on Zulip Nam Junghyun (Oct 05 2022 at 15:33):

Hi, I tried some bash tricks to execute piped webassembly binaries with wasmtime-cli 1.0.1:

> wasmtime target/wasm32-wasi/debug/hello.wasm
Hello, world!
> wasmtime <(cat target/wasm32-wasi/debug/hello.wasm)
Error: failed to run main module `/dev/fd/63`

Caused by:
    failed to parse `/dev/fd/63`: input bytes aren't valid utf-8

This happened because wasmtime CLI's run command opens and closes the module file before executing. It reads a 4-byte header to determine whether the module is precompiled or not. This is fine on regular files, but on piped file(or streams?) this pre-read 4 bytes will be lost so wasmtime seems to fail parsing the rest of the module. (see https://github.com/bytecodealliance/wasmtime/blob/e63771f2d921cdce440620dbf38bd3e186747b1e/src/commands/run.rs#L420 , and thanks to my friend for pointing it out ;D)
The same happens on wasmtime /dev/stdin.

Anyway, quite hack-ish, but this succeedes to run:

> wasmtime /dev/stdin < <(printf "0000"; cat target/wasm32-wasi/debug/hello.wasm)
Hello, world!

because I supplied the extra bytes to be discarded for the header check.

I know this hack should be discouraged as it breaks compatibility at all, so I want to implement a feature on wasmtime CLI about treating- as a stdin, e.g. curl -sSf https://example.com/hello.wasm | wasmtime - or wasmtime run -.

I look forward to this feature be very useful, as the widespread unix tool installation pattern curl {url} | bash - is considered bad in the view of security, AFAIK, and wasmtime can provide an extra sandboxing layer for this.

Does it make sense to wasmtime devs? Opinions appreciated. Thanks in advance.
(I searched wasmtime issue tracker with stdin/standard keyword but couldn't find any relevant issues. Please understand if someone already got this :pray:)

A fast and secure runtime for WebAssembly. Contribute to bytecodealliance/wasmtime development by creating an account on GitHub.

view this post on Zulip bjorn3 (Oct 05 2022 at 15:43):

so I want to implement a feature on wasmtime CLI about treating- as a stdin

makes sense to me

view this post on Zulip bjorn3 (Oct 05 2022 at 15:43):

so I want to implement a feature on wasmtime CLI about treating- as a stdin

makes sense to me

view this post on Zulip Dan Gohman (Oct 05 2022 at 15:45):

When running from the CLI, Wasmtime connects the host-process stdin to the wasm instance stdin. In this case, would we pass an empty string as stdin to the wasm instance?

view this post on Zulip Jamey Sharp (Oct 05 2022 at 17:54):

I don't think special-casing "-" is the most important part here. I'd prefer to avoid having wasmtime-cli open the input module twice, which would mean that /dev/stdin and /dev/fd/* would just work. At that point, special treatment for a filename of "-" would be a nice convenience.
I think the way to do this is to open the given path using MmapVec if allow_precompiled is true, but if that flag isn't provided or mmap fails, then read the file into a buffer. After that, operate only on &[u8]: check the buffer for the ELF header; if present and allow_precompiled, then pass the buffer to Module::deserialize; otherwise pass the buffer to Module::new.
@Dan Gohman, I think the answer is yes: if we've read stdin up to EOF, then the guest would immediately see EOF.

view this post on Zulip Nam Junghyun (Oct 06 2022 at 04:15):

I don't think special-casing "-" is the most important part here. (...)

I agree on that. Will try to follow your guidance to implement this. As you pointed out, should I create two separate PRs, each for buffering(not to open-close-reopen) and - sugar?

if we've read stdin up to EOF, then the guest would immediately see EOF.

IIUC this would make installation tools with prompts (like sh.rustup.rs) painful to use with wasmtime - as they'll get EOF without getting any user feedback .
Briefly read rustup-init.sh and the workaround seems like directly reading from /dev/tty, but as wasmtime denies file access unless explicitly allowed, there could be three options:

  1. Do nothing; programs targeting WASI and Wasmtime should handle stdin EOF case and try falling back to /dev/tty, and users should explicitly allow access to /dev/tty like curl <url> | wasmtime - --dir /dev/tty, which could be a little bit annoying for most users(but they would understand this is for the sake of security I think).
  2. Only add implicit --dir /dev/tty for - inputs, which is less vigilant approach on security perspective (a flag like --no-allow-implicit-tty` may be added)
  3. Automatically redirect /dev/tty to stdin for - inputs, which would be most aggressive approach but would allow exsiting WASI binaries run with piped inputs seamlessly.

Of course this may considered a separate topic/feature to no-module-reopening and - "sugar"s.

I brought some part of the rustup-init.sh code for reference here. $need_tty is set to "yes" by default and disabled only if user wants to skip with an additional flag:

    if [ "$need_tty" = "yes" ] && [ ! -t 0 ]; then
        # The installer is going to want to ask for confirmation by
        # reading stdin.  This script was piped into `sh` though and
        # doesn't have stdin to pass to its children. Instead we're going
        # to explicitly connect /dev/tty to the installer's stdin.
        if [ ! -t 1 ]; then
            err "Unable to run interactively. Run with -y to accept defaults, --help for additional options"
        fi

        ignore "$_file" "$@" < /dev/tty

view this post on Zulip Nam Junghyun (Oct 09 2022 at 11:10):

I implemented this feature on https://github.com/bytecodealliance/wasmtime/pull/5035.

This is not discussed in the issue tracker, but on this Zulip thread. Previously load_module opened a file on path to check if the module is precompiled, then closes before constructing the Module ...

view this post on Zulip Jamey Sharp (Oct 24 2022 at 19:32):

Nam Junghyun said:

if we've read stdin up to EOF, then the guest would immediately see EOF.

IIUC this would make installation tools with prompts (like sh.rustup.rs) painful to use with wasmtime - as they'll get EOF without getting any user feedback .
Briefly read rustup-init.sh and the workaround seems like directly reading from /dev/tty...

Another option is to change the idiom. On bash, something like wasmtime <(curl url) implicitly creates a pipe and binds it to a filename that's substituted into the command. Then the guest's stdin remains connected to the TTY.

view this post on Zulip Nam Junghyun (Oct 26 2022 at 14:31):

Jamey Sharp said:

Nam Junghyun said:

if we've read stdin up to EOF, then the guest would immediately see EOF.

IIUC this would make installation tools with prompts (like sh.rustup.rs) painful to use with wasmtime - as they'll get EOF without getting any user feedback .
Briefly read rustup-init.sh and the workaround seems like directly reading from /dev/tty...

Another option is to change the idiom. On bash, something like wasmtime <(curl url) implicitly creates a pipe and binds it to a filename that's substituted into the command. Then the guest's stdin remains connected to the TTY.

This seems to be a good alternative. Thanks!

view this post on Zulip Nam Junghyun (Nov 19 2022 at 00:48):

Some updates:

view this post on Zulip Jamey Sharp (Nov 19 2022 at 01:03):

Sounds like a reasonable plan. Let us know if you have any questions!


Last updated: Dec 23 2024 at 13:07 UTC