Remix.run Logo
simonw 3 days ago

Do you have any plans to add timeouts or some other mechanism for limiting the amount of CPU a webassembly call can use?

I'm always interested in options for using WebAssembly as a sandbox to run untrusted code, but one of the things I need to protect against is an infinite loop.

(I had Claude knock up an experimental Python binding to try Epsilon out, notes from that here: https://github.com/simonw/research/tree/main/epsilon-python-... )

trj 3 days ago | parent | next [-]

I'm working on an embeddable mini VM (RISC-V rather than WASM) and am considering this. In my model, there's something akin to a hardware watchdog where the VM is considered hung if it executes too many instructions without calling a yield() function in the host, then there's the ability to set a maximum number of instructions to execute before returning to the caller.

This lets it be handled asynchronously at the pace the host code chooses https://github.com/ringtailsoftware/uvm32

usrnm 3 days ago | parent [-]

Lua has hooks, which are callbacks that can be registered for a variety of different events, including every N instructions, and every call can decide to terminate the whole thing with an error. This mechanism can be used for different purposes, including tracing, some kind of performance stats and, of course, cancellation. I always found it to be a nice general solution.

evacchi 3 days ago | parent | prev | next [-]

wazero has supported context cancellation for a long time :) https://github.com/wazero/wazero/blob/9286448974219ab3be0931...

ziggy42 3 days ago | parent | prev | next [-]

Yes, I am considering using something like https://pkg.go.dev/context for this very purpose, though I need to read a bit more into it first.

Funny that you built a Python wrapper as I originally started this implementation in Python, which was...not a good idea. Claude hallucinated the acknowledgments section though :D

embedding-shape 3 days ago | parent | prev | next [-]

Limiting the CPU vs protecting against an infinite loop are two different problems. The former is usually solved by sandboxing and using the limiters exposed by it, while the latter can be easily solved by just adding a cancellation timeout, when the function call/process/API call/whatever takes longer than X seconds, cancel it and return an error.

ilikepi63 3 days ago | parent | prev [-]

I believe that wasmtime has some sort of mechanism for this called Gas if I'm not mistaken.

cfallin 2 days ago | parent [-]

We have two: fuel and epochs. Fuel (analogous to "gas" as it appears in many VM platforms) is a deterministic count and epochs check an always-increasing counter in shared memory meant to be periodically bumped by another thread. In either case, hitting the limit can be configured to either async-yield back to the event loop (in async mode) or to trap/terminate the Wasm instance. Both are based on instrumentation, i.e., extra code inserted during Wasm-to-machine-code compilation to do these checks at the start of loops and at the top of each function. Epoch instrumentation is cheaper because it's checking a mostly-read-only value in memory (so usually cached) while fuel loads and stores that value constantly from the VMContext.

(Core Wasmtime maintainer here, and I built our epochs mechanism when I realized we could do better than fuel if one doesn't need the determinism, only periodic yields)