Skip to content

Commit 1e547fc

Browse files
thurstondvitalybuka
authored and
smallp-o-p
committed
[sanitizer_common] AND signals in BlockSignals instead of deleting (llvm#113443)
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 --------- Co-authored-by: Vitaly Buka <[email protected]>
1 parent 030ce43 commit 1e547fc

File tree

3 files changed

+101
-12
lines changed

3 files changed

+101
-12
lines changed

compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp

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

167+
// Deletes the specified signal from newset, if it is not present in oldset
168+
// Equivalently: newset[signum] = newset[signum] & oldset[signum]
169+
static void KeepUnblocked(__sanitizer_sigset_t &newset,
170+
__sanitizer_sigset_t &oldset, int signum) {
171+
if (!internal_sigismember(&oldset, signum))
172+
internal_sigdelset(&newset, signum);
173+
}
174+
167175
// Block asynchronous signals
168176
void BlockSignals(__sanitizer_sigset_t *oldset) {
169-
__sanitizer_sigset_t set;
170-
internal_sigfillset(&set);
177+
__sanitizer_sigset_t currentset;
178+
SetSigProcMask(NULL, &currentset);
179+
180+
__sanitizer_sigset_t newset;
181+
internal_sigfillset(&newset);
171182
# if SANITIZER_LINUX && !SANITIZER_ANDROID
172183
// Glibc uses SIGSETXID signal during setuid call. If this signal is blocked
173184
// on any thread, setuid call hangs.
174185
// See test/sanitizer_common/TestCases/Linux/setuid.c.
175-
internal_sigdelset(&set, 33);
186+
KeepUnblocked(newset, currentset, 33);
176187
# endif
177188
# if SANITIZER_LINUX
178189
// Seccomp-BPF-sandboxed processes rely on SIGSYS to handle trapped syscalls.
179190
// If this signal is blocked, such calls cannot be handled and the process may
180191
// hang.
181-
internal_sigdelset(&set, 31);
192+
KeepUnblocked(newset, currentset, 31);
182193

183194
// 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);
195+
// but also don't unblock signals that the user had deliberately blocked.
196+
KeepUnblocked(newset, currentset, SIGSEGV);
197+
KeepUnblocked(newset, currentset, SIGBUS);
198+
KeepUnblocked(newset, currentset, SIGILL);
199+
KeepUnblocked(newset, currentset, SIGTRAP);
200+
KeepUnblocked(newset, currentset, SIGABRT);
201+
KeepUnblocked(newset, currentset, SIGFPE);
202+
KeepUnblocked(newset, currentset, SIGPIPE);
191203
# endif
192204

193-
SetSigProcMask(&set, oldset);
205+
SetSigProcMask(&newset, oldset);
194206
}
195207

196208
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
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
75+
76+
} // namespace __sanitizer

0 commit comments

Comments
 (0)