Hacker News new | past | comments | ask | show | jobs | submit login
WebAssembly: Adding Python support to WASM language runtimes (wasmlabs.dev)
216 points by assambar on Jan 31, 2023 | hide | past | favorite | 86 comments



This looks very promising!

The thing I most want to solve right now is this: I want to write a regular Python application that can safely execute untrusted Python code in a WASM sandbox as part of its execution.

I want to do this so I can let end users customize my web applications in weird and interesting ways by pasting their own Python code into a textarea - think features like "run this Python code to transform my stored data" - without them being able to break my system.

This feels like it should be pretty easy with WebAssembly! It's the classic code sandboxing problem - long a big challenge in Python world - finally solved in a robust way.

I've been finding it surprisingly hard to get a proof-of-concept of this working though.

Essentially I want to be able to do this, in my regular Python code:

    import some_webassembly_engine

    python = some_webassembly_engine.load(
        "python.wasm",
        max_cpu_time_in_seconds=3.0,
        max_allowed_memory_in_bytes=32000000
    )
    result = python.execute("3 + 5")
I've not yet figured out the incantations I need to actually do this - in particular the limits on CPU and memory time.

I posed this question on Mastodon recently and Jim Kring put together this demo, which gets most of the way there (albeit using an old Python 3.6 build): https://github.com/jimkring/python-sandbox-wasm

It doesn't feel like this should be as hard to figure out as it is!


Wasmtime's `wasmtime-py` embedding in python has support for Wasm Components: https://github.com/bytecodealliance/wasmtime-py#components (disclosure, I helped create it)

The remaining piece of the puzzle would be to create a wit-bindgen guest generator https://github.com/bytecodealliance/wit-bindgen#guests for this build of the python interpreter. You could then seamlessly call back and forth between the host and guest pythons, without even knowing that wasmtime is under the hood.


If you could provide example code for how to do this - how to run a snippet of untrusted Python code using wasmtime-py with a CPU and RAM limit - I would shout it from the rooftops. I think a LOT of people would benefit from clear examples of how to actually achieve this.


The wit-bindgen work required would be a significant undertaking (a week? more?) by someone who already has some expertise in wit & python. Maybe the wasmlabs folks are up for taking it on.

In general the Wasm Component ecosystem is still a few months away from being generally useful. There are a lot of people across the bytecode alliance working on the fundamentals right now, and we are making great progress, but its not ready to ship quite yet.



Yes! Very much so.

Just tried this and it works great!

I changed app.py to this:

    import sqlite3
    print(sqlite3.connect(":memory:").execute(
        "select sqlite_version()"
    ).fetchone()[0])
And it output "3.39.2" - but the same code in my regular Python interpreter output "3.40.1", which demonstrates that the WASM Python there has its own WASM-compiled SQLite.


Great! I still need to look into how to limit memory consumption. Fuel works well enough for now, but there might be an option to limit execution by time, not just instructions.


Have you seen Extism? We call it a "plugin system", but it's far more generic than that.. just hard to market a "general purpose universal code runtime".

Python SDK: https://extism.org/docs/integrate-into-your-codebase/python-...

Python is 1 of 15+ languages we support, and as far as I know it's the easiest way to setup a wasm engine in your app, load wasm code, and call a function using complex data I/O.

I'm one of the authors - happy to share more about it, or if you'd like to join our Discord we have lots of active users and contributors there: https://discord.gg/cx3usBCWnc


I hadn't looked at that one. It looks very promising! Definitely has better documentation than the other options I've looked at.

I see that the library itself is available in Python, but this page https://extism.org/docs/category/write-a-plug-in currently only lists Rust, JavaScript, Go, Haskell, AssemblyScript, Zig and C. Any chance Python might get added to that list as well?

Just found https://github.com/extism/extism-sqlite3 which is very relevant to my interests too!


Extism does not yet support writing the plugins (our term for Wasm guests) in python. I have been working on javascript support[1] and I think I could use a similar strategy to support python. If you are at all interested in following along or helping out you should join our discord.[2] I just started a #python-pdk channel there.

[1] https://github.com/extism/js-pdk [2] https://discord.gg/mAADpt9r


Oh, and here's an example (test) showing how to construct a "manifest" to control the cpu/memory limits: https://github.com/extism/extism/blob/main/python/tests/test...

cpu is really controlled by # milliseconds until the wasm code is trapped.


That looks like exactly the level of control I've been hoping for in terms of sandboxing. This is really promising!


