How to use the WebAssembly (wasm) binary module in macos App

During the release of our macOS App, we encountered the following issue:

We need to support dynamic code loading of WebAssembly (wasm) inside our App, mainly by loading WebAssembly (wasm) binary modules.

We discovered a problem: a wasm file is neither an executable nor a bundle, so it cannot be code-signed. Since our App needs to pass notarization, we have not set the com.apple.security.cs.allow-unsigned-executable-memory entitlement.

Without setting com.apple.security.cs.allow-unsigned-executable-memory, loading a wasm module results in an “unsigned code” error that causes the process to crash.

Could you please advise on what we should do to avoid this problem? Is it possible to apply for a special entitlement to allow com.apple.security.cs.allow-unsigned-executable-memory?

Answered by DTS Engineer in 864772022

Oh, you’re a system extension. That changes things.

System extensions, due to their privileges position on the system, are not allow to use entitlements to disable the additional security measures imposed by the hardened runtime. The only exception that that rule is that macOS platform extensions [1] are allowed to use JIT via the com.apple.security.cs.allow-jit entitlement.

Sadly this isn’t documented on the Hardened Runtime page, and I’ve filed a bug to get that fixed (r. 164078299).

So, if you want to use JIT in your sysex, you can’t lean on the com.apple.security.cs.allow-unsigned-executable-memory compatibility crutch. You’ll have to do the right thing and use com.apple.security.cs.allow-jit, as explained in Porting just-in-time compilers to Apple silicon.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] So, not DriverKit drivers.

Thanks for bringing this to the forums.

I’m pretty sure that’s a straightforward answer to this, but before I go into details I want to confirm one thing. When you do this:

% file /path/to/module

where /path/to/module is one of these WebAssembly binary modules, what does it report?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

@DTS Engineer it reports: WebAssembly (wasm) binary module version 0x1000d

Yeah, I figured you’d get see something like this.

IMPORTANT All of the following assumes you’re targeting macOS. The story for iOS and its child platforms is very different.

From the perspective of code signing on Apple platforms, this is data not code. We touch on this in Placing content in a bundle; see the discuss of shell scripts in that doc. Given that, you should place these files in Contents/Resources where they’ll be sealed over by the code signature like any other resource file.

That takes care of the on-disk side of things. How you handle this in memory depends on your WebAssembly runtime. Some runtimes are pure interpreters. Those don’t generate code on the fly, and thus don’t need to take any special action to allow that code to run.

However, high-performance WebAssembly runtimes often include a JIT. There are two ways you can create a JIT:

  • The recommended path is the one described in Porting just-in-time compilers to Apple silicon.
  • Many legacy JITs don’t follow those rules, relying instead of com.apple.security.cs.allow-unsigned-executable-memory. This is significantly less secure.

Both approaches are compatible with both of our distribution channels, that is, the Mac App Store and direct distribution using Developer ID signing.

And that brings me to this:

Is it possible to apply for a special entitlement to allow com.apple.security.cs.allow-unsigned-executable-memory?

That entitlement is unrestricted [1], that is:

  • Any developer can use it at any time.
  • It doesn’t have to be authorised by a provision profile.

Likewise for the entitlements used by our recommended JIT path.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] In the terminology defined by TN3125 Inside Code Signing: Provisioning Profiles.

When I use the com.apple.security.cs.allow-unsigned-executable-memory entitlements, after the notarization, I met these errors when I launch the App:

AMFI: When validating /Applications/xxxx.app/Contents/MacOS/xxx: Hardened Runtime relaxation entitlements disallowed on System Extensions mac_vnode_check_signature: /Applications/xxxx.app/Contents/MacOS/xxxx: code signature validation failed fatally: When validating /Applications/xxxx.app/Contents/MacOS/xxxx: Hardened Runtime relaxation entitlements disallowed on System Extensions ASP: Security policy would not allow process: 85453, /Applications/xxxx.app/Contents/MacOS/xxxx

By the way: We found that signing a wasm file writes the signature into the file’s extended attributes. In this case, when the wasm is loaded, is the memory considered to be signed?

We have found the cause of the issue. Below are our current entitlements:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>com.apple.developer.endpoint-security.client</key>
        <true/>
        <key>com.apple.developer.networking.networkextension</key>
        <array>
                <string>content-filter-provider-systemextension</string>
                <string>dns-proxy-systemextension</string>
                <string>app-proxy-provider-systemextension</string>
        </array>
        <key>com.apple.developer.system-extension.install</key>
        <true/>
        <key>com.apple.security.application-groups</key>
        <array>
                <string>$(TeamIdentifierPrefix)xxxxxx</string>
        </array>
        <key>com.apple.security.get-task-allow</key>
        <false/>
        <key>com.apple.security.network.client</key>
        <true/>
        <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
        <true/>
</dict>
</plist>

We discovered that com.apple.developer.endpoint-security.client and com.apple.security.cs.allow-unsigned-executable-memory are in conflict.

Currently, our approach is to download wasm files from the server to the local machine and load them dynamically at runtime, so we cannot package the wasm files into the Contents/Resources directory inside the app bundle.

Could you please advise how to resolve this problem?

Accepted Answer

Oh, you’re a system extension. That changes things.

System extensions, due to their privileges position on the system, are not allow to use entitlements to disable the additional security measures imposed by the hardened runtime. The only exception that that rule is that macOS platform extensions [1] are allowed to use JIT via the com.apple.security.cs.allow-jit entitlement.

Sadly this isn’t documented on the Hardened Runtime page, and I’ve filed a bug to get that fixed (r. 164078299).

So, if you want to use JIT in your sysex, you can’t lean on the com.apple.security.cs.allow-unsigned-executable-memory compatibility crutch. You’ll have to do the right thing and use com.apple.security.cs.allow-jit, as explained in Porting just-in-time compilers to Apple silicon.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] So, not DriverKit drivers.

How to use the WebAssembly (wasm) binary module in macos App
 
 
Q