wasmerio / wasmer

🚀 The leading Wasm Runtime supporting WASIX, WASI and Emscripten
https://wasmer.io
MIT License
18.28k stars 775 forks source link

Does wasmer guaranties execution of instruction in atomic way in case of trap? #4596

Open grishasobol opened 3 months ago

grishasobol commented 3 months ago

Summary

Hi wasmer team! I would like to ask you, whether wasmer guaranties, that store is reverted back to the state right before an instruction which cause a trap during execution?

Additional details

To clarify the question, let's consider some examples.

1) Here we have OutOfBounds error. During execution a trap is appearing and signal handler stops code execution without any store recovering. So, the question is whether wasmer guaranties, that after execution store will be in state exactly before i32.store execution?

#[test]
fn test1() {
    let wat = r#"
        (module
            (memory (export "memory") 1)
            (func (export "main")
                i32.const 2
                global.set $a
                i32.const 0x10001
                i32.const 42
                i32.store
            )
            (global $a (export "a") (mut i32) (i32.const 1))
        )
    "#;
    let mut store = wasmer::Store::default();
    let module = wasmer::Module::new(&store, wat).unwrap();
    let imported_objects = wasmer::imports! {};
    let instance = wasmer::Instance::new(&mut store, &module, &imported_objects).unwrap();

    let res = instance
        .exports
        .get_function("main")
        .unwrap()
        .call(&mut store, &[]);
    assert!(res.is_err());

    let res = instance.exports.get_global("a").unwrap().get(&mut store);
    assert_eq!(res, wasmer::Value::I32(2));

    let mut buf = [0u8; 0x10000];
    instance
        .exports
        .get_memory("memory")
        .unwrap()
        .view(&mut store)
        .read(0, buf.as_mut_slice())
        .unwrap();
    assert_eq!(buf, [0u8; 0x10000]);
}

2) Here we have StackOverflow error. During execution a trap is appearing and signal handler stops code execution without any store recovering. So, the question is whether wasmer guaranties, that after execution store will be the same state as it was before execution of instruction which causes the trap?

#[test]
fn test2() {
    let wat = r#"
        (module
            (func (param i32)
                local.get 0
                global.set $a
                local.get 0
                i32.const 1
                i32.add
                call 0
            )
            (func (export "main")
                i32.const 0
                call 0
            )
            (global $a (export "a") (mut i32) (i32.const 1))
        )
    "#;
    let mut store = wasmer::Store::default();
    let module = wasmer::Module::new(&store, wat).unwrap();
    let imported_objects = wasmer::imports! {};
    let instance = wasmer::Instance::new(&mut store, &module, &imported_objects).unwrap();

    let res = instance
        .exports
        .get_function("main")
        .unwrap()
        .call(&mut store, &[]);
    assert!(res.is_err());

    let res = instance.exports.get_global("a").unwrap().get(&mut store);
    assert_eq!(res, wasmer::Value::I32(65439));
}
syrusakbary commented 2 months ago

In general, the store shall be able to be reused. We need to investigate more from the cases you commented, as if thats not happening, that's not the desired behavior. Thanks for creating the issue @grishasobol