I'm running an instance of wasm code. I want to limit it to ~Xms of runtime - after that, I want to kill the instance.
Is epoch-based interruptions good for this use case? Does epoch based interruption guarantee that the interrupt will happen, or could a malicious wasm module spin loop or something and prevent it from happening?
Additionally, what is the intended way to use epochs? Have a thread call Increment_epoch() every 1ms, and set the epoch deadline to X ticks?
Have a thread sleep Xms, then call increment_epoch() once and set the epoch deadline to 1 tick?
@JMS either of those driver patterns would work, it's really up to you and the architecture of your application!
response time to an epoch interruption should be bounded by the maximum straight-line body length of one function; every loop backedge and every function entry checks for the epoch deadline
response time to an epoch interruption should be bounded by the maximum straight-line body length of one function; every loop backedge and every function entry checks for the epoch deadline
Can I get an eli5 on this? :sweat_smile:
Sorry, basically it's an answer to your question "does epoch based interruption guarantee that the interrupt will happen, or could a malicious wasm module spin loop or something and prevent it from happening? " that means "yes, it guarantees the interrupt will happen"
Ah wait, do you mean every iteration of a loop, and every fn call checks the epoch?
So you can't use loops or recursion to infinitely prevent the epoch rinteruppt ight?
right, exactly. loops and recursion are the two ways to get truly unbounded runtime, and we check at regular intervals during both
just with function calls one could blow up runtime (but still have bounded runtime), by having a tree of functions each of which calls its child ten times; but we guard against that by checking in every function entry as well
Awesome, ty! I also want to suggest that this gets added to the documentation - I don't see mention of it anywhere.
I think it should be in the docs on Config::epoch_interruption
? but I haven't looked in a bit and if not we always welcome doc prs to make things clearer!
I don't see it? https://docs.rs/wasmtime/latest/wasmtime/struct.Config.html#method.epoch_interruption
Yeah, I can make a PR :)
later today*
hmm, yeah, maybe I'm remembering just an implementation-level comment on how it works and why it's bounded then
ok cool, thanks!
Wait a minute, epochs are per engine? Ugh. My game uses the same engine on multiple threads, to run multiple wasm instances at once. Incrementing the epoch would interuppt _all_ of them, not just the one I mean to interuppt right? Is there a way to do per-store epochs or something?
currently no; maybe it could be made configurable; @Alex Crichton would probably have a better head for the API-design tradeoffs here
Through the use of Store::epoch_deadline_callback
I think you can have per-store limits where you can have custom logic for when the epoch comes up of what to do (e.g. "oops interrupted too soon" or "ok time to die, return a trap")
otherwise though each store has its own limit of what epoch it will "stop" at and that can be configured separately in each store relative to when wasm starts executing
so there can be like one timer thread updating the epoch at 60hz or so and you could configure a separate deadline for each store depending on when it starts
Hmm yeah, so one thread incrementing every 1ms, and then have each store set deadline to "current + DURATION" would work well. Question is, can I get the current epoch from the engine somehow? I don't see a method to do so.
The method to set the deadline works in a relative way, starting at the current tick: "Sets the epoch deadline to a certain number of ticks in the future." (Store::set_epoch_deadline
)
Ah, makes sense, ty
Does it set the epoch based on the time I call the function, or the time the instance starts?
Like, should I call it right before I run the instance, after setup? Or is it ok to do it during the store creation.
it sets it when the function is called, not on each wasm invocation
so you'd want to configure it just before calling wasm
Ok, interesting. So I was setting it too early before, and by the time I called the wasm, it was already past the deadline. So the wasm code ended up being able to run an infinite loop :grimacing:
It should be the case that the wasm immediately trapped rather than actually running anything, once the deadline is passed wasm won't make any more progress
Hmm, that definitely wasn't the case. My logs showed it was making no progress - nothing was printing. Something seems strange about it.
Now that I set the deadline right before running the call, it's behaving even more weird. Setting a 9s deadline somehow makes it trap every single time. But disabling epochs has it run hundreds of times a second!
Oooh crap - I forgot to put the increment_epoch() in a loop :sweat_smile:
Nope, it's still behaving strange, ugh
Replacing my wasm code with an empty function makes it trap still. No way an empty function is taking more than 9s to run, when a non-empty function takes 63 microseconds...
Question: If I never call https://docs.rs/wasmtime/latest/wasmtime/struct.Store.html#method.set_epoch_deadline, but have epoch interrupts enabled, what happens? Is there a default deadline?
I've narrowed it down to https://github.com/JMS55/botnet/blob/master/src/compute_bot_action.rs#L40 getting interuptted every single time I call it
Ok, so doing store.set_epoch_deadline(10000000000000000000);
at the very start, and then setting a real deadline when I'm ready fixes it.
Opened a bug report https://github.com/bytecodealliance/wasmtime/issues/4342
JMS has marked this topic as resolved.
Last updated: Dec 23 2024 at 12:05 UTC