wasmerio/wasmer

(WASI) Wrong file offset after file write in append mode

Closed this issue · 1 comments

Describe the bug

echo "`wasmer -V` | `rustc -V` | `uname -m`"
wasmer 4.2.5 | rustc 1.76.0 (07dca489a 2024-02-04) | x86_64

Compiling a Rust test case to Wasm and running it with Wasmer gives a different result than when compiling to native code (x86 assembly). Other Wasm runtimes (Wasmtime, WasmEdge, WAMR, and Wasmi) are consistent with x86.

The test case (shown below) creates a file in append mode, writes an arbitrary string, seeks to the beginning of the file, writes another string, and then outputs the file offset. Wasmer outputs a different file offset than the other targets/runtimes.

use std::fs::OpenOptions;
use std::io::prelude::*;
use std::io::SeekFrom;

fn main() {
    let mut fp = OpenOptions::new()
        .append(true)
        .read(true)
        .create(true)
        .open("file")
        .unwrap();

    write!(fp, "{}", "a").unwrap();
    let _ = fp.rewind(); // seek to start of the file
    write!(fp, "{}", "b").unwrap();
    println!("{}", fp.seek(SeekFrom::Current(0)).unwrap()); // output file offset
}

Steps to reproduce

Here is a zipped Rust project that reproduces the bug:
wasmer-seek-append-bug.zip

  1. Unzip wasmer-seek-append-bug.zip
  2. Go to wasmer-seek-append-bug
  3. Compile with cargo build ; cargo build --target wasm32-wasi
  4. Run the test case with ./verify.sh
  5. Observe that Wasmer and the default target yield different outputs

My default target is x86_64-unknown-linux-gnu, and I get the following output:

Wasmer ==> 1
Default ==> 2

Expected behavior

The outputs should be the same.

Actual behavior

The outputs differ.

Note that running the test case with other Wasm runtimes yields 2.

The targets have the same behavior after the first file write and file seek i.e. both successfully write the string a to file and then set the file offset to 0.

The targets diverge after the second file write. They both write b to file, but the file offsets are updated differently. Wasmer updates the file offset to 1, which points to the b character that was just written. The other targets update the file offset to 2, or the end of the file.

The 2018 edition of POSIX states the following about file writes in append mode: "If the O_APPEND flag of the file status flags is set, the file offset shall be set to the end of the file prior to each write and no intervening file modification operation shall occur between changing the file offset and the write operation."

It also states that "Before successful return from write(), the file offset is incremented by the number of bytes actually written."

Before the second file write, the file offset should be set to the end of the file (offset 1, since the file only contains a at this point). Before the file write returns, the file offset must be incremented by one because one byte (b) was written. Therefore, the expected output is 2.

Additional context

This test case is derived from a program created by Wasimilar, an Xsmith-based random program generator.