I am reading about signal safety in the manpages, and abort
is one of the async signal safe functions as required by the POSIX standard.
However, according to the manpages for abort, it says
Up until glibc 2.26, if the abort() function caused process termination, all open streams were closed and flushed (as with fclose(3)).
fclose
is not listed as async-signal-safe (although close
is).
Does this mean that abort
was not async-signal-safe in glibc versions before 2.27?
Another reason why I'm thinking this is that exit(3)
is not async-signal-safe because it flushes the stdio buffers, which is what abort
used to do.
CodePudding user response:
fclose
is not listed as async-signal-safe (althoughclose
is). Does this mean thatabort
was not async-signal-safe in glibc versions before 2.27?
Yes, you are correct. abort
was not async-signal-safe in the earlier versions. At a certain stage, it would [try to] fclose
all open streams. And, it will do this from an internal signal handler it sets up.
A given stream could be locked or in an indeterminate state. The TL;DR is that abort
was not async safe because fclose
was [and is] not async safe.
It might help to be able to look at the older source. Here is the glibc
source page:
https://www.gnu.org/software/libc/sources.html
On that page, we can clone the glibc
git repository:
git clone https://sourceware.org/git/glibc.git
cd glibc
git checkout master
This will give us a fairly modern version.
But, with a slight change to the instructions there, we can checkout an earlier version:
git checkout release/2.25/master
Then, if we look at stdlib/abort.c
, we see:
/* Now close the streams which also flushes the output the user
defined handler might has produced. */
if (stage == 4)
{
stage;
__fcloseall ();
}
In later versions, stage 4 is missing the __fcloseall
call:
/* Now try to abort using the system specific command. */
if (stage == 4)
{
stage;
ABORT_INSTRUCTION;
}
Here's a [manual] diff of 2.25 to current:
--- abort.c 2022-12-11 22:42:33.064470777 -0500
/tmp/abort.c 2022-12-11 22:42:21.106728443 -0500
@@ -1,4 1,4 @@
-/* Copyright (C) 1991-2017 Free Software Foundation, Inc.
/* Copyright (C) 1991-2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
@@ -13,7 13,7 @@
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
- <http://www.gnu.org/licenses/>. */
<https://www.gnu.org/licenses/>. */
#include <libc-lock.h>
#include <signal.h>
@@ -21,6 21,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <internal-signals.h>
/* Try to get a machine dependent instruction which will make the
program crash. This is used in case everything else fails. */
@@ -30,11 31,8 @@
# define ABORT_INSTRUCTION
#endif
-#include <libio/libioP.h>
-#define fflush(s) _IO_flush_all_lockp (0)
-
/* Exported variable to locate abort message in core files etc. */
-struct abort_msg_s *__abort_msg __attribute__ ((nocommon));
struct abort_msg_s *__abort_msg;
libc_hidden_def (__abort_msg)
/* We must avoid to run in circles. Therefore we remember how far we
@@ -50,32 48,24 @@
abort (void)
{
struct sigaction act;
- sigset_t sigs;
/* First acquire the lock. */
__libc_lock_lock_recursive (lock);
/* Now it's for sure we are alone. But recursive calls are possible. */
- /* Unlock SIGABRT. */
/* Unblock SIGABRT. */
if (stage == 0)
{
stage;
- if (__sigemptyset (&sigs) == 0 &&
- __sigaddset (&sigs, SIGABRT) == 0)
- __sigprocmask (SIG_UNBLOCK, &sigs, (sigset_t *) NULL);
- }
-
- /* Flush all streams. We cannot close them now because the user
- might have registered a handler for SIGABRT. */
- if (stage == 1)
- {
- stage;
- fflush (NULL);
internal_sigset_t sigs;
internal_sigemptyset (&sigs);
internal_sigaddset (&sigs, SIGABRT);
internal_sigprocmask (SIG_UNBLOCK, &sigs, NULL);
}
/* Send signal which possibly calls a user handler. */
- if (stage == 2)
if (stage == 1)
{
/* This stage is special: we must allow repeated calls of
`abort' when a user defined handler for SIGABRT is installed.
@@ -93,7 83,7 @@
}
/* There was a handler installed. Now remove it. */
- if (stage == 3)
if (stage == 2)
{
stage;
memset (&act, '\0', sizeof (struct sigaction));
@@ -103,30 93,22 @@
__sigaction (SIGABRT, &act, NULL);
}
- /* Now close the streams which also flushes the output the user
- defined handler might has produced. */
- if (stage == 4)
- {
- stage;
- __fcloseall ();
- }
-
/* Try again. */
- if (stage == 5)
if (stage == 3)
{
stage;
raise (SIGABRT);
}
/* Now try to abort using the system specific command. */
- if (stage == 6)
if (stage == 4)
{
stage;
ABORT_INSTRUCTION;
}
/* If we can't signal ourselves and the abort instruction failed, exit. */
- if (stage == 7)
if (stage == 5)
{
stage;
_exit (127);