FWIW although it's not WebAssembly based, you can do that with GraalVM. It has a concept of language contexts which can be sandboxed including those constraints. There are two caveats:

1. Sandboxing for CPU time and max allowed memory requires the enterprise edition, so you'd have to pay for it.

2. The Python engine isn't 100% compatible with regular Python, although that may not matter for your use case as the compatibility is pretty good and issues mostly show up around extension modules.


Unfortunately there are at least two more major caveats:

1. Capability control only works for JavaScript (https://www.graalvm.org/latest/reference-manual/embed-langua...)

2. The documentation says in no uncertain terms that running untrusted code is unsupported (https://www.graalvm.org/latest/security-guide/#security-mode...)


The startup I'm working at is basically trying to do exactly that as a service, but a one-off thing for a regular Python application shouldn't be as hard to figure out as it is. Can you link to the Mastodon thread (darn lack of search!) and we can continue there?


Here's the Mastodon conversation: https://fedi.simonwillison.net/@simon/109682777068881522

(I'm so close to building my own search engine just against my own content there.)


I have Covid and it's late in the UK, but tried poking at wasmtime-py for the first time and got this far: https://gist.github.com/callahad/81b33e4e4456e4b27a5934c1a36...

If anyone else is awake and wants to pick up the baton...

- CPU limits are just using an arbitrary amount of wasmtime "fuel". Would be worth looking into epoch interrupts instead.

- Memory limits aren't implemented. Seems like wasmtime-py doesn't expose bindings to anything with a wasmtime::ResourceLimiter trait.

- All i/o is going through tempfiles instead of dealing with proper interfaces.


Why do this on the client? Why not pass it to the server and run it on Python there?


That's what I'm talking about: I want to run Python code on my server, but since it's from an untrusted source I want to make sure that it's in a sandbox with strict limits on what it can do, how much CPU it can use and how much RAM it has available to it - so malicious code can't be used to crash my server or steal data it shouldn't have access to.


How do you think WASM will solve this where everything else has failed?


Because WASM supports capability based security and thus never trusts code to do the right thing. Unlike every single one of it's predecessors.


WASM does not support capability based security that is something of an extension to the WASI proposal.

And even then the security stuff is based on previously existing work in the cloud space. Which has existed for some time but is not widespread.


Java? .NET?


Depending on what your inputs and outputs look like, perhaps you can spawn the Python interpreter in a subprocess that's sandboxed with seccomp and/or setrlimit?


I have not been able to figure out how to do that in the past. I think a solution using those would be restricted to Linux, and I want something that also works on macOS and maybe even Windows too.


Run it in a docker container?


This would be great. And with an exposeable API for safety a memory safe API that could be exposed to wasm applications. And rate limited.


https://nsjail.dev is a common tool for sandboxing


What kind of Python types do you envision needing to pass across the native/wasm boundary?


Have you tried to do it with pyodide? What issues did you hit using that?


Pyodide isn't currently supported outside of browsers, though that might change: https://github.com/pyodide/pyodide/issues/869

Either way, I couldn't figure out how to do the above sequence of steps with any of the available Python WASM runtimes - they're all very under-documented at the moment, sadly. I tried all three of these:

- https://github.com/wasmerio/wasmer-python

- https://github.com/bytecodealliance/wasmtime-py

- https://github.com/wasm3/pywasm3


the issue right now with Python support in WASM (at least for machine learning, the main driver of the language) is that Python is largely a wrapper language and none the utilities that make it so powerful (numpy, PyTorch, JAX) work particularly well in wasm, since it's so limited performance-wise (no FMA, no GPU support).

I'm excited for pairing wasm with WebGPU, which will likely unblock these projects from building support for the web/untrusted ecosystem. A useful project would be one that makes this integration really easy to build today and a flip of the switch to turn on in the future.


I've come across this notion that nowadays machine learning provides (in some sense) the biggest group of Python users a few times recently.

What reason is there to suppose this is true? It seems surprising to me.


It's really hard to do much ML in anything _except_ python. Virtually everyone improving the ML ecosystems of other language got their start in Python and are knowingly competing with Python (e.g. R, Julia). If you want to get started in ML today, python is the obvious easiest path forward.

So, most ML users are python users. I don't know how that group compares to non-ML python users, but I have a feeling there isn't a flood of eager new Django devs the way there is Pytorch users. Most non-ML things you could do with python can be done similarly well in Go/Rust/Typescript, but there's no other option for most ML stuff.


I found a recentish (2021) survey at [1] which suggests that in 2021 ML was some way behind web development, sysadmin stuff, and data analysis among Python users (and didn't seem to be on the way up the list).

[1] https://lp.jetbrains.com/python-developers-survey-2021/#Gene...


The obvious next question being, what's the difference between ML and data analysis from the perspective of the survey participants (is ML a strict subset)? Given the values don't add up to 100%, there's likely lots of overlap and so you could easily have web developers choosing Python for the ML ecosystem.


Great source; looks like I've quite underestimated the python-web-dev crowd's size.

I'm curious what the longer-term trends look like; not much change between consecutive years.

Data analysis is basically a pre-requisite for ML, so the combined "data stuff" usage is quite a lot bigger than web dev usage!


I can see that a huge part of the ML space is in python. But is the ML space really such a large community? I mean in the companies I have worked for maybe 1 team in a 100 work with some ML stuff while a large majority of the rest come in contact with web/devops/unix scripting. Granted non ML work takes place in many different languages but python is used alot there.


> What reason is there to suppose this is true? It seems surprising to me.

One reason is its just super easy for input output operations. ML is all about data and getting the data to the right place is really easy in python compared to some other languages..


Which languages?

Python is OOP; but the "classical" data-centric languages are actually all more or less in the FP space. (I count array languages and APL-likes to FP in this case).

Just an example: You don't have immutable data types by default in Python. This is actually a pretty bad default for data processing tasks.


Python has a huge library ecosystem and the average Machine Learning programmer is not a CS geek and so prefers pythonic quasi-OOO then over FP (one of the hurdles for JAX adoption is it's functional paradigm)


The claim was that Python is better suited to writing such libs and frameworks than other languages.

You now say it's like that because Python has already quite some libs / frameworks in that direction.

This looks like circular reasoning.

Also the the "prototypical ML dude" comes form the math department. People with math background have a much easier time to grasp FP than procedural programming. FP is much more "natural" when you're used to math.

(Procedural programming says things like `x = x + 1`, but even my grandma would know that "this has no solution", or is likewise "plain wrong" ;-))


Just because python makes sense for ML does not mean that it's primarily used for ML.


I have integrated pyodide + webgpu recently. (you can do matmul using webgpu's compute pipeline). The real problem is that browser tabs have 4gb max memory size. So, training neural networks on this stack is almost impossible. ( I don't even want to mention pyTorch's dependency hell).


WebAssembly Memory64 is coming

https://webassembly.org/roadmap/


My claim is that it’s not easy, not impossible. There’s little incentive to hack in JavaScript or maintain a Pyodide compatible build. The 4gb limit isn’t a technical limitation, just a standards thing (it could change easily).


Don't expect too much from WebGPU, its hardware target model for the MVP is how version 1.0 of modern GPU bindings used to be several years ago.

In fact, in what concerns compute, it is hardly any better than GL ES compute shaders that Chrome refused to add to WebGL.


There seems to be so many different variants of the same thing out there. What makes this unique? For example I know Pyodide exists and also runs CPython under WASM.


This one is designed to run on the server side and interface with the OS via WASI, so it can read/write files etc


Pyodide can do so too, though through an emscripten interface. I managed to get it running on Cloudflare Workers as a POC[0] (though I should add a disclaimer that I work at Cloudflare)

0 - https://github.com/dom96/pycloud


The non-Docker version seems to require an external site-packages, unless I missed it. Is it possible to produce a single wasm binary with all dependencies compiled in?


Hey! Dev here :)

For external libraries, it requires you to mount the libraries with WASI when running the python.wasm module. Another option we're exploring is to use wasi-vfs[1] to include some common modules in our pre-built binaries. For example, Ruby does require some extra libraries for common workloads (like JSON parsing). This is still on the exploration phase, but we may do something with it.

[1] https://github.com/kateinoigakukun/wasi-vfs


Very cool. We ship some Python as a Debian dependency and so this could become a really interesting way to package everything up.


I have been following and playing with this repository: https://github.com/singlestore-labs/python-wasi/

It builds a single Python WASM module with all dependencies included (they use VFS) and a Dockerfile to make the process easy (and actually worked first go). It does produce large files though: wasi-python3.11.wasm 110MB


Yes! Single store is a great team. We are currently using some of their work for this Python release, like libz


Can anyone give me a ELI5 version what is the relationship between this and pyodie?


Pyodide is for the browser, this is intended for server side environments, so it can interact with files, sockets etc via WASI standard


How does this handle garbage collection? AFAIK the WebAssembly GC proposal is still in development. Does it implement GC in WASM code?


Perhaps it just uses Python's built-in garbage collector that just increases/decreases the data segment size as needed by calling sbrk()?


Correct, it is just CPython compiled to Wasm (similar to compiling to x86 or arm)


Sidenote: WasmEdge 0.12 (to be released in Feb) will be fully compatible with wasmtime’s socket API. There will be no need for separate builds soon.

In addition to wasmtime socket, WasmEdge supports async non-blocking socket for high-performance apps


Looking forward to it!


I thought this was running python in the browser.

I don't understand what's the point of this article.

I'm already okay with brython, which is fast enough, but sometimes it generates JavaScript errors, which are difficult to understand.


I believe this is envisioned a bit more like docker-lite minus the os image layer?


This looks like a solution looking for a problem, like many others regarding the use of WebAssembly outside of the browser.


Our most immediate goal is to be able to run traditional web apps written in PHP, Ruby, Python mostly unchanged but with the additional layer of isolation and sandboxing. We see this (once fully matured) as a great way to improve security without changing how web developers work


Containers and similar OS security features already provide that.


Not with that level of granularity. Wasm is able to run in severely constrained environments (ie microcontrollers) and non-posix OS that may not be able to support containers


Like Western Digital having hard disk controllers running P-Code in 1979.

https://en.wikipedia.org/wiki/Pascal_MicroEngine


Sure. Everything in computing pretty much builds on top of everything else. Wasm is no different and incorporates a lot of the learnings from previous technologies like Java and .NET


Exploring and providing new and different approaches is worthwhile, and I doubt the tradeoffs of each technology is identical.



So for someone who has python installed locally, what's the point?

Is it just the sandbox or is there anything else I'm missing?


If you're just looking to run trusted scripts locally, there isn't much point. If you're running a system that uses wasm, this means you can now easily support Python.


It's not for someone who only runs Python locally.


You get an extra layer of isolation, even at your development environment level.

I remember a NodeJs CVE that was caused by a poisoned dependency. It was affecting people when downloading it from npm.

There’s still a gap here to cover, but the benefits may be worth :)


I don't see how this would in any way prevent you from being affected by a equivalent poisoned pypi dependency; after all your secrets/credentials are inside the sandbox anyways or your code can't work.


With Wasm + WASI, you need to explicitly mount files and environment variables. Inside the Wasm VM, the Python interpreter, source code and dependencies only have access to a very reduced surface. Although you're right that if you mount credentials inside, they will be accessible too.

The incident I was talking about was the event-stream[1] vulnerability. The attacker introduced code that looked for the data of a crypto wallet. This data was stored in the user's home.

By default, interpreters may get access to the same resources that the user running the process. In Wasm, the resources are granted manually.

[1] https://blog.npmjs.org/post/180565383195/details-about-the-e...


> By default, interpreters may get access to the same resources that the user running the process. In Wasm, the resources are granted manually.

What's the difference to run the code under a different user (like for example `nobody` for "full sandboxing", or a "clone of nobody" with some additional access rights)?


Running code under a different user is hard. I don't know how I would do that on macOS, and the times I've done it on Linux I've always had to jump through a bunch of hoops.


MacOS aside, as this is not a serous operating system for this kind of workloads, do you think the "hoops" (which actually?) one needs to "jump through" are so problematic that it makes sense to create millions of lines of new code just to work around them? Only to arrive at the same result that is already working since something around 50 years? This must be really grave issues with just running a task as a different user… Could you expand on that?


I want to write software that end users can install on their own machines, that supports plugins so they can execute additional code from untrusted sources.

So macOS needs to work.

Microsoft Flight Simulator uses WebAssembly for its extensions system. I want to do the same thing for my own projects.


If Pygame can do wasm outputs to WebGL/WebGPU it'll move things along quite a bit for game development


Ready-to-use python.wasm, also in a Docker+Wasm container image.


But please don't forget to wrap it in at least some VM! /s

That's not even funny, as in real life people would run something like that actually in a VM.

So we have now: HW memory protection -> HW virtualization -> VM -> OS -> Docker -> WASM -> language runtime -> some code snippet.

Things become quite crazy these days, to be honest…


I totally agree with you on how ridiculous this is. But it's all about trade offs in the end. In the case of Docker+Wasm you get portability and delivery speed.

You build one image for the wasm32/wasi platform and now it can run on any architecture that has Docker, just like that. And also you don't carry the whole OS baggage of a typical container. Only the language runtime. So download times are faster.

---

Lastly, the Docker deployment is just the easiest way for a WASM application to reach people. They can try it out without the necessity to setup and support a WASI runtime.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: