Stream: wasmtime

Topic: macOS 14.4 behavior change


view this post on Zulip bjorn3 (Mar 18 2024 at 14:06):

I'm curious if the macOS 14.4 change referenced in https://blogs.oracle.com/java/post/java-on-macos-14-4 breaks Wasmtime. Anyone has an apple silicon mac and updated to macOS 14.4 to test it?

view this post on Zulip Ramon Klass (Mar 18 2024 at 14:13):

what do you need to be run?

view this post on Zulip Till Schneidereit (Mar 18 2024 at 14:25):

I'm pretty certain that this won't affect Wasmtime: we don't actually JIT code at runtime. Instead, we AOT compile all code in a module/component before starting execution

view this post on Zulip bjorn3 (Mar 18 2024 at 14:29):

We do still use the jit permissions for mapping the compiled code as executable, right?

view this post on Zulip bjorn3 (Mar 18 2024 at 14:30):

Ramon Klass said:

what do you need to be run?

Run any wasm module which does an out of bound memory access and check that it gives an error message about this rather than getting immediately SIGKILLed before the signal handler can catch the out of bounds access.

view this post on Zulip Ramon Klass (Mar 18 2024 at 14:33):

I ran my basic integration tests locally which build a component and check its functions against expected outputs, was green

view this post on Zulip Ramon Klass (Mar 18 2024 at 14:33):

how do I access oob?

view this post on Zulip Ramon Klass (Mar 18 2024 at 14:33):

(in rust)

view this post on Zulip Ramon Klass (Mar 18 2024 at 14:35):

nvm I overthought the problem, I'll add a check to my code

view this post on Zulip bjorn3 (Mar 18 2024 at 14:35):

Try this:

(module
  (type (;0;) (func (result i32)))
  (func $foo (type 0) (result i32)
    i32.const 0
    i32.load8_u offset=16777216)
  (table (;0;) 1 1 funcref)
  (memory (;0;) 16)
  (global $__stack_pointer (mut i32) (i32.const 1048576))
  (global (;1;) i32 (i32.const 1048576))
  (global (;2;) i32 (i32.const 1048576))
  (export "memory" (memory 0))
  (export "foo" (func $foo))
  (export "__data_end" (global 1))
  (export "__heap_base" (global 2)))

view this post on Zulip bjorn3 (Mar 18 2024 at 14:36):

wasmtime run should accept .wat text files.

view this post on Zulip Ramon Klass (Mar 18 2024 at 14:40):

wasmtime run example.wat (with that wat) exits immediately without printing anything, is that the desired behavior?

view this post on Zulip Till Schneidereit (Mar 18 2024 at 14:40):

We do still use the jit permissions for mapping the compiled code as executable, right?

We do, yes. If I understand the problem correctly, that is entirely ok. (And it better be, because otherwise most every JIT out there would be broken, including Safari's.)

What the JVM seems to be doing is to map a region as WO, but not guarantee that the VM won't try to access it in other ways while it's mapped that way. They then rely on signal handlers to catch these kinds of accesses—presumably by making them wait until the mapping has changed?

If that understanding is correct, then that seems like a clever hack, but also not something that'd be all too common in JITs

view this post on Zulip bjorn3 (Mar 18 2024 at 14:41):

Ramon Klass said:

wasmtime run example.wat (with that wat) exits immediately without printing anything, is that the desired behavior?

Forgot that you have to do wasmtime run --invoke foo example.wat as I named the sole function foo.

view this post on Zulip Ramon Klass (Mar 18 2024 at 14:41):

Error: failed to run main module `example.wat`

Caused by:
    0: failed to invoke `foo`
    1: error while executing at wasm backtrace:
           0:   0x6e - <unknown>!foo
    2: memory fault at wasm address 0x1000000 in linear memory of size 0x100000
    3: wasm trap: out of bounds memory access

view this post on Zulip Ramon Klass (Mar 18 2024 at 14:42):

so everything's fine

view this post on Zulip bjorn3 (Mar 18 2024 at 14:42):

Great! Thanks for testing!

view this post on Zulip bjorn3 (Mar 18 2024 at 14:43):

What the JVM seems to be doing is to map a region as WO, but not guarantee that the VM won't try to access it in other ways while it's mapped that way. They then rely on signal handlers to catch these kinds of accesses—presumably by making them wait until the mapping has changed?

We map the guard pages as PROT_NONE and then use a signal handler to catch out-of-bounds accesses to the linear memory. What is exactly the difference with what the JVM does?

view this post on Zulip Chris Fallin (Mar 18 2024 at 16:10):

looking at the JVM bug and the man page for pthread_jit_write_protect_np, it seems that macOS provides a mechanism where one can mmap some memory with MAP_JIT and then flip a global (per-thread?) flag with a pthread API that makes it either writable or executable, but never both. The reproducer at the end of the JVM bug shows that the new SIGKILL case is exactly when the global jit-mode is not-writable and one has any write segfault

view this post on Zulip Chris Fallin (Mar 18 2024 at 16:10):

Wasmtime doesn't use this mechanism, rather it uses individual mmaps for modules' code memory and mprotects them when publishing

view this post on Zulip Chris Fallin (Mar 18 2024 at 16:12):

see in particular this sequence and here

view this post on Zulip Chris Fallin (Mar 18 2024 at 16:12):

so I think we aren't affected (and, empirically, I upgraded to macOS 14.4 on aarch64 recently and haven't seen test failures)


Last updated: Dec 23 2024 at 13:07 UTC