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

Create externref example once it is usable in rust and/or tinygo #544

Closed
codefromthecrypt opened this issue May 12, 2022 · 5 comments
Closed

Comments

@codefromthecrypt
Copy link
Contributor

codefromthecrypt commented May 12, 2022

wazero allows use of unsafe.Pointer via globals or function parameters of ValueTypeExternref/uintptr. However, we shouldn't create an externref example yet because the ecosystem doesn't widely support it. Examples are for new users: introducing a complex topic awkwardly doesn't help them and probably will only increase confusion.

Once there is more use of externref (even if that's supporting another feature like interface types) and more language support (ex tinygo or rust), we should put in an example of externref. That should at least show a "borrowed" pattern, where the host passes via a param or a global a reference to something it created.

externref

As described in various places, the new externref value type is an interesting addition to value types as it is opaque and doesn't occupy linear memory. In fact, it can't because there is intentionally no way to serialize it abstractly. This gives interesting security properties vs passing pointers as i64. It is more safe as as you can't accidentally allow pointer arithmetic because it has no ops like that.

What uses externref today?

externref's only notable use is JavaScript binding via wasm-bindgen. Examples beyond that are theoretical. Examples in this project should be actionable, so this is a problem.

Examples:

There is an example of a more secure pattern of wasi that uses externref. However, the next version of wasi won't use externrefs, so this is theoretically interesting, but not actionable on its own.

There's also an example in the wasmedge book. Even though it uses externref, it isn't showing something unique bought by this feature.

A more comprehensive example is in a proof of concept. While this is interesting, it is too deep a topic to show to first time users.

One common thread across all of these is that they are in the raw WebAssembly Text format. The impact of this point is covered in the next section.

Which languages support externref?

Theoretical examples use the %.wat format because language support is not even close to wide, yet.

It is true that there are languages that support externref, for example AssemblyScript. However, that's not enough to make an example for. Moreover, there are other languages like Zig who also doesn't support it. We need the ecosystem to catch up, in other words.

When we do examples, we should start with borrowed references.

The first, possibly only example should be a borrowed externref. A "borrowed" pattern is when the host passes via a param or a global a reference to something it created.

While complicated, a refcount example of wasm calling ctor and dtor could be made later. It would be better to employ some wasm that already does that. This is similar to allocation where we don't create an allocation impl in our example, rather we use what the language compiling down to wasm generated.

When we do examples, we may need a compat mode

externrefs are defined opaque, but since so few languages can target that, a CompileConfig may be
necessary to coerse an import mismatching an export to bind externref -> i64. To solve that now, is "cart before the horse", and if we did this it would need to be highly warned that this undoes the security properties of externref mentioned in the last section.

What's the workaround?

The current workaround is to use i64/uint64 in signatures instead of externref/uintptr. Casting a uintptr to uint64 works today, just this should be done with caution. See the last topic for more.

Why is externref better than passing pointers for security?

The externref is a better, albeit imperfect development for security vs passing raw pointer values in a numeric form (ex i64 global or param).

externref by definition has no binary representation, which means only the host can create or modify it. However, an attacker could pass an externref where it shouldn't be passed, and via this crash or leak data if the impl isn't careful.

externref has no binary representation, so it is not recordable. However, this doesn't mean an attacker can't exploit something else that can record it, such as an unguarded logging function.

externref in practice is more secure when the embedder manages the lifecycle. Even if externref is allocated and deallocated by the host, wasm can miscount references, allowing a reference to outlive its lifespan. Bugs like this can materialize as "use after free" CVEs.

The summary is that externref cannot remove your responsibility in security. Even if there's less exploitable surface area, there's still a surface area. If you want best security, you should avoid both passing pointers as uint64 or externref (uintptr). In other words, security first sites should avoid any pointers.

@codefromthecrypt
Copy link
Contributor Author

I'm going to close this issue as we can re-open it when tinygo supports externref

@munrocket
Copy link

munrocket commented Jun 29, 2022

Rust wasm32 arch does not support externref

Rust is support reference types and it uses externref.

externref's only notable use is JavaScript binding via wasm-bindgen. Examples beyond that are theoretical. Examples in this project should be actionable, so this is a problem.

This examples not theoretical, JS glue become much smaller with it, also interoperation overhead become smaller. Interop is the main bottleneck in WebAssembly, that is why externref and reference types are so important.

@codefromthecrypt
Copy link
Contributor Author

@munrocket you are right that wasm-bindgen documents this. wasm-bindgen is made for javascript (invoking JS fucntions etc) so that's more work to support outside the browser as you have to emulate one.

Moroever, the implementation of wasm-bindgen seems to convert signature to pointers for LLVM which makes me curious if indeed rust can generate multi-value code or not.

https://github.com/rustwasm/wasm-bindgen/pull/1764/files#diff-d4d14c66ff3cd22500589a2302d6142817613f63a0385f21ef2f8b85fff46513R85
https://github.com/rustwasm/wasm-bindgen/blob/main/crates/multi-value-xform/src/lib.rs

You mind creating a simple rust project that uses multi-value? Ex returns two values and the generated %.wasm file has a signature with 2 returns?

In other words, wasm-bindgen (and its various successors) is a separate topic from multi-value. If we can use multi-value in rust that would be interesting!

@codefromthecrypt
Copy link
Contributor Author

still no progress I can tell for native rust support, though some are using macros as a workaround ex. https://github.com/slowli/externref

tinygo also isn't progressed yet tinygo-org/tinygo#2702

@codefromthecrypt
Copy link
Contributor Author

clang seems in progress, so perhaps rust or another soon. or we can accept clang as a valid example since we now have other examples in clang

https://docs.google.com/document/d/1-FTryoM9aBdTzBwRbp39yA8deqCCttkLP5OIq5LF2qY/
pmatos/llvm-project@8e3d6dd

@mathetake mathetake closed this as not planned Won't fix, can't repro, duplicate, stale Feb 4, 2024
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

3 participants