• Re: signal handling issues

    From Rainer Weikusat@21:1/5 to Muttley@dastardlyhq.com on Tue Feb 4 18:55:25 2025
    Muttley@dastardlyhq.com writes:
    Lawrence D'Oliveiro <ldo@nz.invalid> gabbled:
    On Fri, 31 Jan 2025 09:15:39 -0000 (UTC), Muttley wrote:

    All this confusion and complexity is why I always recommend that in a
    multithreaded program if you must use signals then all signals should be >>> blocked with a single thread dedicated to sitting in sigwait() and handling >>> whatever comes along.

    Or use signalfd(2) <https://manpages.debian.org/signalfd(2)>.

    I never knew about that, very useful! Unfortunately it appears to be linux only.

    I've started to use it exclusively in the last couple of larger programs
    I wrote for my employer.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rainer Weikusat@21:1/5 to All on Thu Jan 30 17:57:45 2025
    Up to and including The Open Group Base Specifications Issue 6, the
    following program was a correct way of using signal handlers:

    ----
    #include <signal.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>

    static void alarmed(int unused)
    {
    puts("I was alarmed!");
    alarm(2);
    }

    static void interrupted(int unused)
    {
    puts("I was interrupted!");
    exit(0);
    }

    int main(void)
    {
    struct sigaction sa;
    sigset_t omask;

    sigemptyset(&sa.sa_mask);
    sigaddset(&sa.sa_mask, SIGALRM);
    sigaddset(&sa.sa_mask, SIGINT);
    sa.sa_flags = 0;

    sigprocmask(SIG_BLOCK, &sa.sa_mask, &omask);

    sa.sa_handler = alarmed;
    sigaction(SIGALRM, &sa, NULL);

    sa.sa_handler = interrupted;
    sigaction(SIGINT, &sa, NULL);

    alarm(2);
    while (1) sigsuspend(&omask);

    return 0;
    }
    ----

    As the only requirement for functions not listed as async-signal-safe
    was

    In the presence of signals, all functions defined by this volume
    of IEEE Std 1003.1-2001 shall behave as defined when called from
    or interrupted by a signal-catching function, with a single
    exception: when a signal interrupts an unsafe function and the
    signal-catching function calls an unsafe function, the behavior
    is undefined.

    Since version 7, its behaviour is undefined because the following
    requirement was added:

    If the process is multi-threaded, or if the process is
    single-threaded and a signal handler is executed other than as
    the result of:


    The process calling abort(), raise(), kill(),
    pthread_kill(), or sigqueue() to generate a signal that is
    not blocked

    A pending signal being unblocked and being delivered before
    the call that unblocked it returns

    the behavior is undefined if the signal handler
    [...]
    calls any function defined in this standard other than one of
    the functions listed in the following table.

    despite the statement from version 6 was retained as is. These two
    requirements seem to contradict each other. It would also be
    interesting to know what the point of this change was to begin with, considering that it decreed that code whose behaviour had hitherto been
    defined would now have undefined behaviour.

    [I think this should be reported as a defect but there doesn't seem to
    be a way to do that or at least no obvious way.]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rainer Weikusat@21:1/5 to Scott Lurndal on Thu Jan 30 19:11:14 2025
    scott@slp53.sl.home (Scott Lurndal) writes:
    Rainer Weikusat <rweikusat@talktalk.net> writes:
    Up to and including The Open Group Base Specifications Issue 6, the >>following program was a correct way of using signal handlers:

    ----

    [example program removed]

    ----

    As the only requirement for functions not listed as async-signal-safe
    was

    In the presence of signals, all functions defined by this volume
    of IEEE Std 1003.1-2001 shall behave as defined when called from
    or interrupted by a signal-catching function, with a single
    exception: when a signal interrupts an unsafe function and the
    signal-catching function calls an unsafe function, the behavior
    is undefined.

    Since version 7, its behaviour is undefined because the following >>requirement was added:

    If the process is multi-threaded, or if the process is
    single-threaded and a signal handler is executed other than as
    the result of:

    [signal being synchronously raised

    ... otherwise, behaviour always undefined if handler calls any unsafe
    function regardless of the older requirement quoted above]

    [...]

    once multithreading was introduced, the stdio library functions were
    required to synchronize access to the underlying FILE buffers between threads. As those are library functions which invoke kernel system
    calls, unless they mask signals, they can be interrupted while they
    own exclusive access to the stream.

    Subsequent accesses from a signal handler to that stream
    will block indefinitely.

    These functions were never async-signal-safe and I don't quite
    understand why this locking would necessarily conflict with the
    requirement that unsafe functions shall behave as documented when called
    from a signal handler or interrupted by a signal unless the signal
    handler interrupted an unsafe function and also called an unsafe
    function.

    For stdio, this basically just means that a stream used by some stdio
    routine must be unlocked by the time it returns to its caller.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Rainer Weikusat on Thu Jan 30 18:32:57 2025
    Rainer Weikusat <rweikusat@talktalk.net> writes:
    Up to and including The Open Group Base Specifications Issue 6, the
    following program was a correct way of using signal handlers:

    ----
    #include <signal.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>

    static void alarmed(int unused)
    {
    puts("I was alarmed!");
    alarm(2);
    }

    static void interrupted(int unused)
    {
    puts("I was interrupted!");
    exit(0);
    }

    int main(void)
    {
    struct sigaction sa;
    sigset_t omask;

    sigemptyset(&sa.sa_mask);
    sigaddset(&sa.sa_mask, SIGALRM);
    sigaddset(&sa.sa_mask, SIGINT);
    sa.sa_flags = 0;

    sigprocmask(SIG_BLOCK, &sa.sa_mask, &omask);

    sa.sa_handler = alarmed;
    sigaction(SIGALRM, &sa, NULL);

    sa.sa_handler = interrupted;
    sigaction(SIGINT, &sa, NULL);

    alarm(2);
    while (1) sigsuspend(&omask);

    return 0;
    }
    ----

    As the only requirement for functions not listed as async-signal-safe
    was

    In the presence of signals, all functions defined by this volume
    of IEEE Std 1003.1-2001 shall behave as defined when called from
    or interrupted by a signal-catching function, with a single
    exception: when a signal interrupts an unsafe function and the signal-catching function calls an unsafe function, the behavior
    is undefined.

    Since version 7, its behaviour is undefined because the following
    requirement was added:

    If the process is multi-threaded, or if the process is
    single-threaded and a signal handler is executed other than as
    the result of:

    While Geoff may chime in with the definitive answer, once
    multithreading was introduced, the stdio library functions
    were required to synchronize access to the underlying
    FILE buffers between threads. As those are library functions
    which invoke kernel system calls, unless they mask signals, they can
    be interrupted while they own exclusive access to the stream.

    Subsequent accesses from a signal handler to that stream
    will block indefinitely.

    Even non-multithreaded processes in an implementation that supports
    threading would have the same issue assuming a common libc implementation.

    It is possible that the wording was tightened in issue 7
    to reflect these constraints.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Rainer Weikusat on Thu Jan 30 19:44:45 2025
    On 2025-01-30, Rainer Weikusat <rweikusat@talktalk.net> wrote:
    despite the statement from version 6 was retained as is. These two requirements seem to contradict each other. It would also be

    The older requirement can be interpreted as saying that if a signal goes
    off, and the only functions in the interrupted call stack are async-safe functions, then the handler may call async-unsafe functions.
    This situation is outside of the conditions for undefined behavior
    given in that requirement (handler calling unsafe, while interrupting
    unsafe).

    The newer requirement seems to say that if an asynchronous signal goes
    off, the handler may not call async-unsafe functions, regardless of what
    is in the call stack. Even if no POSIX function at all has been
    interrupted (only the application's own code), the behavior is undefined
    if an unsafe function is called.

    So yes, these requirements conflict.

    [I think this should be reported as a defect but there doesn't seem to
    be a way to do that or at least no obvious way.]

    Austin Group mailing list or whatever.


    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Scott Lurndal on Thu Jan 30 19:49:58 2025
    On 2025-01-30, Scott Lurndal <scott@slp53.sl.home> wrote:
    Rainer Weikusat <rweikusat@talktalk.net> writes:
    If the process is multi-threaded, or if the process is
    single-threaded and a signal handler is executed other than as
    the result of:

    While Geoff may chime in with the definitive answer, once
    multithreading was introduced, the stdio library functions
    were required to synchronize access to the underlying
    FILE buffers between threads.

    But the requirement applies to a single-threaded program in
    which an asynchronous signal occurs.

    The multi-threaded case seems to say that if the program is MT,
    then if a signal occurs even as a result of the program
    calling raise, etc, it is effectivecly an async signal
    (since it happens as the result of a thread executing
    asynchronously with regard to whatever thread catches the signal
    via a handler.)

    As those are library functions
    which invoke kernel system calls, unless they mask signals, they can
    be interrupted while they own exclusive access to the stream.

    Subsequent accesses from a signal handler to that stream
    will block indefinitely.

    But those functions were never dubbed async safe; the first requirement
    quoted by Rainer from Issue 6 already makes it undefined to call putc
    from a signal handler interrupting putc.

    That covers all situations in which the interrupt would observe
    stdio to be in any kind of bad state, including holding a mutex.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dan Cross@21:1/5 to 643-408-1753@kylheku.com on Thu Jan 30 22:51:36 2025
    In article <20250130113438.837@kylheku.com>,
    Kaz Kylheku <643-408-1753@kylheku.com> wrote:
    On 2025-01-30, Rainer Weikusat <rweikusat@talktalk.net> wrote:
    despite the statement from version 6 was retained as is. These two
    requirements seem to contradict each other. It would also be

    The older requirement can be interpreted as saying that if a signal goes
    off, and the only functions in the interrupted call stack are async-safe >functions, then the handler may call async-unsafe functions.
    This situation is outside of the conditions for undefined behavior
    given in that requirement (handler calling unsafe, while interrupting >unsafe).

    The newer requirement seems to say that if an asynchronous signal goes
    off, the handler may not call async-unsafe functions, regardless of what
    is in the call stack. Even if no POSIX function at all has been
    interrupted (only the application's own code), the behavior is undefined
    if an unsafe function is called.

    So yes, these requirements conflict.

    [I think this should be reported as a defect but there doesn't seem to
    be a way to do that or at least no obvious way.]

    Austin Group mailing list or whatever.

    Austin group has a defect tracker; one must be subscribed to the
    mailing list to submit an issue.

    I don't think this is a defect.

    - Dan C.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Muttley@DastardlyHQ.org@21:1/5 to All on Fri Jan 31 09:15:39 2025
    On Thu, 30 Jan 2025 19:49:58 -0000 (UTC)
    Kaz Kylheku <643-408-1753@kylheku.com> wibbled:
    On 2025-01-30, Scott Lurndal <scott@slp53.sl.home> wrote:
    Rainer Weikusat <rweikusat@talktalk.net> writes:
    If the process is multi-threaded, or if the process is
    single-threaded and a signal handler is executed other than as
    the result of:

    While Geoff may chime in with the definitive answer, once
    multithreading was introduced, the stdio library functions
    were required to synchronize access to the underlying
    FILE buffers between threads.

    But the requirement applies to a single-threaded program in
    which an asynchronous signal occurs.

    The multi-threaded case seems to say that if the program is MT,
    then if a signal occurs even as a result of the program
    calling raise, etc, it is effectivecly an async signal
    (since it happens as the result of a thread executing
    asynchronously with regard to whatever thread catches the signal
    via a handler.)

    All this confusion and complexity is why I always recommend that in a multithreaded program if you must use signals then all signals should be blocked with a single thread dedicated to sitting in sigwait() and handling whatever comes along.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Geoff Clare@21:1/5 to Geoff Clare on Thu Feb 6 14:39:42 2025
    Geoff Clare wrote:

    After some digging I found the source of the change,
    which was this bug:

    https://austingroupbugs.net/view.php?id=66

    It updated some text on the signal() page and then copied it into
    XSH 2.4.3.

    So the conflict Rainer has identified was already there in Issue 6,
    but only for signal handlers installed using signal(), not sigaction(). Obviously it made no sense for there to be different signal handler
    rules depending on which function installed the handler, hence the
    copying.

    I have now reported the problem to the Austin Group. See https://austingroupbugs.net/view.php?id=1905

    --
    Geoff Clare <netnews@gclare.org.uk>

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rainer Weikusat@21:1/5 to Geoff Clare on Thu Feb 6 22:31:47 2025
    Geoff Clare <geoff@clare.See-My-Signature.invalid> writes:
    Geoff Clare wrote:

    After some digging I found the source of the change,
    which was this bug:

    https://austingroupbugs.net/view.php?id=66

    It updated some text on the signal() page and then copied it into
    XSH 2.4.3.

    So the conflict Rainer has identified was already there in Issue 6,
    but only for signal handlers installed using signal(), not sigaction().
    Obviously it made no sense for there to be different signal handler
    rules depending on which function installed the handler, hence the
    copying.

    I have now reported the problem to the Austin Group. See https://austingroupbugs.net/view.php?id=1905

    Thanks. I was thinking on-and-off if I should do this myself but I would certainly have made a much worse job of it due to being entirely
    unfamiliar with the processes involved here.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rainer Weikusat@21:1/5 to Dan Cross on Fri Jan 31 11:44:07 2025
    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <20250130113438.837@kylheku.com>,
    Kaz Kylheku <643-408-1753@kylheku.com> wrote:
    On 2025-01-30, Rainer Weikusat <rweikusat@talktalk.net> wrote:
    despite the statement from version 6 was retained as is. These two
    requirements seem to contradict each other. It would also be

    The older requirement can be interpreted as saying that if a signal goes >>off, and the only functions in the interrupted call stack are async-safe >>functions, then the handler may call async-unsafe functions.
    This situation is outside of the conditions for undefined behavior
    given in that requirement (handler calling unsafe, while interrupting >>unsafe).

    The newer requirement seems to say that if an asynchronous signal goes
    off, the handler may not call async-unsafe functions, regardless of what
    is in the call stack. Even if no POSIX function at all has been
    interrupted (only the application's own code), the behavior is undefined
    if an unsafe function is called.

    So yes, these requirements conflict.

    [I think this should be reported as a defect but there doesn't seem to
    be a way to do that or at least no obvious way.]

    Austin Group mailing list or whatever.

    Austin group has a defect tracker; one must be subscribed to the
    mailing list to submit an issue.

    I don't think this is a defect.

    Mandating that functions defined by the standard behave as documented
    when either called by a signal handler or interrupted by a signal unless
    the signal handler interrupted an unsafe function and calls an unsafe
    function while declaring that the behaviour is undefined if the signal
    actually calls an unsafe function at least doesn't make any sense: How's
    an unsafe function supposed to behave as documentend when called by a
    signal handler if the behaviour is undefined when a signal handler
    actually calls such a function?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dan Cross@21:1/5 to rweikusat@talktalk.net on Fri Jan 31 13:12:08 2025
    In article <87h65f6we0.fsf@doppelsaurus.mobileactivedefense.com>,
    Rainer Weikusat <rweikusat@talktalk.net> wrote:
    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <20250130113438.837@kylheku.com>,
    Kaz Kylheku <643-408-1753@kylheku.com> wrote:
    On 2025-01-30, Rainer Weikusat <rweikusat@talktalk.net> wrote:
    despite the statement from version 6 was retained as is. These two
    requirements seem to contradict each other. It would also be

    The older requirement can be interpreted as saying that if a signal goes >>>off, and the only functions in the interrupted call stack are async-safe >>>functions, then the handler may call async-unsafe functions.
    This situation is outside of the conditions for undefined behavior
    given in that requirement (handler calling unsafe, while interrupting >>>unsafe).

    The newer requirement seems to say that if an asynchronous signal goes >>>off, the handler may not call async-unsafe functions, regardless of what >>>is in the call stack. Even if no POSIX function at all has been >>>interrupted (only the application's own code), the behavior is undefined >>>if an unsafe function is called.

    So yes, these requirements conflict.

    [I think this should be reported as a defect but there doesn't seem to >>>> be a way to do that or at least no obvious way.]

    Austin Group mailing list or whatever.

    Austin group has a defect tracker; one must be subscribed to the
    mailing list to submit an issue.

    I don't think this is a defect.

    Mandating that functions defined by the standard behave as documented
    when either called by a signal handler or interrupted by a signal unless
    the signal handler interrupted an unsafe function and calls an unsafe >function while declaring that the behaviour is undefined if the signal >actually calls an unsafe function at least doesn't make any sense: How's
    an unsafe function supposed to behave as documentend when called by a
    signal handler if the behaviour is undefined when a signal handler
    actually calls such a function?

    That is confusing.

    I believe the intent is to say that it is undefined when a
    signal-catching function causes an unsafe function to be called
    in the context they describe, even if that function is not
    directly invoked from the signal handler. E.g., if one calls
    `longjmp` or `siglongjmp` from the the handler, and then
    whatever one jumps to calls an unsafe function such as `puts`.

    One will note that there is some weasel language in issue 6
    along these lines. For instance, this paragraph:

    |When signal-catching functions are invoked asynchronously with
    |process execution, the behavior of some of the functions
    |defined by this volume of IEEE Std 1003.1-2001 is unspecified
    |if they are called from a signal-catching function.

    Seems self-contradictory in much the same way you are saying
    about the language in later issues.

    POSIX has always tried to thread a very thin needle here, and I
    do agree the language should be tightened up. Filing a report
    on the Austin Group issue tracker is the way to do so.

    - Dan C.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Muttley on Sat Feb 1 22:06:29 2025
    On Fri, 31 Jan 2025 09:15:39 -0000 (UTC), Muttley wrote:

    All this confusion and complexity is why I always recommend that in a multithreaded program if you must use signals then all signals should be blocked with a single thread dedicated to sitting in sigwait() and handling whatever comes along.

    Or use signalfd(2) <https://manpages.debian.org/signalfd(2)>.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Muttley@dastardlyhq.com@21:1/5 to All on Sun Feb 2 09:45:47 2025
    On Sat, 1 Feb 2025 22:06:29 -0000 (UTC)
    Lawrence D'Oliveiro <ldo@nz.invalid> gabbled:
    On Fri, 31 Jan 2025 09:15:39 -0000 (UTC), Muttley wrote:

    All this confusion and complexity is why I always recommend that in a
    multithreaded program if you must use signals then all signals should be
    blocked with a single thread dedicated to sitting in sigwait() and handling >> whatever comes along.

    Or use signalfd(2) <https://manpages.debian.org/signalfd(2)>.

    I never knew about that, very useful! Unfortunately it appears to be linux only.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Geoff Clare@21:1/5 to Scott Lurndal on Mon Feb 3 15:57:53 2025
    Scott Lurndal wrote:

    Rainer Weikusat <rweikusat@talktalk.net> writes:
    Up to and including The Open Group Base Specifications Issue 6, the >>following program was a correct way of using signal handlers:


    As the only requirement for functions not listed as async-signal-safe
    was

    In the presence of signals, all functions defined by this volume
    of IEEE Std 1003.1-2001 shall behave as defined when called from
    or interrupted by a signal-catching function, with a single
    exception: when a signal interrupts an unsafe function and the
    signal-catching function calls an unsafe function, the behavior
    is undefined.

    Since version 7, its behaviour is undefined because the following >>requirement was added:

    If the process is multi-threaded, or if the process is
    single-threaded and a signal handler is executed other than as
    the result of:

    While Geoff may chime in with the definitive answer, once
    multithreading was introduced, [...]

    Although multithreading complicates things, it wasn't the reason for
    this change. After some digging I found the source of the change,
    which was this bug:

    https://austingroupbugs.net/view.php?id=66

    It updated some text on the signal() page and then copied it into
    XSH 2.4.3.

    So the conflict Rainer has identified was already there in Issue 6,
    but only for signal handlers installed using signal(), not sigaction(). Obviously it made no sense for there to be different signal handler
    rules depending on which function installed the handler, hence the
    copying.

    The underlying cause of the problem is the need to match the signal
    handling rules in the C standard (C99 at the time), but with the
    concept of async-signal-safe functions incorporated. It appears that
    mostly this was done from the point of view of what can be safely done
    in a signal handler that could be called at any time. The other aspect,
    of the case when the signal only ever interrupts async-signal-safe
    functions, was never fully realised.

    I think the first paragraph quoted by Rainer above shows the intention,
    but it was not properly allowed for in the text on the signal() page
    that was copied to XSH 2.4.3.

    --
    Geoff Clare <netnews@gclare.org.uk>

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)