Notes on WebAssembly
neugierig.org
neugierig.org
One thing to note here is that WebAssembly is not just a stack machine but a stack machine encoding of an instruction tree. You can still execute it as like a stack machine but it doesn't look like a typical stack machine. In particular there are no generic stack manipulation operations at all (`drop` is the sole exception), so it is trivial to convert this encoding back to the instruction tree. This greatly simplifies the validation step.
(Edited to clarify that WebAssembly is still a stack machine, just that it lacks usual stack operations.)
Things like Forth or the JVM byte code are true stack machines: all locals can be on the stack, and the stack shape can vary at runtime (which in turn makes validation harder). I seem to be in the minority, but I think wasm is quite different and the distinction is useful to maintain.
WASM that uses the regular DOM for rendering also doesn't really change blocking capabilities.
The only real issue is visual/selector based blocking when frameworks do their own rendering via canvas, webgl or webgpu. Flutter does this. But doing your own rendering comes with a lot of overhead, both in download sizes, startup time and performance.
Applications might do that (with lot's of caveats), but most websites never will. So I'm not that concerned.
Wasm is certainly much lower level than JS, but also much higher level than Assembly. It's far from an opaque binary blob. All the minified and partially even obfuscated JS is not that much better.
only because it's harder to do that right now than just describing a document via html.
But there will come a time when it's trivially easy to write a full app in the browser via wasm, and the outcome will be that the user can either accept or reject the entire app, rather than selectively choose the part of the app to view or use.
However, do make us to think we need another API for general JS without WebGL and GPU access.
This will be facilitated by libraries and frameworks that do all the low-level heavy lifting for the developer, who'll be able to concentrate on higher level concerns.
And I think this hasn't happened because it wouldn't work with the "ad mafia" business model. Embedding hardwired ads into a WASM blob of an application doesn't make any business sense just as it doesn't make business sense to host ads locally from your own server. Instead ads are typically injected from an external ad network, and those requests can be filtered even when they are coming out of a WASM blob.
As for the malware comment: WASM isn't any different from minified Javascript in that regard.
Remember that ads is an unethical arms race and there was a time where ad companies were using VBScript to create system-level popups which defeated any popup blocker.
The question is not if it will happen, but when and what will be the consequences.
What I expect wasm to do is to introduce new frameworks that take whatever the devs consider to be native behaviour and plonk it right into the browser. There are already plenty of demos out there that break the most basic browser features because compiling all of Qt into a canvas view can't possibly render a native experience.
Then again, we've lost the concept of "native" years ago when we started packaging browser engines with popular applications.
Soon we'll reach a stage where native code gets compiled to wasm, bundled with a slightly outdated copy of Chrome and sold off as a "native application".
If you step back far enough, you realize that pretty much everything we run on our machines fits this definition. The key feature of WebAssembly is that the code has NO access to the rest of the system, by default. It's this freedom to just run anything, without the worry of unwanted side effects, that gives me hope for the future.
And JS is so easy to minify / obfuscate that de-obfuscating JS isn't much better than dis-assembling WebASM.
Anything that gets popular gets dis-assembled, even MIPS and x86 binaries.
In fact, even before WASM, you could compile C++ to very hard-to-read asm.js subset of Javascript as well, see Emscripten.
You could create tools to block WASM based on signatures, heuristics, behavioral analysis, etc. Difficulty level is very similar to doing same with Javascript.
Yes, the concern is that as the browser becomes the operating system, we'll need anti-malware and anti-virus tools on it just as we've needed them on Windows.
The only way to do that would be to use browser extensions that disable and report, to disable for other users automatically. But how would we identify the executables? hash? That can be changed trivially.
I really fear that wasm brings us 180 degrees from "don't execute anything from the internet" to "all WWW usage is running other peoples' executables".
I think WASM will actually be more secure than the exsting www model. See: POST for an example :)
I agree though... there will be bad actors and an ever evolving cat and mouse game
This blocking could be strict, or lax, where you block even non-intentional ad stuff like someone writing in a HN comment "I love X".
This is an endless cat-and-mouse game, where advertisers could start using DRM that prevents the viewer from interposing anything between the browser and the digitally-connected monitor.
Maybe eventually users will start wearing AR glasses that filter out ads for them...
Until advertisers figure out a way around that too.
So I don't see much has changed.
int powi(int n, int m) {
int s = 1;
while (--m >= 0) s *= n;
return s;
}
The compiled WebAssembly code would look like this (the actual textual format): (func $powi (param $0 i32) (param $1 i32) (result i32)
(local $2 i32)
(set_local $2 (i32.const 1))
(block $break
(loop $continue
(br_if $break (i32.ge_s (tee_local $1 (i32.sub (get_local $1) (i32.const 1)))
(i32.const 0)))
(set_local $2 (i32.mul (get_local $2) (get_local $0)))
(br $continue)
)
)
(get_local $2)
)
Note the clear resemblance between the original code and the compiled binary. The main difference is two branch labels, because WebAssembly distinguishes forward and backward jumps by block types (`block` vs. `loop`). The binary retains all parameters and local variables.When we say WebAssembly is a stack machine, it means that this gets serialized like this: (The textual format allows both forms, while the binary format is a direct translation of the latter.)
(func $powi (param $0 i32) (param $1 i32) (result i32)
(local $2 i32)
i32.const 1
set_local $2
block $break
loop $continue
get_local $1
i32.const 1
i32.sub
tee_local $1
i32.const 0
i32.ge_s
br_if $break
get_local $2
get_local $0
i32.mul
set_local $2
br $continue
end
end
get_local $2
)
In principle you can execute this just like a stack machine with variables, but optimizing implementations will translate this into machine codes, which wouldn't look much like either forms. So the stack machine representation doesn't imply the stack machine execution.This is why WASM kind-of-is a stack machine. Basically the opcode sequences should be possible to easily re-translate back into a syntax tree for a back-compat JS generator.
This is also why you have these block/loop branches.. it basically is translateable to labelled while loops and if blocks.
And this kids, is why security in WASM still needs to be taken with a grain of salt if any of those modules touch critical data or return values that influence the caller's decisions.
> And this kids, is why security in WASM still needs to be taken with a grain of salt if any of those modules touch critical data or return values that influence the caller's decisions
Isn’t this true of any sandboxed environment? You can trust it not to touch things outside of the sandbox, but any holes you poke to get data in/out need their own verification.
No it isn't true for all sandboxed environments, there are sandboxed environments where memory accesses are bound checked for the underlying data types, instead of only the complete linear memory segment.
The point is preventing that a 100 byte array gets a 200 index access, even though the overall linear memory segment for the WASM module is 500 big.
I suppose you could access and maybe even exfiltrate data from the application itself but unless you get some kind of reflected XSS down to autoload your malware, your infection clears the second I refresh. When the application authors fix the hole and the user reloads your persistence vanishes.
Wasm won't solve C(++) memory management flaws but it can severely limit the impact they have in practice. In my opinion that's the strong suit of wasm as an application format.
Great it doesn't affect the host code, yet it affects the authentication, thus now a plain user might eventually be upgraded into admin.
And who knows it doesn't send network request?
It is all a matter of what the JavaScript functions imported into the module are capable of, or the code that happens to call into the module and what decisions it does in regards to the returned values.
I suppose developers could be foolish enough to break the WASM security model by exposing function to connect to arbitrary hosts but code running in the browser is still subject to CORS/CSP. You need to actively work around those to break the security layer and if someone is foolish enough to sabotage their own security that way then I don't think it matters much whether you use WASM or not.
As for the backend, WebAssembly startups want to bring back Application Servers.
As how foolish one might be, as we often discover during pentesting of Web applications, quite often.
We're really moving back to the early 2000s. I wonder when websites start working without Javascript again!
WASM on the backend makes sense in a few cases (i.e. let customers upload modules in a sandboxed, heavily restricted environment, or whatever) but building your entire application around a speedup format for a Javascript engine while writing low-level code is a new level of ridiculous I didn't even see coming.
As for your JavaScript remark, well SPAs are now rediscovering SSR, naturally with newer buzzwords like hydration.
https://github.com/floooh/sokol
...live samples:
See for instance:
It's good that these things render flawlessly in the browser but it seems like the platform still has some work to do.
rust is stuck with "rewrite everything to be usable", it can't consume any of that
zig on the other hand.. it can, i think it'll have a bright future
I don't expect Zig to do any better or worse than Rust in this regard.
The reverse cases are sadly not that great atm. If you have a Rust application that uses SDL2 and OpenGL, for example, porting it to the Web is not that easy (you either need to port those APIs to something else, or mix Rust and C to get Emscripten's SDL2 and OpenGL support). And if you want to write new C++ code just for the Web, current bindings solutions are not polished enough.
But hopefully all those things will keep improving!
This seems like a huge design mistake. Is it possible to make dereferencing a null pointer trap in WASM? That's an essential part of debugging C/C++ applications.