mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-28 08:12:28 +00:00
Improve signals and memory protection
- Document sigaction() - Simplify New Technology fork() code - Testing and many bug fixes for mprotect() - Distribute Intel Xed ILD in the amalgamation - Turn Xed enums into defines to avoid DWARF bloat - Improve polyfilling of SA_SIGINFO on BSDs and fix bugs - setpgid(getpid(), getpid()) on Windows will ignore CTRL-C - Work around issues relating to NT mappings being executable - Permit automatic executable stack override via `ape_stack_pf`
This commit is contained in:
parent
c95c9d9508
commit
f684e348d4
76 changed files with 1844 additions and 1121 deletions
|
@ -236,15 +236,64 @@ static int __sigaction(int sig, const struct sigaction *act,
|
|||
* .sa_flags = SA_RESETHAND|SA_RESTART|SA_SIGINFO};
|
||||
* CHECK_NE(-1, sigaction(SIGINT, &sa, NULL));
|
||||
*
|
||||
* Here's an example of the most professional way to handle signals.
|
||||
* It's generally a best practice to have signal handlers do the fewest
|
||||
* number of things possible. The trick is to have your signals work
|
||||
* hand-in-glove with the EINTR errno returned by i/o.
|
||||
* The following flags are supported across platforms:
|
||||
*
|
||||
* - `SA_SIGINFO`: Causes the `siginfo_t` and `ucontext_t` parameters to
|
||||
* be passed. This not only gives you more information about the
|
||||
* signal, but also allows your signal handler to change the CPU
|
||||
* registers. That's useful for recovering from crashes. If you don't
|
||||
* use this attribute, then signal delivery will go a little faster.
|
||||
*
|
||||
* - `SA_RESTART`: Enables BSD signal handling semantics. Normally i/o
|
||||
* entrypoints check for pending signals to deliver. If one gets
|
||||
* delivered during an i/o call, the normal behavior is to cancel the
|
||||
* i/o operation and return -1 with EINTR in errno. If you use the
|
||||
* `SA_RESTART` flag then that behavior changes, so that any function
|
||||
* that's been annotated with @restartable will not return `EINTR` and
|
||||
* will instead resume the i/o operation. This makes coding easier but
|
||||
* it can be an anti-pattern if not used carefully, since poor usage
|
||||
* can easily result in latency issues. It also requires one to do
|
||||
* more work in signal handlers, so special care needs to be given to
|
||||
* which C library functions are @asyncsignalsafe.
|
||||
*
|
||||
* - `SA_RESETHAND`: Causes signal handler to be single-shot. This means
|
||||
* that, upon entry of delivery to a signal handler, it's reset to the
|
||||
* `SIG_DFL` handler automatically. You may use the alias `SA_ONESHOT`
|
||||
* for this flag, which means the same thing.
|
||||
*
|
||||
* - `SA_NODEFER`: Disables the reentrancy safety check on your signal
|
||||
* handler. Normally that's a good thing, since for instance if your
|
||||
* `SIGSEGV` signal handler happens to segfault, you're going to want
|
||||
* your process to just crash rather than looping endlessly. But in
|
||||
* some cases it's desirable to use `SA_NODEFER` instead, such as at
|
||||
* times when you wish to `longjmp()` out of your signal handler and
|
||||
* back into your program. This is only safe to do across platforms
|
||||
* for non-crashing signals such as `SIGCHLD` and `SIGINT`. Crash
|
||||
* handlers should use Xed instead to recover execution, because on
|
||||
* Windows a `SIGSEGV` or `SIGTRAP` crash handler might happen on a
|
||||
* separate stack and/or a separate thread. You may use the alias
|
||||
* `SA_NOMASK` for this flag, which means the same thing.
|
||||
*
|
||||
* - `SA_NOCLDWAIT`: Changes `SIGCHLD` so the zombie is gone and you
|
||||
* can't call `wait()` anymore; similar to SIGCHLD + SIG_IGN but may
|
||||
* still deliver the SIGCHLD.
|
||||
*
|
||||
* - `SA_NOCLDSTOP`: Lets you set `SIGCHLD` handler that's only notified
|
||||
* on exit/termination and not notified on `SIGSTOP`, `SIGTSTP`,
|
||||
* `SIGTTIN`, `SIGTTOU`, or `SIGCONT`.
|
||||
*
|
||||
* Here's an example of the most professional way to handle signals in
|
||||
* an i/o event loop. It's generally a best practice to have signal
|
||||
* handlers do the fewest number of things possible. The trick is to
|
||||
* have your signals work hand-in-glove with the EINTR errno. This
|
||||
* obfuscates the need for having to worry about @asyncsignalsafe.
|
||||
*
|
||||
* static volatile bool gotctrlc;
|
||||
*
|
||||
* void OnCtrlC(int sig) {
|
||||
* gotctrlc = true;
|
||||
* }
|
||||
*
|
||||
* int main() {
|
||||
* size_t got;
|
||||
* ssize_t rc;
|
||||
|
@ -286,7 +335,96 @@ static int __sigaction(int sig, const struct sigaction *act,
|
|||
*
|
||||
* Please note that you can't do the above if you use SA_RESTART. Since
|
||||
* the purpose of SA_RESTART is to restart i/o operations whose docs say
|
||||
* that they're @restartable and read() is one such function.
|
||||
* that they're @restartable and read() is one such function. Here's
|
||||
* some even better news: if you don't install any signal handlers at
|
||||
* all, then your i/o calls will never be interrupted!
|
||||
*
|
||||
* Here's an example of the most professional way to recover from
|
||||
* `SIGSEGV`, `SIGFPE`, and `SIGILL`.
|
||||
*
|
||||
* void ContinueOnCrash(void);
|
||||
*
|
||||
* void SkipOverFaultingInstruction(struct ucontext *ctx) {
|
||||
* struct XedDecodedInst xedd;
|
||||
* xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_LONG_64);
|
||||
* xed_instruction_length_decode(&xedd, (void *)ctx->uc_mcontext.rip, 15);
|
||||
* ctx->uc_mcontext.rip += xedd.length;
|
||||
* }
|
||||
*
|
||||
* void OnCrash(int sig, struct siginfo *si, struct ucontext *ctx) {
|
||||
* SkipOverFaultingInstruction(ctx);
|
||||
* ContinueOnCrash(); // reinstall here in case *rip faults
|
||||
* }
|
||||
*
|
||||
* void ContinueOnCrash(void) {
|
||||
* struct sigaction sa = {.sa_handler = OnSigSegv,
|
||||
* .sa_flags = SA_SIGINFO | SA_RESETHAND};
|
||||
* sigaction(SIGSEGV, &sa, 0);
|
||||
* sigaction(SIGFPE, &sa, 0);
|
||||
* sigaction(SIGILL, &sa, 0);
|
||||
* }
|
||||
*
|
||||
* int main() {
|
||||
* ContinueOnCrash();
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* You may also edit any other CPU registers during the handler. For
|
||||
* example, you can use the above technique so that division by zero
|
||||
* becomes defined to a specific value of your choosing!
|
||||
*
|
||||
* Please note that Xed isn't needed to recover from `SIGTRAP` which can
|
||||
* be raised at any time by embedding `DebugBreak()` or `asm("int3")` in
|
||||
* your program code. Your signal handler will automatically skip over
|
||||
* the interrupt instruction, assuming your signal handler returns.
|
||||
*
|
||||
* The important signals supported across all platforms are:
|
||||
*
|
||||
* - `SIGINT`: When you press Ctrl-C this signal gets broadcasted to
|
||||
* your process session group. This is the normal way to terminate
|
||||
* console applications.
|
||||
*
|
||||
* - `SIGQUIT`: When you press CTRL-\ this signal gets broadcasted to
|
||||
* your process session group. This is the irregular way to kill an
|
||||
* application in cases where maybe your `SIGINT` handler is broken
|
||||
* although, Cosmopolitan Libc ShowCrashReports() should program it
|
||||
* such as to attach a debugger to the process if possible, or else
|
||||
* show a crash report. Also note that in New Technology you should
|
||||
* press CTRL+BREAK rather than CTRL+\ to get this signal.
|
||||
*
|
||||
* - `SIGHUP`: This gets sent to your non-daemon processes when you
|
||||
* close your terminal session.
|
||||
*
|
||||
* - `SIGTERM` is what the `kill` command sends by default. It's the
|
||||
* choice signal for terminating daemons.
|
||||
*
|
||||
* - `SIGUSR1` and `SIGUSR2` can be anything you want. Their default
|
||||
* action is to kill the process. By convention `SIGUSR1` is usually
|
||||
* used by daemons to reload the config file.
|
||||
*
|
||||
* - `SIGCHLD` is sent when a process terminates and it takes a certain
|
||||
* degree of UNIX mastery to address sanely.
|
||||
*
|
||||
* - `SIGALRM` is invoked by `setitimer()` and `alarm()`. It can be
|
||||
* useful for interrupting i/o operations like `connect()`.
|
||||
*
|
||||
* - `SIGTRAP`: This happens when an INT3 instruction is encountered.
|
||||
*
|
||||
* - `SIGILL` happens on illegal instructions, e.g. `UD2`.
|
||||
*
|
||||
* - `SIGABRT` happens when you call `abort()`.
|
||||
*
|
||||
* - `SIGFPE` happens when you divide ints by zero, among other things.
|
||||
*
|
||||
* - `SIGSEGV` and `SIGBUS` indicate memory access errors and they have
|
||||
* inconsistent semantics across platforms like FreeBSD.
|
||||
*
|
||||
* - `SIGWINCH` is sent when your terminal window is resized.
|
||||
*
|
||||
* - `SIGXCPU` and `SIGXFSZ` may be raised if you run out of resources,
|
||||
* which can happen if your process, or the parent process that
|
||||
* spawned your process, happened to call `setrlimit()`. Doing this is
|
||||
* a wonderful idea.
|
||||
*
|
||||
* @return 0 on success or -1 w/ errno
|
||||
* @see xsigaction() for a much better api
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue