webassembly.sh icon indicating copy to clipboard operation
webassembly.sh copied to clipboard

wasi fd_read sets bogous n_bytes_read value / ignores buffer length on webassembly.sh

Open jcaesar opened this issue 3 years ago • 2 comments

The following Rust code panics in webassembly.sh when compiled with rustc ≥ 1.57 (but runs fine on desktop wasmer).

use std::io::Read;
fn main() {
    let mut buffer = String::new();
    std::io::stdin().read_to_string(&mut buffer).unwrap();
}

Panic

thread 'main' panicked at 'assertion failed: n <= buf.len()', library/std/src/io/mod.rs:398:17
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Quick repro:

  • open
  • enter echo putatleastthirtytwobyteshereandyouwillsee | ./repro

The failing assert is here. I haven't really looked at how the surrounding code changed, but change it did.

Adding some more debug output to wasi, I get the following with 1.56 on desktop

[1653482124.970 DEBUG wasmer_wasi::state] Initializing WASI filesystem
[1653482124.972 DEBUG wasmer_wasi::syscalls] wasi::environ_sizes_get
[1653482124.972 DEBUG wasmer_wasi::syscalls] env_var_count: 0, env_buf_size: 0
[1653482124.972 DEBUG wasmer_wasi::syscalls] wasi::fd_read: fd=0
[1653482124.972 DEBUG wasmer_wasi::syscalls] wasi::read_bytes: buf=0x00102910 buf_len=8192 bytes_read=42
[1653482124.972 DEBUG wasmer_wasi::syscalls] wasi::fd_read: fd=0
[1653482124.973 DEBUG wasmer_wasi::syscalls] wasi::read_bytes: buf=0x00102910 buf_len=8192 bytes_read=0

and the following on 1.57

[1653482131.932 DEBUG wasmer_wasi::state] Initializing WASI filesystem
[1653482131.934 DEBUG wasmer_wasi::syscalls] wasi::environ_sizes_get
[1653482131.934 DEBUG wasmer_wasi::syscalls] env_var_count: 0, env_buf_size: 0
[1653482131.934 DEBUG wasmer_wasi::syscalls] wasi::fd_read: fd=0
[1653482131.934 DEBUG wasmer_wasi::syscalls] wasi::read_bytes: buf=0x001048b0 buf_len=32 bytes_read=32
[1653482131.934 DEBUG wasmer_wasi::syscalls] wasi::fd_read: fd=0
[1653482131.934 DEBUG wasmer_wasi::syscalls] wasi::read_bytes: buf=0x001048d0 buf_len=32 bytes_read=10
[1653482131.934 DEBUG wasmer_wasi::syscalls] wasi::fd_read: fd=0
[1653482131.934 DEBUG wasmer_wasi::syscalls] wasi::read_bytes: buf=0x001048da buf_len=22 bytes_read=0

so I guess the initial buffer size changed and webassembly.sh somehow manages to return the wrong value for bytes_read (maybe 42?).

jcaesar avatar May 25 '22 12:05 jcaesar

Actually, nevermind this being about rust ≥1.57, this occurs with all rust versions for large enough inputs:

$ curl https://gist.githubusercontent.com/jcaesar/c33e0759fc120cfe964a16e0b3783379/raw/4d67aafbaaa9cccf4dc274b9934cf84b6bab25c7/1.56.wasm -o repro
$ sl-cmd '"asdf"*3000' | ./repro
thread 'main' panicked at 'range end index 12003 out of range for slice of length 8192', library/std/src/io/buffered/bufreader.rs:325:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

This is also happening with other languages/compilers/system libs, they just have less strict checks and probably do some undefined behavior memory access… Nice.

$ sl-cmd '"asdf"*3000' | hexdump -C
00000000  22 61 73 64 66 61 73 64  66 61 73 64 66 61 73 00  |"asdfasdfasdfas.|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000410  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 2a  |...............*|
00000420  30 30 30 30 34 31 30 20  20 30 30 20 30 30 20 30  |0000410  00 00 0|
00000430  30 20 33 30 20 33 34 20  33 31 20 33 30 20 32 30  |0 30 34 31 30 20|
00000440  20 20 33 33 20 33 31 20  32 30 20 33 33 20 33 30  |  33 31 20 33 30|
00000450  20 32 30 20 33 33 20 33  30 20 20 7c 20 20 33 33  | 20 33 30  |  33|
00000460  33 33 20 33 30 20 20 7c  20 20 33 33 00 00 00 00  |33 30  |  33....|
00000470  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000820  00 00 00 00 00 00 00 43  2e 55 54 46 2d 38 3b 43  |.......C.UTF-8;C|
00000830  3b 43 3b 43 3b 43 3b 43  00 00 00 00 00 00 00 00  |;C;C;C;C........|
00000840  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*

jcaesar avatar May 27 '22 07:05 jcaesar

I made a little test file to check whether a value directly after the buffer is overwritten, i.e. whether the just the returned n_bytes is wrong, or whether it ignores the input length.

Seems only the returned value is wrong, the canary is left intact:

$ curl https://gist.githubusercontent.com/jcaesar/c33e0759fc120cfe964a16e0b3783379/raw/0c44e77e0118ab1e6781e802c377f574da2c907a/test.wasm -o test
$ sl-cmd '"asdf" * 20' | ./test
fd_read: ret 0, nbytes = 83, canary 739477648 = 739477648
fd_read: ret 0, nbytes = 0, canary 739477648 = 739477648
fail = 1

Other than that, this only happens when piping. But I haven't found the code for piping in wasm-terminal, so no idea what's going on.

jcaesar avatar May 29 '22 04:05 jcaesar