[libc-commits] [libc] [libc] Fix WIFSIGNALED and WIFSTOPPED for stopped and continued children (PR #235759)
Mike Lothian via libc-commits
libc-commits at lists.llvm.org
Wed Oct 28 03:00:05 PDT 2026
https://github.com/FireBurn updated https://github.com/llvm/llvm-project/pull/235759
>From 591687334fc74a7fda970371a4e791227ae166e7 Mon Sep 17 00:00:00 2001
From: Mike Lothian <mike at fireburn.co.uk>
Date: Mon, 26 Oct 2026 07:16:50 +0000
Subject: [PATCH] [libc] Fix WIFSIGNALED and WIFSTOPPED for stopped and
continued children
WIFSIGNALED treated any non-zero termination signal as a signal, so it
was also true for a stopped child, whose low byte is 0x7f, and for a
continued one (0xffff). WIFSTOPPED masked with 0x7f instead of 0xff, so
it was also true for a continued child.
Make each macro true for exactly one kind of status, as glibc and musl
do, and decode the statuses the kernel produces for exited, signalled, stopped
and continued children. WIFSIGNALED evaluates its argument once.
Assisted-by: Claude
---
.../llvm-libc-macros/linux/sys-wait-macros.h | 4 +-
.../integration/src/unistd/CMakeLists.txt | 16 ++++
.../src/unistd/wait_status_test.cpp | 87 +++++++++++++++++++
3 files changed, 105 insertions(+), 2 deletions(-)
create mode 100644 libc/test/integration/src/unistd/wait_status_test.cpp
diff --git a/libc/include/llvm-libc-macros/linux/sys-wait-macros.h b/libc/include/llvm-libc-macros/linux/sys-wait-macros.h
index d01cfa71ba390..410d4eadb29b0 100644
--- a/libc/include/llvm-libc-macros/linux/sys-wait-macros.h
+++ b/libc/include/llvm-libc-macros/linux/sys-wait-macros.h
@@ -15,8 +15,8 @@
#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
#define WIFCONTINUED(status) ((status) == 0xffff)
#define WIFEXITED(status) (WTERMSIG(status) == 0)
-#define WIFSIGNALED(status) ((WTERMSIG(status) + 1) >= 2)
-#define WIFSTOPPED(status) (WTERMSIG(status) == 0x7f)
+#define WIFSIGNALED(status) (((signed char)(((status) & 0x7f) + 1) >> 1) > 0)
+#define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
#define WSTOPSIG(status) WEXITSTATUS(status)
#define WTERMSIG(status) ((status) & 0x7f)
diff --git a/libc/test/integration/src/unistd/CMakeLists.txt b/libc/test/integration/src/unistd/CMakeLists.txt
index 06eae2b8eaaf9..b23cd3775a25d 100644
--- a/libc/test/integration/src/unistd/CMakeLists.txt
+++ b/libc/test/integration/src/unistd/CMakeLists.txt
@@ -39,6 +39,22 @@ add_integration_test(
libc.include.sys_syscall
)
+add_integration_test(
+ wait_status_test
+ SUITE
+ libc-unistd-integration-tests
+ SRCS
+ wait_status_test.cpp
+ DEPENDS
+ libc.include.signal
+ libc.include.sys_wait
+ libc.src.signal.kill
+ libc.src.signal.raise
+ libc.src.stdlib.exit
+ libc.src.sys.wait.waitpid
+ libc.src.unistd.fork
+)
+
add_integration_test(
lockf_test
SUITE
diff --git a/libc/test/integration/src/unistd/wait_status_test.cpp b/libc/test/integration/src/unistd/wait_status_test.cpp
new file mode 100644
index 0000000000000..d965a44b4b4db
--- /dev/null
+++ b/libc/test/integration/src/unistd/wait_status_test.cpp
@@ -0,0 +1,87 @@
+//===-- Round trip test for the wait status macros ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/signal/kill.h"
+#include "src/signal/raise.h"
+#include "src/stdlib/exit.h"
+#include "src/sys/wait/waitpid.h"
+#include "src/unistd/fork.h"
+
+#include "test/IntegrationTest/test.h"
+
+#include <signal.h>
+#include <sys/wait.h>
+
+// Decodes statuses produced by the kernel, and checks that exactly one of the
+// predicates is true for each.
+
+void exited() {
+ pid_t pid = LIBC_NAMESPACE::fork();
+ if (pid == 0)
+ LIBC_NAMESPACE::internal::exit(3);
+ ASSERT_TRUE(pid > 0);
+ int status = 0;
+ ASSERT_EQ(LIBC_NAMESPACE::waitpid(pid, &status, 0), pid);
+ ASSERT_TRUE(WIFEXITED(status));
+ ASSERT_EQ(WEXITSTATUS(status), 3);
+ ASSERT_FALSE(WIFSIGNALED(status));
+ ASSERT_FALSE(WIFSTOPPED(status));
+ ASSERT_FALSE(WIFCONTINUED(status));
+}
+
+void signaled() {
+ pid_t pid = LIBC_NAMESPACE::fork();
+ if (pid == 0) {
+ LIBC_NAMESPACE::raise(SIGTERM);
+ LIBC_NAMESPACE::internal::exit(0);
+ }
+ ASSERT_TRUE(pid > 0);
+ int status = 0;
+ ASSERT_EQ(LIBC_NAMESPACE::waitpid(pid, &status, 0), pid);
+ ASSERT_TRUE(WIFSIGNALED(status));
+ ASSERT_EQ(WTERMSIG(status), SIGTERM);
+ ASSERT_FALSE(WIFEXITED(status));
+ ASSERT_FALSE(WIFSTOPPED(status));
+ ASSERT_FALSE(WIFCONTINUED(status));
+}
+
+void stopped_and_continued() {
+ pid_t pid = LIBC_NAMESPACE::fork();
+ if (pid == 0) {
+ LIBC_NAMESPACE::raise(SIGSTOP);
+ LIBC_NAMESPACE::internal::exit(0);
+ }
+ ASSERT_TRUE(pid > 0);
+
+ int status = 0;
+ ASSERT_EQ(LIBC_NAMESPACE::waitpid(pid, &status, WUNTRACED), pid);
+ ASSERT_TRUE(WIFSTOPPED(status));
+ ASSERT_EQ(WSTOPSIG(status), SIGSTOP);
+ ASSERT_FALSE(WIFEXITED(status));
+ ASSERT_FALSE(WIFSIGNALED(status));
+ ASSERT_FALSE(WIFCONTINUED(status));
+
+ ASSERT_EQ(LIBC_NAMESPACE::kill(pid, SIGCONT), 0);
+ ASSERT_EQ(LIBC_NAMESPACE::waitpid(pid, &status, WCONTINUED), pid);
+ ASSERT_TRUE(WIFCONTINUED(status));
+ ASSERT_FALSE(WIFEXITED(status));
+ ASSERT_FALSE(WIFSIGNALED(status));
+ ASSERT_FALSE(WIFSTOPPED(status));
+
+ ASSERT_EQ(LIBC_NAMESPACE::waitpid(pid, &status, 0), pid);
+ ASSERT_TRUE(WIFEXITED(status));
+ ASSERT_EQ(WEXITSTATUS(status), 0);
+}
+
+TEST_MAIN([[maybe_unused]] int argc, [[maybe_unused]] char **argv,
+ [[maybe_unused]] char **envp) {
+ exited();
+ signaled();
+ stopped_and_continued();
+ return 0;
+}
More information about the libc-commits
mailing list