Skip to content

Commit 3f043e1

Browse files
committed
Reapply "[sanitizer_common] AND signals in BlockSignals instead of deleting (llvm#113443)" for non-Android Linux only
The original patch (25fd366) was reverted in 083a5cd because it broke some buildbots. This revised patch makes two changes: - No-op the change for Android: this had mysterious (but reproducible) test failures. - Only define KeepUnblocked if SANITIZER_LINUX: this avoids a build breakage on solaris, which does not support internal_sigismember. - Two other buildbot failures were non-sanitizer tests and are therefore unrelated. Original commit message: My earlier patch llvm#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 llvm#113385
1 parent 375bb38 commit 3f043e1

File tree

3 files changed

+111
-12
lines changed

3 files changed

+111
-12
lines changed

compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp

+34-12
Original file line numberDiff line numberDiff line change
@@ -164,33 +164,55 @@ void SetSigProcMask(__sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset) {
164164
CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, set, oldset));
165165
}
166166

167+
# if SANITIZER_LINUX
168+
// Deletes the specified signal from newset, if it is not present in oldset
169+
// Equivalently: newset[signum] = newset[signum] & oldset[signum]
170+
static void KeepUnblocked(__sanitizer_sigset_t &newset,
171+
__sanitizer_sigset_t &oldset, int signum) {
172+
// FIXME: Figure out why Android tests fail with internal_sigismember
173+
// See also: comment in BlockSignals().
174+
// In the meantime, we prefer to sometimes incorrectly unblock signals.
175+
if (SANITIZER_ANDROID || !internal_sigismember(&oldset, signum))
176+
internal_sigdelset(&newset, signum);
177+
}
178+
# endif
179+
167180
// Block asynchronous signals
168181
void BlockSignals(__sanitizer_sigset_t *oldset) {
169-
__sanitizer_sigset_t set;
170-
internal_sigfillset(&set);
182+
__sanitizer_sigset_t currentset;
183+
# if !SANITIZER_ANDROID
184+
// FIXME: SetSigProcMask and pthread_sigmask cause mysterious failures
185+
// See also: comment in KeepUnblocked().
186+
// In the meantime, we prefer to sometimes incorrectly unblock signals.
187+
SetSigProcMask(NULL, &currentset);
188+
# endif
189+
190+
__sanitizer_sigset_t newset;
191+
internal_sigfillset(&newset);
171192
# if SANITIZER_LINUX && !SANITIZER_ANDROID
172193
// Glibc uses SIGSETXID signal during setuid call. If this signal is blocked
173194
// on any thread, setuid call hangs.
174195
// See test/sanitizer_common/TestCases/Linux/setuid.c.
175-
internal_sigdelset(&set, 33);
196+
KeepUnblocked(newset, currentset, 33);
176197
# endif
177198
# if SANITIZER_LINUX
178199
// Seccomp-BPF-sandboxed processes rely on SIGSYS to handle trapped syscalls.
179200
// If this signal is blocked, such calls cannot be handled and the process may
180201
// hang.
181-
internal_sigdelset(&set, 31);
202+
KeepUnblocked(newset, currentset, 31);
182203

183204
// Don't block synchronous signals
184-
internal_sigdelset(&set, SIGSEGV);
185-
internal_sigdelset(&set, SIGBUS);
186-
internal_sigdelset(&set, SIGILL);
187-
internal_sigdelset(&set, SIGTRAP);
188-
internal_sigdelset(&set, SIGABRT);
189-
internal_sigdelset(&set, SIGFPE);
190-
internal_sigdelset(&set, SIGPIPE);
205+
// but also don't unblock signals that the user had deliberately blocked.
206+
KeepUnblocked(newset, currentset, SIGSEGV);
207+
KeepUnblocked(newset, currentset, SIGBUS);
208+
KeepUnblocked(newset, currentset, SIGILL);
209+
KeepUnblocked(newset, currentset, SIGTRAP);
210+
KeepUnblocked(newset, currentset, SIGABRT);
211+
KeepUnblocked(newset, currentset, SIGFPE);
212+
KeepUnblocked(newset, currentset, SIGPIPE);
191213
# endif
192214

193-
SetSigProcMask(&set, oldset);
215+
SetSigProcMask(&newset, oldset);
194216
}
195217

196218
ScopedBlockSignals::ScopedBlockSignals(__sanitizer_sigset_t *copy) {

compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ set(SANITIZER_UNITTESTS
1515
sanitizer_array_ref_test.cpp
1616
sanitizer_atomic_test.cpp
1717
sanitizer_bitvector_test.cpp
18+
sanitizer_block_signals.cpp
1819
sanitizer_bvgraph_test.cpp
1920
sanitizer_chained_origin_depot_test.cpp
2021
sanitizer_common_test.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//===-- sanitizer_block_signals.cpp ---------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file is a part of sanitizer_common unit tests.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
#include <signal.h>
13+
#include <stdio.h>
14+
15+
#include "gtest/gtest.h"
16+
#include "sanitizer_common/sanitizer_linux.h"
17+
18+
namespace __sanitizer {
19+
20+
#if SANITIZER_LINUX && !SANITIZER_ANDROID
21+
volatile int received_sig = -1;
22+
23+
void signal_handler(int signum) { received_sig = signum; }
24+
25+
TEST(SanitizerCommon, NoBlockSignals) {
26+
// No signals blocked
27+
signal(SIGUSR1, signal_handler);
28+
raise(SIGUSR1);
29+
EXPECT_EQ(received_sig, SIGUSR1);
30+
31+
received_sig = -1;
32+
signal(SIGPIPE, signal_handler);
33+
raise(SIGPIPE);
34+
EXPECT_EQ(received_sig, SIGPIPE);
35+
}
36+
37+
TEST(SanitizerCommon, BlockSignalsPlain) {
38+
// ScopedBlockSignals; SIGUSR1 should be blocked but not SIGPIPE
39+
{
40+
__sanitizer_sigset_t sigset = {};
41+
ScopedBlockSignals block(&sigset);
42+
43+
received_sig = -1;
44+
signal(SIGUSR1, signal_handler);
45+
raise(SIGUSR1);
46+
EXPECT_EQ(received_sig, -1);
47+
48+
received_sig = -1;
49+
signal(SIGPIPE, signal_handler);
50+
raise(SIGPIPE);
51+
EXPECT_EQ(received_sig, SIGPIPE);
52+
}
53+
EXPECT_EQ(received_sig, SIGUSR1);
54+
}
55+
56+
TEST(SanitizerCommon, BlockSignalsExceptPipe) {
57+
// Manually block SIGPIPE; ScopedBlockSignals should not unblock this
58+
sigset_t block_sigset;
59+
sigemptyset(&block_sigset);
60+
sigaddset(&block_sigset, SIGPIPE);
61+
sigprocmask(SIG_BLOCK, &block_sigset, NULL);
62+
{
63+
__sanitizer_sigset_t sigset = {};
64+
ScopedBlockSignals block(&sigset);
65+
66+
received_sig = -1;
67+
signal(SIGPIPE, signal_handler);
68+
raise(SIGPIPE);
69+
EXPECT_EQ(received_sig, -1);
70+
}
71+
sigprocmask(SIG_UNBLOCK, &block_sigset, NULL);
72+
EXPECT_EQ(received_sig, SIGPIPE);
73+
}
74+
#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
75+
76+
} // namespace __sanitizer

0 commit comments

Comments
 (0)