> It is a stack machine, no registers; instructions generally push and pop 32/64-bit ints and floats onto that stack.
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.)
Thanks for this comment. In a previous draft of the post I had more about this, but I decided to omit it because I didn't have much insight about it beyond some handwaving around how it's just encoding the instructions and the optimizer will do something different. Your comments (this and the other one in this thread) are better than what I would have written, so I learned something today! (Edit: I updated the post to link to your comment.)
I dislike referring to WASM as a "stack machine" for this reason. It is useful to remember that the text format started out as a low-level lisp-y expression tree with local variables and nested control flow. It still basically is that, but with the AST syntactically flattened in a way that looks a bit like a stack machine. (Admittedly, multi-value and block returns have since made it trickier to recover the AST.)
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.
I am concerned that web assembly will mean an end to ad blocking and to a proliferation of malware, as the end user is basically going to be handed an opaque binary blob they can't do much with but execute.
Wasm in the browser can only communicate by going through browser APIs. Most ad / analytics / tracker blocking works by intercepting HTTP requests, so they will work just as well for Wasm code.
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.
You’re right, but it’s worth noting that nothing about that is particular to wasm. A JS app could equally avoid the DOM and render its own UI components via WebGL/WebGPU.
"there will come a time when it's trivially easy to write a full app in the browser via wasm"
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.
WebAssembly just had its 7th anniversary. If it hasn't happened yet, it probably won't happen at any time in the future.
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.
> If it hasn't happened yet, it probably won't happen at any time in the future.
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.
More to the point, it didn't happen with Java, it didn't happen with Flash and it didn't happen with Silverlight. This is just a perennial conspiracy theory.
You don't need wasm for that, anything that just throws up a huge <canvas> in the browser will do.
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".
>the end user is basically going to be handed an opaque binary blob they can't do much with but execute.
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.
Minified obfuscated Javascript isn't really all that different. You can create call graphs out of WASM binary as well, decompile and analyze it in detail.
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.
"You could create tools to block WASM based on signatures, heuristics, behavioral analysis, etc."
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 fight that is to, as a community, refuse to run _all_ wasm that is found to serve ads, or infringe on privacy, or exhibit other malware traits.
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".
"The end point of ad blocking is neural networks analyzing the final rendered image before being shown to the user."
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.
Wasm executing in the browser can't do anything on it's own. Can't make requests, cant update the dom, can't ask for input, nothing. All that still needs to happen in javascript.
There are some comments now... I guess a lot of people (like me) are interested in WASM, but most don't have enough knowledge to say anything "authoritative" about it. When I read "It is a stack machine, no registers", I remembered this other article http://troubles.md/wasm-is-not-a-stack-machine/ and thought "Ok now, these can't both be right at the same time, right?". But apparently they can - as another comment here states, "WebAssembly is not exactly a stack machine but a stack machine encoding of an instruction tree". To that I say, if I can use the stack as I'm used to from other systems, I don't really care if it's a "real" stack machine. But I'm not really sure of that either...
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.)
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.
Iirc WASM was initially explicitly designed to be translate-able to Asm.JS (that in turn translated to JS), basically they continued the way Asm.JS had retained backwards compatiblity with JS to either force other browsers to play along in implementing the standard or just look bad in benchmarks. (I think they've relaxed on back-compat now but it was a tool for getting everyone on board and once that had happened it was easier to break it).
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.
> Here's a thing that is obvious to me now but I think wasn't obvious when I started. C++ has its own notion of the stack and heap, in that in C++ you can e.g. take the address of something on the stack. This translates to Wasm by C++ stack variables existing in the Wasm-addressable memory, in the same way the C++ stack exists in memory. That memory is totally under the control of the C++ code. Meanwhile, Wasm runtime structures, including the call stack, are separate and are effectively invisible to C++. In summary, C++ memory errors like buffer overflows can still stomp on C++ memory, but that memory is distinct from any Wasm structures such as the call stack, so all a broken Wasm program can do is mess up its own memory, not the host's.
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.
Yes. WASM is secure in that it won't let programs escape. But it runs C and C++ programs which means that it lets them do anything those programs would do - just inside a secure box.
If you want your program to be more correct and secure that's then down to you to make sure it is. WASM just makes sure it can't take over the user's PC (or host's, if it's running outside of a browser).
Which is a message that unfortunely many WASM advocates leave out, asserting it is secure all over the board, hence why I tend to point out there are actually some footnotes related to it.
Maybe I’m missing some nuance here but it’s not clear to me what the omission by wasm advocates is?
> 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.
Thanks, that makes sense. This would only matter if you’re sharing the linear memory between different bytecode modules, though, right? If only one wasm module can touch a span of linear memory I can’t think of anything that it could do that wouldn’t be possible with type-aware bound checks (in the extreme, it could just treat the memory as an array of u8s and implement data structures on top of that, bypassing any type-awareness that the VM has)
The sandbox design makes it very impractical to hack wasm, though. Okay you've got code execution in my browser tab, but now you can't send network requests, read the file system, access contacts, what are you going to do? Run a cryptominer?
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.
WASM authentication module reusing code from something like Heartbleed.
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 don't really understand the scenario you pose. WASM isn't exactly something you run on the backend. If you're writing code that you need to compile to run, what's the point of compiling to WASM when you can generate native binaries that are much faster and much less complex? To me, this stuff only makes sense for optimizing some kind of core logic in a browser game or heavy web application, which hopefully doesn't need to do any authentication at all.
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.
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.
There is a good chance that it will happen eventually. Atwood's law is: "Any application that can be written in JavaScript, will eventually be written in JavaScript." But the root cause of this is really that when you get enough volume of activity in some area then the lowest common denominator tends to take over. "Worse is better" should have been written as "Worse is better for adoption" because it is definitely worse for every other attribute. In general, network effects are lowering the quality of everything.
Shameless plug: It's not C++ (but C instead, but of course also usable from C++), and not "great" as in "created by a big Silicon Valley company", but check out the sokol headers:
This may be a problem with the underlying C++ code but many of these demos are absolutely unusable through touch screens. The UI doesn't seem to know what to do with simple things like a drag motion or a tap. Canonic won't even open the soft keyboard on my phone.
It's good that these things render flawlessly in the browser but it seems like the platform still has some work to do.
Rust wasm support seems quite excellent, though? I haven't had too many issues with getting wasm output out of Rust. Emscriptem, though, has been a whole different story. I suppose it depends on what tool chains you're used to using.
I don't expect Zig to do any better or worse than Rust in this regard.
Zig can (eventually) do better than Rust because it includes the ability to compile C/C++ code to WASM, Rust needs an external C/C++ compiler for this (so you're back to having to deal with Emscripten if you need to embed C/C++ libraries in your Rust project).
The same is true in reverse, though, you need an external Rust compiler if you include Rust code in your C++ project. Mixing compilers and languages will always cause conflicts, I don't think either Emscripten or Zig will be any different other than that Zig has some backwards compatibility with C/C++.
They are good for different things, atm. Rust is focused on people writing new code for the Web, and does well there. C++ is focused on porting existing applications, which works well for things like Unity, Photoshop, Google Earth, etc.
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!
True, but there is an unfortunate amount of bugs in that area. We (I'm an Emscripten dev) would like to look more into improving Rust's Emscripten support, but there have just been higher priority items for us (and I guess others) so far.
> Writing to a null pointer is just writing to the memory array at offset zero, as legal as any other write.
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.
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.)