aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThurston Dang <thurston@google.com>2024-10-31 11:07:41 -0700
committerGitHub <noreply@github.com>2024-10-31 11:07:41 -0700
commit25fd366d6a7d40266ff27c134ed8beb0a90cc33b (patch)
tree426312d7e3ce131183033a98602e0f6bef291e30
parentcd8d507b074e8d30f6c9925a24224673b2908a51 (diff)
downloadllvm-25fd366d6a7d40266ff27c134ed8beb0a90cc33b.zip
llvm-25fd366d6a7d40266ff27c134ed8beb0a90cc33b.tar.gz
llvm-25fd366d6a7d40266ff27c134ed8beb0a90cc33b.tar.bz2
[sanitizer_common] AND signals in BlockSignals instead of deleting (#113443)
My earlier patch https://github.com/llvm/llvm-project/pull/98200 caused a regression because it unconditionally unblocked synchronous signals, even if the user program had deliberately blocked them. This patch fixes the issue by checking the current signal mask, as suggested by Vitaly. It also adds tests. Fixes #113385 --------- Co-authored-by: Vitaly Buka <vitalybuka@gmail.com>
-rw-r--r--compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp36
-rw-r--r--compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt1
-rw-r--r--compiler-rt/lib/sanitizer_common/tests/sanitizer_block_signals.cpp76
3 files changed, 101 insertions, 12 deletions
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
index 33107eb..82bc21d 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
@@ -164,33 +164,45 @@ void SetSigProcMask(__sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset) {
CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, set, oldset));
}
+// Deletes the specified signal from newset, if it is not present in oldset
+// Equivalently: newset[signum] = newset[signum] & oldset[signum]
+static void KeepUnblocked(__sanitizer_sigset_t &newset,
+ __sanitizer_sigset_t &oldset, int signum) {
+ if (!internal_sigismember(&oldset, signum))
+ internal_sigdelset(&newset, signum);
+}
+
// Block asynchronous signals
void BlockSignals(__sanitizer_sigset_t *oldset) {
- __sanitizer_sigset_t set;
- internal_sigfillset(&set);
+ __sanitizer_sigset_t currentset;
+ SetSigProcMask(NULL, &currentset);
+
+ __sanitizer_sigset_t newset;
+ internal_sigfillset(&newset);
# if SANITIZER_LINUX && !SANITIZER_ANDROID
// Glibc uses SIGSETXID signal during setuid call. If this signal is blocked
// on any thread, setuid call hangs.
// See test/sanitizer_common/TestCases/Linux/setuid.c.
- internal_sigdelset(&set, 33);
+ KeepUnblocked(newset, currentset, 33);
# endif
# if SANITIZER_LINUX
// Seccomp-BPF-sandboxed processes rely on SIGSYS to handle trapped syscalls.
// If this signal is blocked, such calls cannot be handled and the process may
// hang.
- internal_sigdelset(&set, 31);
+ KeepUnblocked(newset, currentset, 31);
// Don't block synchronous signals
- internal_sigdelset(&set, SIGSEGV);
- internal_sigdelset(&set, SIGBUS);
- internal_sigdelset(&set, SIGILL);
- internal_sigdelset(&set, SIGTRAP);
- internal_sigdelset(&set, SIGABRT);
- internal_sigdelset(&set, SIGFPE);
- internal_sigdelset(&set, SIGPIPE);
+ // but also don't unblock signals that the user had deliberately blocked.
+ KeepUnblocked(newset, currentset, SIGSEGV);
+ KeepUnblocked(newset, currentset, SIGBUS);
+ KeepUnblocked(newset, currentset, SIGILL);
+ KeepUnblocked(newset, currentset, SIGTRAP);
+ KeepUnblocked(newset, currentset, SIGABRT);
+ KeepUnblocked(newset, currentset, SIGFPE);
+ KeepUnblocked(newset, currentset, SIGPIPE);
# endif
- SetSigProcMask(&set, oldset);
+ SetSigProcMask(&newset, oldset);
}
ScopedBlockSignals::ScopedBlockSignals(__sanitizer_sigset_t *copy) {
diff --git a/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt
index 2b4c151..fef8bb7 100644
--- a/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt
+++ b/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt
@@ -15,6 +15,7 @@ set(SANITIZER_UNITTESTS
sanitizer_array_ref_test.cpp
sanitizer_atomic_test.cpp
sanitizer_bitvector_test.cpp
+ sanitizer_block_signals.cpp
sanitizer_bvgraph_test.cpp
sanitizer_chained_origin_depot_test.cpp
sanitizer_common_test.cpp
diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_block_signals.cpp b/compiler-rt/lib/sanitizer_common/tests/sanitizer_block_signals.cpp
new file mode 100644
index 0000000..17791c1
--- /dev/null
+++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_block_signals.cpp
@@ -0,0 +1,76 @@
+//===-- sanitizer_block_signals.cpp ---------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of sanitizer_common unit tests.
+//
+//===----------------------------------------------------------------------===//
+#include <signal.h>
+#include <stdio.h>
+
+#include "gtest/gtest.h"
+#include "sanitizer_common/sanitizer_linux.h"
+
+namespace __sanitizer {
+
+#if SANITIZER_LINUX
+volatile int received_sig = -1;
+
+void signal_handler(int signum) { received_sig = signum; }
+
+TEST(SanitizerCommon, NoBlockSignals) {
+ // No signals blocked
+ signal(SIGUSR1, signal_handler);
+ raise(SIGUSR1);
+ EXPECT_EQ(received_sig, SIGUSR1);
+
+ received_sig = -1;
+ signal(SIGPIPE, signal_handler);
+ raise(SIGPIPE);
+ EXPECT_EQ(received_sig, SIGPIPE);
+}
+
+TEST(SanitizerCommon, BlockSignalsPlain) {
+ // ScopedBlockSignals; SIGUSR1 should be blocked but not SIGPIPE
+ {
+ __sanitizer_sigset_t sigset = {};
+ ScopedBlockSignals block(&sigset);
+
+ received_sig = -1;
+ signal(SIGUSR1, signal_handler);
+ raise(SIGUSR1);
+ EXPECT_EQ(received_sig, -1);
+
+ received_sig = -1;
+ signal(SIGPIPE, signal_handler);
+ raise(SIGPIPE);
+ EXPECT_EQ(received_sig, SIGPIPE);
+ }
+ EXPECT_EQ(received_sig, SIGUSR1);
+}
+
+TEST(SanitizerCommon, BlockSignalsExceptPipe) {
+ // Manually block SIGPIPE; ScopedBlockSignals should not unblock this
+ sigset_t block_sigset;
+ sigemptyset(&block_sigset);
+ sigaddset(&block_sigset, SIGPIPE);
+ sigprocmask(SIG_BLOCK, &block_sigset, NULL);
+ {
+ __sanitizer_sigset_t sigset = {};
+ ScopedBlockSignals block(&sigset);
+
+ received_sig = -1;
+ signal(SIGPIPE, signal_handler);
+ raise(SIGPIPE);
+ EXPECT_EQ(received_sig, -1);
+ }
+ sigprocmask(SIG_UNBLOCK, &block_sigset, NULL);
+ EXPECT_EQ(received_sig, SIGPIPE);
+}
+#endif // SANITIZER_LINUX
+
+} // namespace __sanitizer