On 12.11.20 16:37, Michael Kerrisk (man-pages) wrote: > Hello Heinrich, > > On 11/11/20 1:42 AM, Heinrich Schuchardt wrote: >> Hello Michael, >> >> I have been writing a handler for SIGILL and SIGSEGV which restarts the >> program using execv() if an exception occurs. The handler never returns. >> >> >From the description of SA_NODEFER it was not evident that even if the >> process is restarted with execv() the signal remains masked if >> SA_NODEFER is not set as a flag. >> >> It think this behavior deserves mentioning on the sigaction.2 manpage, e.g. >> >> "Do not prevent the signal from being received from within its own >> signal handler. A signal handler call is not terminated by calling >> execv() as the pending signal property is inherited by the new process." >> >> On the signal.7 manpage there is a paragraph "Signal mask and pending >> signals". Here pending signals are mentioned. There is a sentence >> "Between the time when it is generated and when it is delivered a signal >> is said to be pending." >> >> To me "delivered" means the instance when the signal handler is called >> and not the instance when the signal handler returns. So said sentence >> should be reworked, e.g. >> >> "Between the time when it is generated and when the signal handler >> returns a signal is said to be pending." > > Your description above seems to conflate two concepts: the signal mask > and pending signals. The sentence that you propose reworking is > correct as it stands. > > The sequence of events goes like this: > > 1. Signal is generated > > [At this point the signal is pending, but typically the interval > between this step and the next is typically so brief that the fact > that the signal is pending is unobservable] > > 2. The kernel delivers the signal to the process: > > + The signal is removed from the set of pending signals. > + The signal is added to the signal mask (unless SA_NOFER > was specified) > + The kernel constructs a frame for the signal handler on the > user-space stack. The return address in that frame points to a > small piece of code in user-space called the signal trampoline. > + The kernel passes control back to the process with execution > commencing at the start of the signal handler. > > 3. The signal handler executes. > > [At this point, the signal is not pending, but it is present in the > signal mask.] > > 4. The signal handler returns. > > 5. Control passes to the signal trampoline, which calls > sigreturn(2). > > 6. When sigreturn(2) is called, the kernel once more has control > and restored various pieces of process state (e.g., the signal > mask) to the values they had before the signal handler was > invoked. > > 7. At completion of the sigreturn(2) system call, the kernel passes control > back to the user-space program with execution recommencing at the > point where the main program was interrupted by the signal handler. > > Now, if you interrupt things before step 4 (e.g., with exec(), > swapcontext(3), or possibly--depending on how sigsetjmp() was > called--siglongjmp()), then of course the remaining steps are not > performed. > > I've added a few words to the SA_NODEFER description to hopefully > further clarify what it does: > > SA_NODEFER > Do not prevent the signal from being received from within > its own signal handler (i.e., do not add the signal to the > thread's signal mask while the handler is executing). > > Probably, the signal(7) manual page could say more about all of this. > What would you think of the following text to be added to that page > > Execution of signal handlers > Whenever there is a transition from kernel-mode to user-mode exe‐ > cution (e.g., on return from a system call or scheduling of a > thread onto the CPU), the kernel checks whether there is a pending > signal for which the process has established a signal handler. If > there is such a pending signal, the following steps occur: > > 1. The kernel performs the necessary preparatory steps for execu‐ > tion of the signal handler: > > a) The signal is removed from the set of pending signals. > > b) If the thread has defined an alternate signal stack (using > sigaltstack(2)), then that stack is installed. > > c) Various pieces of signal-related context are saved into a > "hidden" frame that is created on the stack. The saved in‐ > formation includes: > > + the program counter register (i.e., the address of the > next instruction in the main program that should be exe‐ > cuted when the signal handler returns); > > + the thread's current signal mask; > > + the thread's alternate signal stack settings. > > d) The thread's signal mask is adjusted by adding the signal > (unless the handler was established using the SA_NODEFER > flag) as well as any additional signals specified in > act->sa_mask when sigaction(2) was called. > > 2. The kernel constructs a frame for the signal handler on the > stack. Within that frame, the return address points to a piece > of user-space code called the signal trampoline (described in > sigreturn(2)). > > 3. The kernel passes control back to user-space, where execution > commences at the start of the signal handler function. > > 4. When the signal handler returns, control passes to the signal > trampoline code. > > 5. The signal trampoline calls sigreturn(2), a system call that > uses the information in the "hidden" stack frame to restore the > thread's signal mask and alternate stack settings to their > state before the signal handler was called. Upon completion of > the call to sigreturn(2), the kernel transfers control back to > user space, and the thread recommences execution at the point > where it was interrupted by the signal handler. > > Note that if the signal handler does not return (e.g., control is > transferred out of the handler using sigsetjmp(3) or swapcon‐ > text(3), or the handler executes a new program with execve(2)), > then the final step is not performed. In particular, in such sce‐ > narios it is the programmer's responsibility to restore that state > of the signal mask, if that is desired. Hello Michael, this text is very helpful. "Signal mask and pending signals" already mentions that the signal mask controls the blocking of signals. But maybe you could reiterate this in 1d) and in the note below 5). Best regards Heinrich