Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rust: corrupted argument of function with signature func(record, record, u8, record) #1134

Open
ethanstanley3 opened this issue Jan 18, 2025 · 1 comment

Comments

@ethanstanley3
Copy link

Description

echo "`wit-bindgen -V` | `rustc -V` | `wasmtime -V` | `uname -m`"
wit-bindgen-cli 0.37.0 | rustc 1.83.0 (90b35a623 2024-11-26) | wasmtime 28.0.1 (1bdf2c2b5 2025-01-14) | arm64

The value of a function argument is corrupted when a host component calls a function of a guest component. The signature of the function is foo: func(a: rec, b: rec, x4: u8, d: rec), where rec is defined as follows:

record rec {
    x: s8,
    f2: string,
}

The value of foo's third argument, x4, is corrupted and overwritten by the value of d.x. The host and guest components are compiled from Rust source programs using the wit-bindgen crate and the wasm32-wasip2 target. The two components implement the WIT worlds defined below:

package ns:pkg;

world guest {
  record rec {
    x: s8,
    f2: string,
  }
  export foo: func(a: rec, b: rec, x4: u8, d: rec);
}

world host {
  record rec {
    x: s8,
    f2: string,
  }
  import foo: func(a: rec, b: rec, x4: u8, d: rec);
  export wasi:cli/run@0.2.0;
}

package wasi:cli@0.2.0 {
  interface run {
    run: func() -> result;
  }
}

Below are the Rust source programs of the host and guest components, respectively:

fn run() -> Result<(),()> {

    let default_rec = Rec {
        x: 0, // can be anything
        f2: "".to_string(), // ^
    };

    let x4: u8 = 1; // this value should be printed, but isn't (can be anything)
    
    let special_rec = Rec {
        x: -1, // this field is magically converted to typeof(x4) and printed instead
        f2: "".to_string(), // can be anything
    };
    
    println!("Sending: {x4}");

    foo(
        &default_rec, 
        &default_rec, 
        x4, // prints x4
        &special_rec
    );
    
    Ok(())
}
fn foo(_a: Rec, _b: Rec, x4: u8, _d: Rec) {
    println!("Received: {x4}");
}

Host and guest components are created from the source programs above using the Rust toolchain. The components are composed using wac. The resulting component is executed by Wasmtime. The value of x4 is printed before and after being passed as an argument to foo.

Steps to reproduce

Here is a zipped directory that reproduces the bug:
three_records_and_int_corruption.zip

  1. Unzip three_records_and_int_corruption.zip
  2. Build and run with ./verify.sh

Expected behavior

The printed values should match:

Sending: 1
Received: 1

Actual behavior

The printed values don't match:

Sending: 1
Received: 255

Notes:
Various modifications to the test case eliminate the unexpected behavior. For example, renaming the fields of rec, changing the number/types of foo's parameters, or renaming foo's third parameter can cause the printed values to match.

Additional context

The Rust source programs are derived from programs generated by a differential testing framework for wit-bindgen. The wit definitions are derived from a test case produced by wit-smith.

@primoly
Copy link
Contributor

primoly commented Jan 19, 2025

The issue is that wit-bindgen generates variable names for its internal conversion code by appending numbers to identifiers.1 In this case this causes the x field in the d argument to be named x4 and the original x4 argument to be overwritten.

Footnotes

  1. https://github.com/bytecodealliance/wit-bindgen/blob/94ba15617543815de6bdf21025a27d4f4a324625/crates/rust/src/bindgen.rs#L150C1-L150C50

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants