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

Clash of the tags #25

Open
dhil opened this issue Oct 16, 2023 · 2 comments
Open

Clash of the tags #25

dhil opened this issue Oct 16, 2023 · 2 comments
Labels
bug Something isn't working

Comments

@dhil
Copy link
Member

dhil commented Oct 16, 2023

At the time of writing, Wasmtime represents tags as unsigned integers. Thus, tags are not guaranteed to have a instance-unique identity. This is unsound. Here is a small example program that fails on our wasmtime implementation, but passes on the reference interpreter.

(module $foo
  (tag $foo (export "foo"))
)
(register "foo")

(module $bar
  (type $ft (func))
  (type $ct (cont $ft))
  (tag $foo (import "foo" "foo"))
  (tag $bar)
  (func $do_foo
    (suspend $foo))

  (func $main (export "main")
    (block $on_bar (result (ref $ct))
      (resume $ct (tag $bar $on_bar) (cont.new $ct (ref.func $do_foo)))
      (unreachable)
    )
    (unreachable))
  (elem declare func $do_foo)
)
(register "bar")
(assert_suspension (invoke "main") "unhandled")
@dhil dhil added bug Something isn't working wontfix This will not be worked on labels Oct 16, 2023
@dhil
Copy link
Member Author

dhil commented Oct 17, 2023

This should be fixed in the exception handling implementation first (in which case we get the fix for free).

@dhil dhil removed the wontfix This will not be worked on label May 30, 2024
@dhil
Copy link
Member Author

dhil commented May 30, 2024

Extended testsuite for this issue:

(module $foo
  (type $ft (func (result i32)))
  (type $ct (cont $ft))
  (type $ft-2 (func (param i32) (result i32)))
  (type $ct-2 (cont $ft-2))

  (tag $foo (export "foo") (result i32)) ;; occupies defined tag entry 0

  (func $do_foo (export "do_foo") (result i32)
     (suspend $foo))
  (func $handle_foo (export "handle_foo") (param $f (ref $ft)) (result i32)
    (block $on_foo (result (ref $ct-2))
      (resume $ct (tag $foo $on_foo) (cont.new $ct (local.get $f)))
      (return)
    ) ;; on_foo
    (drop)
    (return (i32.const 1))
  )
  (func (export "test_foo") (result i32)
    (call $handle_foo (ref.func $do_foo)))
  (elem declare func $do_foo)
)
(register "foo")
(assert_return (invoke "test_foo") (i32.const 1))

(module $bar
  (type $ft (func (result i32)))
  (type $ct (cont $ft))

  (type $ft-2 (func (param i32) (result i32)))
  (type $ct-2 (cont $ft-2))

  (tag $foo (import "foo" "foo") (result i32))
  (tag $bar (result i32))
  (func $do_foo (result i32)
    (suspend $foo))

  ;; Don't handle the imported foo.
  (func (export "skip-imported-foo") (result i32)
    (block $on_bar (result (ref $ct-2))
      (resume $ct (tag $bar $on_bar) (cont.new $ct (ref.func $do_foo)))
      (unreachable)
    )
    (unreachable))

  ;; Handle the imported foo.
  (func (export "handle-imported-foo") (result i32)
    (block $on_foo (result (ref $ct-2))
      (resume $ct (tag $foo $on_foo) (cont.new $ct (ref.func $do_foo)))
      (unreachable)
    )
    (drop)
    (return (i32.const 2))
  )

  (elem declare func $do_foo)
)
(register "bar")
(assert_suspension (invoke "skip-imported-foo") "unhandled")
(assert_return (invoke "handle-imported-foo") (i32.const 2))


(module $baz
  (type $ft (func (result i32)))
  (type $ct (cont $ft))

  (type $ft-2 (func (param i32) (result i32)))
  (type $ct-2 (cont $ft-2))

  (func $handle_foo (import "foo" "handle_foo") (param (ref $ft)) (result i32))
  (func $do_foo (import "foo" "do_foo") (result i32))

  (tag $baz (result i32)) ;; unused, but occupies defined tag entry 0

  (func $handle_baz (param $f (ref $ft)) (result i32)
    (block $on_baz (result (ref $ct-2))
      (resume $ct (tag $baz $on_baz) (cont.new $ct (local.get $f)))
      (return)
    ) ;; on_baz
    (drop)
    (return (i32.const 3))
  )

  (func $inner-baz (result i32)
    (call $handle_baz (ref.func $do_foo)))
  (func (export "compose-handle-foo-baz") (result i32)
    (call $handle_foo (ref.func $inner-baz)))

  (func $inner-foo (result i32)
    (call $handle_foo (ref.func $do_foo)))
  (func (export "compose-handle-baz-foo") (result i32)
    (call $handle_baz (ref.func $inner-foo)))
  (elem declare func $do_foo $inner-baz $inner-foo)
)
(register "baz")
(assert_return (invoke "compose-handle-baz-foo") (i32.const 1))
(assert_return (invoke "compose-handle-foo-baz") (i32.const 1))

(module $quux
  (type $ft (func (result i32)))
  (type $ct (cont $ft))

  (type $ft-2 (func (param i32) (result i32)))
  (type $ct-2 (cont $ft-2))

  (func $handle_foo (import "foo" "handle_foo") (param (ref $ft)) (result i32))
  (tag $foo (import "foo" "foo") (result i32))

  (func $do_foo (result i32)
    (suspend $foo))

  (func $my_handle_foo (param $f (ref $ft)) (result i32)
    (block $on_foo (result (ref $ct-2))
      (resume $ct (tag $foo $on_foo) (cont.new $ct (local.get $f)))
      (return)
    ) ;; on_foo
    (drop)
    (return (i32.const 4))
  )

  (func $inner-my-foo (result i32)
    (call $my_handle_foo (ref.func $do_foo)))
  (func (export "compose-handle-foo-my-foo") (result i32)
    (call $handle_foo (ref.func $inner-my-foo)))

  (func $inner-foo (result i32)
    (call $handle_foo (ref.func $do_foo)))
  (func (export "compose-handle-my-foo-foo") (result i32)
    (call $my_handle_foo (ref.func $inner-foo)))
  (elem declare func $do_foo $inner-my-foo $inner-foo)
)
(register "quux")
(assert_return (invoke "compose-handle-foo-my-foo") (i32.const 4))
(assert_return (invoke "compose-handle-my-foo-foo") (i32.const 1))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant