Re: sigaction.2: clarification for SA_NODEFER needed

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Kernel Documentation]     [Netdev]     [Linux Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux