WebAssembly / WASI

WebAssembly System Interface
Other
4.93k stars 257 forks source link

signal handling #166

Open mash-graz opened 4 years ago

mash-graz commented 4 years ago

while debating the question of more satisfaying keyboard input control (#161 and #163) i had to notice, that WASI up to now doesn't provide any commands to activate signal handlers.

it provides at least one routine to send signals to the parent (wasi_proc_raise), but it's unable to listen and react to incoming signals. that's a significant limitation, because it makes a lot of sense in practice to e.g. provide cleanup routines in applications, which will be called in case of execution abort etc.

beside this most common utilization it's a simple asynchronous communication mechanism, which has to be seen as closely tied to other input and output capabilities and recognized as a nearly indispensable complement to interact with the outside world. although we perhaps don't need all the support for it's use in process management on POSIX platforms, but we should at least try to satisfy the essential requirements of the C standard library.

cjihrig commented 4 years ago

Signal handling, including __wasi_proc_raise(), was recently removed in https://github.com/WebAssembly/WASI/commit/d07615f2c9b8f0761ccde71dbd13d8bb27551594.

mash-graz commented 4 years ago

thanks for this rectification, which i wasn't aware...

nevertheless i have the say, that the argumentation given in the referred commit comment ("...these weren't useful...") doesn't look very convincing to me.

sure, if you only support an insufficient portion of the traditional signal handling infrastructure, it may look indeed useless to some of us, but improving resp. complete the signal handling support would be IMHO a better choice, then removing it.

i simply don't see any alternative, how this essential functionality could be substituted by other means.

sunfishcode commented 4 years ago

Full POSIX-style signal handling would likely be a massive undertaking, involving complex security, portability, and VM implementation considerations, and probably core wasm semantics changes (outside the scope of WASI).

Signal handlers in POSIX serve a variety of purposes, so it would be interesting if you could say more about what specifically you want to do. If you want to intercept control-c, get notified when the window size changes, clean up temporary files or reset terminal settings, handle program aborts gracefully, or other specific needs, we may be able to help find alternative approaches that don't need signals.

mash-graz commented 4 years ago

Full POSIX-style signal handling would likely be a massive undertaking, involving complex security, portability, and VM implementation considerations, and probably core wasm semantics changes (outside the scope of WASI).

yes @sunfishcode! after digging deeper, i also came to the conclusion, that it's indeed an issue, which is highly related to fundamental wasm limitations.

nevertheless there was already a section dedicated to "Asynchronous Signals" in the "Features to add after the MVP" wasm document, although it's perhaps the most vague described topic of all the mentioned extensions.

but in the meanwhile wasm saw a lot of progress and research efforts concerning exception handling and asynchronous event handling resp. un/rewinding approaches, which look closely related to the general requirements of a more satisfying signal handling beside the basic single trap mechanism provided by wasm MVP.

and although this issue is tied so close to general wasm constraints, it's still a significant question of the WASI project, how to realize a sufficient capable "System Interface" to interact with existing real world operating systems beyond our embedded wasm cages and already available application source code and languages, that we want to run inside them.

if you look around for real world issues, which are related to this issue, you will already find some reports concerning this topic:

Signal handlers in POSIX serve a variety of purposes, so it would be interesting if you could say more about what specifically you want to do. If you want to intercept control-c, get notified when the window size changes, clean up temporary files or reset terminal settings, handle program aborts gracefully, or other specific needs, we may be able to help find alternative approaches that don't need signals.

indeed i came across this question, when i was noodling over the peculiarities of traditional unix terminal handling resp. its capability to toggle signal generation by the local system vs. uncooked control character exchange. its a somehow irritating detail at fist sight, but it makes a lot of sense whenever you want to support the requirements of remote access, which do not allow unserialized signal transport, and local terminal resp. keyboard processing by the same general configuration interface.

and although i agree with your statement, that most of the common cases of signal handling could be realized by additional dedicated event handlers as well -- e.g.proc_exit for cleanup tasks as already mentioned in the proc_raise-removal debate at the nov.-07-meeting-notes --, i personally still would prefer a functional congruence to existing signal handling facilities found in common programming languages and existing operating systems.

maybe i'm wrong, but i simply understand WASI mainly as a smallest common denominator of host system access facilities, which shouldn't be restricted to much by practical usage associations from the outset. the requirements of utilizing WASI on small µCs and much less OS mediated basic interrupt handling do not have much in common with sandboxed code execution in browsers and serverless utilization scenarios or more platform independent reuse of existing general purpose CLI tools, which i'm most focused on. that's why i more inclined to sympathize with a rather traditional resp. more general structured signal handling approach.

btw: do you really see any serious security concerns in regard to signal handling, which would perhaps justify a more restrictive interface design resp. necessary precautions in this case?

joshtriplett commented 4 years ago

Signal handling in the traditional UNIX style of "yank control from wherever it currently is and put it at this handler instead" seems to me like a mistake we shouldn't repeat.

We could, however, provide a module to handle a portable set of signals via a file descriptor, which we could implement via signalfd or similar when available and a handler plus pipe otherwise. (That can't handle things like SIGSEGV, but I don't see how we could reasonably support that "natively".)

Among others, SIGINT and SIGWINCH both make sense.

sbc100 commented 4 years ago

@joshtriplett I agree that those kinds of signal handlers that are more like synchronous interrupt handlers (that stop the world and then allow it then continue) don't make any sense in WebAssembly.

Having said that, I do wonder if some kind SIGFPE handler might be possible though. For example, in JS today you can catch WebAssembly traps. You could then call back into a module's registered signal handler if you so wished. What you can't do is resume from the trap location, or present any kind of useful ucontext_t to the handler.

mash-graz commented 4 years ago

@joshtriplett for the very common case of cleanup procedures in case of application termination, i still would prefer traditional event handlers, because not all kinds of applications do need/have a polling main loop and we also do not have to worry about un/rewinding complications in this case, because the process would simply trap otherwise as well.

for all other usage scenarios i could accept signalfd based access likewise. i just see the drawback, that it would narrow the portability resp. cross platform implementations even more in practice.

programmerjake commented 4 years ago

For SIGINT (and other signals that you want to handle even if the rest of your code is in the middle of something), spawning a new thread to run the signal handler (like on Windows) seems better than Unix's current signal handling method, since you are not restricted by what functions you can call and you don't have to worry about what to do if the signal handler is called in the middle of malloc or similar. For the rest of the asynchronous signals, including SIGWINCH, signalfd sounds like a much better plan. Synchronous signals such as SIGSEGV or SIGFPE should translate into a trap, to be handled once WebAssembly implements trap handlers.

sbc100 commented 4 years ago

@programmerjake WASI doesn't currently have threads. And even if/when we build threads many apps will choose (as they do on the web today) to build without a shared memory (which means without the possibility to more then one thread).

joshtriplett commented 4 years ago

@mash-graz Having default handlers to terminate the application seems perfectly fine. I just want to avoid any implementation that can interrupt a running thread at any arbitrary time and move its control flow to the handler. Down that path lies "signal safe" and locking issues and so many other problems.

mash-graz commented 4 years ago

Having default handlers to terminate the application seems perfectly fine.

fine, that we agree at least on this class of signal handlers, which in fact represent a large share of the practical used signals.

nevertheless we shouldn't forget the fact, that an undifferentiated reception of signals, as it is unfortunately the case in most runtimes right now, leads to very unpleasant side effects. most of the unhanded [IPC] signals should be better caught and ignored instead of causing trap termination.

IMHO it would even make sense, to support the traditional utilization of some particular signals to force debugable core dump output etc.

but concerning the other cases i understand your justified objections. nevertheless it's really nice to e.g. provide some simple kind of SIGHUP handling to instruct a long running server process to reread its configuration etc. sure, this can be archived by other means as well, and perhaps in a much more exemplary manner, but it's still a rather useful control mechanism.

jeff-hykin commented 4 years ago

As some have mentioned, I think it might be useful to split this issue into

If others agree, I think we can open up respective issues for them

jeff-hykin commented 4 years ago

it would be interesting if you could say more about what specifically you want to do

My situation is that I'd like to make a language that compiles to WASI by default, meaning if WASI is missing a core feature, so will the high level language. If synchronous interrupts aren't going to be supported in any form, that will significantly change the language design and add overhead cost in the higher level language. If the user wants to open a massive file, but then they change their mind after 10 seconds of waiting, IMO they should be able to cancel that without killing the entire program.

Use cases (issue summary)

it would be interesting if you could say more about what specifically you want to do

How does wasi plan to (or plan not to) enable these kinds of messages:

  1. (Async interrupts) 1.1 "please stop the whole program ASAP" 1.2 "the terminal window changed its size, just FYI" 1.3 "please switch to a lower-power-consumption/highest-performance mode" 1.4 "reread your configuration file"

  2. (synchronous interrupts) 2.1 "its been ___ sec (or ms), I'm reminding you to stop the synchronous function you're executing" 2.2 "something just happened in the outside world, you might want to cancel your synchronous process"

  3. (vm responses) 3.1 "there was an error performing your requested instruction (out-of-memory, div-by-zero, etc)" 3.2 pause/resume the process 3.3 Freeze and dump everything related to the process for debugging

Discussion Summary (or my understanding of it)

Addressed Use Cases

  1. (Async interrupts) 1.1 "please stop the whole program ASAP" 1.2 "the terminal window changed its size, just FYI" 1.3 "please switch to a lower-power-consumption/highest-performance mode" 1.4 "reread your configuration file"

3.1 "there was an error performing your requested instruction (out-of-memory, div-by-zero, etc)"

Someone correct me, but I think these would happen (or at least ideally happen) synchronously at the level of the instruction, not as an event at all but as a kind of return value.

3.2 pause/resume the process 3.3 Freeze and dump everything related to the process for debugging

While this is being addressed since it's related to C signals, I think for WASM this would be handled at the level of the VM, and probably isn't as relevant to the rest of the discussion.

jeff-hykin commented 4 years ago

Synchronous Interrupts

I agree the "yank control from wherever" is a mistake. I don't think that is the only method for for solving the very big need of killing unresponsive 3rd-party function calls though.

Somehow there is a way for a whole process to be force-killed ("pulling the plug"), even if its in the middle of writing a file or some other important un-reversible action.

What if there was a restricted (and computationally costly) way to save an execution context, and, for a particular section of code, tell the VM "if a __ signal occurs, go here with that context". Then if the VM gets the signal, it basically force-kills the process, but resumes at the designated place with the designated context.

for illustration, at a high level it might look something like

fallbackTo( GetContext(), SIGNAL_TIMER ) {

    start_timer(2000)
    while(1){ /* dangerously write to IO */ }

}
// jumps to here with the old context if the signal occurs inside the brackets

This would provide much tighter control, and is closer to error handling rather than just injecting a goto into whatever random location the program is currently at.

SoniEx2 commented 4 months ago

we think wasi should implement full posix signal handling.

the most obvious benefit is that wasi can show everyone else how to make it safe. in fact... wasi already has the entire framework to be able to do it, it just needs to choose to.

sync signals are... well, you just run them normally, that's fine; async signals are where the fun is at, and you obviously can't interrupt a running wasm vm (think an interpreter - how would you even interrupt that).

but, with some care, you can just spawn a new one.

in fact, after reading the discussion on this issue, we haven't seen anyone propose something as debatably ridiculous as defining an entire separate world for signal handlers. so we would like to be the first to do it. here's a rough draft:

The Signals World (wasi-signal)

Similar to wasi-cli and wasi-threads, wasi-signal defines a world in which a program runs. Unlike wasi-threads, it doesn't replace the program's world, but defines its own, and is compatible with both wasi-cli and wasi-threads. It works by exposing an API to wasi-cli and wasi-threads:

wasi_signal(...);

When this is called, the WASI host looks for a wasi-signal module to load which it can later instantiate to handle (some) asynchronous signals.

The wasi-signal world imposes strict requirements on the module, for example:

Further, compilers targeting wasi-signal MUST NOT include signal-unsafe functions in the wasi-signal module. For example, a compiler MUST NOT include the malloc and free functions.


we expect no compilers to implement this, because it involves compiling your code twice. even then, that's still a win. if you really do wanna use signal handlers, you should be made to put up with the full restrictions they come with.

fitzgen commented 4 months ago

@SoniEx2

we think wasi should implement full posix signal handling.

Please re-read @sunfishcode's comment upthread:

Full POSIX-style signal handling would likely be a massive undertaking, involving complex security, portability, and VM implementation considerations, and probably core wasm semantics changes (outside the scope of WASI).

Signal handlers in POSIX serve a variety of purposes, so it would be interesting if you could say more about what specifically you want to do. If you want to intercept control-c, get notified when the window size changes, clean up temporary files or reset terminal settings, handle program aborts gracefully, or other specific needs, we may be able to help find alternative approaches that don't need signals.

Nothing has fundamentally changed since that comment was made.


POSIX signals, in their full generality, are infamously tricky, footgun-y, and generally not a paragon of API design.

Synchronous signals, where any instruction can be interrupted (possibly in the middle of a wasm instruction??) would be a disastrous addition to WASI, IMO.

Exposing particular asynchronous signals to WASI via streams, similar to signalfd, seems very reasonable, on the other hand.

This is why @sunfishcode was asking for use cases, and why I qualified my earlier statement with "in their full generality": we can come up with solutions to address just about every particular thing applications want to do, but we cannot add "full posix signal handling" to WASI.

SoniEx2 commented 4 months ago

we understand. in POSIX, signal handlers are mostly used to hardware-accelerate null checks (in e.g. the JVM; this can't be replaced with signalfd), but wasm traps work slightly different from that (and that's okay).

but the argument we're making is that "possibly in the middle of a wasm instruction" is a safe place to interrupt if you're using an entirely separate wasm state. our use-case is to show ppl that this is not only possible, but also safer than in native C, and to show ppl that worlds are one of wasi's greatest strengths. and for that... what better place to start than infamously dangerous signals.