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

Support re-entering instance after previous trap inside continuation #253

Open
frank-emrich opened this issue Nov 15, 2024 · 0 comments
Open

Comments

@frank-emrich
Copy link

TL;DR: We currently have some support for trapping inside a continuation, but things fall apart if you want to re-use the instance afterwards.

Consider the following sequence of events:

  • We create an instance i.
  • We start execution of some Wasm function entry1 inside i.
  • entry1 runs some code inside a continuation. While doing so, a trap occurs.
  • Wasm execution terminates, we return to the host.
  • We want to execute more code inside i.

The following code snippet illustrates this:

fn reuse_instance_after_trap1() -> Result<()> {
    let wat = r#"
    (module
        (type $ft (func))
        (type $ct (cont $ft))

        (tag $t)

        (func $entry1 (export "entry1")
          (local $c (ref $ct))
          (local.set $c (cont.new $ct (ref.func $b)))
          (block $handlet (result (ref $ct))
            (resume $ct (on $t $handlet) (local.get $c))
            (return)
          )
          (drop)
        )

        (func $entry2 (export "entry2")
          (suspend $t)
        )

        (func $b (export "b")
          (unreachable)
        )
    )
    "#;

    let mut config = Config::default();
    config.wasm_function_references(true);
    config.wasm_exceptions(true);
    config.wasm_stack_switching(true);

    let engine = Engine::new(&config).unwrap();
    let mut store = Store::<()>::new(&engine, ());
    let module = Module::new(&engine, wat)?;

    let instance = Instance::new(&mut store, &module, &[])?;
    let entry1 = instance.get_typed_func::<(), ()>(&mut store, "entry1")?;

    let result1 = entry1.call(&mut store, ());

    let _ = result1.expect_err("Was expecting wasm execution to yield error");

    let entry2 = instance.get_typed_func::<(), ()>(&mut store, "entry2")?;

    let _ = entry2.call(&mut store, ());

    Ok(())
}

This segfaults, for a good reason: At the point when we do instance.get_typed_func::<(), i32>(&mut store, "entry2")?; the Instance (more precisely: the Store) thinks that we are still running inside the continuation previously called $c. On suspend, it then tries to use the context stored in $c about its parent, trying to suspend itself into a stack frame supposed to be executing the (long dead) entry1.

In general, what needs to happen is that either when a trap occurs while running inside a continuation, or when entering an instance in general, we reset the information about the currently active continuation. However, this is rather subtle, due to the various pieces of information associated with it (e.g., we also need to update the VMRuntimeLimits correctly).

frank-emrich added a commit that referenced this issue Nov 18, 2024
This PR splits `linking_tags.wast` into two files.

This is a preparation step needed to land fast effect forwarding due to
the following:
`linking_tags.wast` currently has two `assert_suspension` directives. In
the current implementation, we are always on the main stack when we
detect that a tag is unhandled and trap from there. However, in the
presence of fast effect forwarding, we detect the lack of an appropriate
handler at the `suspend` site and trap there (i.e., possibly while still
running on a continuation stack).

As a result, the `assert_suspension` directives in `linking_tags.wast`
trigger the problem described in #253 as soon as fast fowarding lands.
As a workaround, this PR splits the file into two, where the
`assert_suspension` is the very last step in each test (i.e., we avoid
further execution inside the `Store` where we trapped while inside a
continuation). The combined contents of both files is identical to the
original `linking_tags.wast`, but the module `bar` is moved to the end
of the second file.
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

1 participant