c - abort() 在 glibc 版本 < 2.27 中不是异步信号安全的吗?

标签 c linux posix

我正在阅读 signal safety在联机帮助页中,abort 是 POSIX 标准要求的异步信号安全功能之一。 但是,根据 abort 的联机帮助页, 它说

Up until glibc 2.26, if the abort() function caused process termination, all open streams were closed and flushed (as with fclose(3)).

fclose 未列为异步信号安全(尽管 close 是)。 这是否意味着 abort 在 2.27 之前的 glibc 版本中不是异步信号安全的?

我这么想的另一个原因是 exit(3) 不是异步信号安全的,因为它会刷新 stdio 缓冲区,这正是 abort 使用的做。

最佳答案

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?

是的,你是对的。 abort 在早期版本中不是异步信号安全的。在某个阶段,它会[尝试]fclose所有打开的。而且,它将通过它设置的内部信号处理程序执行此操作。

给定的流可能被锁定或处于不确定状态。 TL;DR 是 abort 不是 异步安全的,因为 fclose 是[并且] 不是 异步安全的。

缺乏异步安全的原因之一是 malloc 等。阿尔。 异步安全。尽管它们将访问包装在互斥体中,但它们不会屏蔽信号。进程/线程可以在涉及执行 realloc 的流上执行 set*buf。信号处理程序中的调用可能会损坏堆和/或崩溃。


能够查看较旧的源代码可能会有所帮助。这是 glibc 源代码页面:

https://www.gnu.org/software/libc/sources.html

在该页面上,我们可以克隆 glibc git 存储库:

git clone https://sourceware.org/git/glibc.git
cd glibc
git checkout master

这将为我们提供一个相当现代的版本。

但是,只要稍微改变那里的说明,我们就可以检查早期版本:

git checkout release/2.25/master

然后,如果我们查看 stdlib/abort.c,我们会看到:

  /* Now close the streams which also flushes the output the user
     defined handler might has produced.  */
  if (stage == 4)
    {
      ++stage;
      __fcloseall ();
    }

在以后的版本中,第 4 阶段缺少 __fcloseall 调用:

  /* Now try to abort using the system specific command.  */
  if (stage == 4)
    {
      ++stage;
      ABORT_INSTRUCTION;
    }

这是 2.25 到当前的 [手动] 差异:

--- 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);

关于c - abort() 在 glibc 版本 < 2.27 中不是异步信号安全的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74766327/

相关文章:

c - ESP 32 中断问题

c++ - 使用 POSIX 从 SERIAL 也读取 null

c - 寻找表生成宏惯用语的良好解释

linux - ksh:将函数输出分配给数组

c - Scrabble 小游戏程序中的 Stack smashing

python 原始套接字 : Protocol not supported

c - 在非规范模式下使用终端 IO 确定按钮边界

c - "Segmentation fault"同时执行动态 malloc 代码

带有命令行选项的 C 程序

c - gdb 评估进程核心中的函数