glibc/signal
Adhemerval Zanella d40ac01cbb stdlib: Make abort/_Exit AS-safe (BZ 26275)
The recursive lock used on abort does not synchronize with a new process
creation (either by fork-like interfaces or posix_spawn ones), nor it
is reinitialized after fork().

Also, the SIGABRT unblock before raise() shows another race condition,
where a fork or posix_spawn() call by another thread, just after the
recursive lock release and before the SIGABRT signal, might create
programs with a non-expected signal mask.  With the default option
(without POSIX_SPAWN_SETSIGDEF), the process can see SIG_DFL for
SIGABRT, where it should be SIG_IGN.

To fix the AS-safe, raise() does not change the process signal mask,
and an AS-safe lock is used if a SIGABRT is installed or the process
is blocked or ignored.  With the signal mask change removal,
there is no need to use a recursive loc.  The lock is also taken on
both _Fork() and posix_spawn(), to avoid the spawn process to see the
abort handler as SIG_DFL.

A read-write lock is used to avoid serialize _Fork and posix_spawn
execution.  Both sigaction (SIGABRT) and abort() requires to lock
as writer (since both change the disposition).

The fallback is also simplified: there is no need to use a loop of
ABORT_INSTRUCTION after _exit() (if the syscall does not terminate the
process, the system is broken).

The proposed fix changes how setjmp works on a SIGABRT handler, where
glibc does not save the signal mask.  So usage like the below will now
always abort.

  static volatile int chk_fail_ok;
  static jmp_buf chk_fail_buf;

  static void
  handler (int sig)
  {
    if (chk_fail_ok)
      {
        chk_fail_ok = 0;
        longjmp (chk_fail_buf, 1);
      }
    else
      _exit (127);
  }
  [...]
  signal (SIGABRT, handler);
  [....]
  chk_fail_ok = 1;
  if (! setjmp (chk_fail_buf))
    {
      // Something that can calls abort, like a failed fortify function.
      chk_fail_ok = 0;
      printf ("FAIL\n");
    }

Such cases will need to use sigsetjmp instead.

The _dl_start_profile calls sigaction through _profil, and to avoid
pulling abort() on loader the call is replaced with __libc_sigaction.

Checked on x86_64-linux-gnu and aarch64-linux-gnu.

Reviewed-by: DJ Delorie <dj@redhat.com>
2024-10-08 14:40:12 -03:00
..
bits/types
sys
allocrtsig.c
kill.c
killpg.c
libc_sigaction.c
Makefile
raise.c
sigaction.c
sigaddset.c
sigaltstack.c
sigandset.c
sigblock.c
sigdelset.c
sigempty.c
sigfillset.c
siggetmask.c
sighold.c
sigignore.c
sigintr.c
sigisempty.c
sigismem.c
signal.c
signal.h
sigorset.c
sigpause.c
sigpending.c
sigprocmask.c
sigqueue.c
sigrelse.c
sigreturn.c
sigset.c
sigsetmask.c
sigsetops.c
sigstack.c
sigsuspend.c
sigtimedwait.c
sigvec.c
sigwait.c
sigwaitinfo.c
sysv_signal.c
tst-minsigstksz-1.c
tst-minsigstksz-2.c
tst-minsigstksz-3.c
tst-minsigstksz-3a.c
tst-minsigstksz-4.c
tst-minsigstksz-5.c
tst-raise.c
tst-sigaction.c
tst-sigisemptyset.c
tst-signal.c
tst-sigset2.c
tst-sigset.c
tst-sigsimple.c
tst-sigwait-eintr.c
Versions