Skip to content

Commit 5d7e930

Browse files
committed
[sanitizer] Add cloak_sanitizer_signal_handlers runtime option
If set, signal/sigaction will pretend that the sanitizers did not preinstall any signal handlers. If a user successfully installs a signal handler, it will not be cloaked. The flag is currently off by default, which means this patch should not affect the behavior of any sanitizers. This can be useful in an ecosystem where: 1) there exists a library that will install a signal handler iff it does not detect a preinstalled signal handler (a heuristic to prevent overriding user-installed exception handlers etc.) 2) the aforementioned library is linked in to some, but not all, apps 3) user-installed signal handlers have the highest priority, followed by the library-installed signal handler, and then the sanitizer's signal handler This patch also adds an API function, __sanitizer_uncloak_preinstalled_signal_handlers(), that can be used to effectively undo the runtime option. This makes it possible to set the cloak_sanitizer_signal_handlers option broadly, and selectively programmatically disable it for incompatible programs (e.g., allow_user_segv.cpp, which wants to manually call ASan's preinstalled handler). The flag is in sanitizer_common, though it is currently only supported in ASan, LSan and UBSan.
1 parent 01ac00b commit 5d7e930

File tree

10 files changed

+167
-10
lines changed

10 files changed

+167
-10
lines changed

compiler-rt/include/sanitizer/common_interface_defs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,11 @@ void SANITIZER_CDECL __sanitizer_finish_switch_fiber(void *fake_stack_save,
485485
int SANITIZER_CDECL __sanitizer_get_module_and_offset_for_pc(
486486
void *pc, char *module_path, size_t module_path_len, void **pc_offset);
487487

488+
// If cloak_sanitizer_signal_handlers() is set, signal/sigaction will pretend
489+
// that sanitizers did not preinstall any signal handlers. This function clears
490+
// the cloaking state.
491+
void SANITIZER_CDECL __sanitizer_uncloak_preinstalled_signal_handlers();
492+
488493
#ifdef __cplusplus
489494
} // extern "C"
490495
#endif

compiler-rt/lib/sanitizer_common/sanitizer_common.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ namespace __sanitizer {
2424

2525
const char *SanitizerToolName = "SanitizerTool";
2626

27+
bool signal_handler_is_from_sanitizer[MaxSignals] = {0};
28+
2729
atomic_uint32_t current_verbosity;
2830
uptr PageSizeCached;
2931
u32 NumberOfCPUsCached;
@@ -416,6 +418,12 @@ int __sanitizer_install_malloc_and_free_hooks(void (*malloc_hook)(const void *,
416418
return InstallMallocFreeHooks(malloc_hook, free_hook);
417419
}
418420

421+
SANITIZER_INTERFACE_ATTRIBUTE
422+
void __sanitizer_uncloak_preinstalled_signal_handlers() {
423+
internal_memset(signal_handler_is_from_sanitizer, 0,
424+
sizeof(signal_handler_is_from_sanitizer));
425+
}
426+
419427
// Provide default (no-op) implementation of malloc hooks.
420428
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_malloc_hook, void *ptr,
421429
uptr size) {

compiler-rt/lib/sanitizer_common/sanitizer_common.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ const u64 kExternalPCBit = 1ULL << 60;
5252

5353
extern const char *SanitizerToolName; // Can be changed by the tool.
5454

55+
const int MaxSignals = 64;
56+
extern bool signal_handler_is_from_sanitizer[MaxSignals];
57+
5558
extern atomic_uint32_t current_verbosity;
5659
inline void SetVerbosity(int verbosity) {
5760
atomic_store(&current_verbosity, verbosity, memory_order_relaxed);

compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ INTERFACE_WEAK_FUNCTION(__sanitizer_on_print)
2424
INTERFACE_WEAK_FUNCTION(__sanitizer_report_error_summary)
2525
INTERFACE_WEAK_FUNCTION(__sanitizer_sandbox_on_notify)
2626
INTERFACE_WEAK_FUNCTION(__sanitizer_get_dtls_size)
27+
INTERFACE_FUNCTION(__sanitizer_uncloak_preinstalled_signal_handlers)
2728
// Sanitizer weak hooks
2829
INTERFACE_WEAK_FUNCTION(__sanitizer_weak_hook_memcmp)
2930
INTERFACE_WEAK_FUNCTION(__sanitizer_weak_hook_strcmp)

compiler-rt/lib/sanitizer_common/sanitizer_flags.inc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ COMMON_FLAG(HandleSignalMode, handle_sigfpe, kHandleSignalYes,
113113
COMMON_FLAG(bool, allow_user_segv_handler, true,
114114
"Deprecated. True has no effect, use handle_sigbus=1. If false, "
115115
"handle_*=1 will be upgraded to handle_*=2.")
116+
COMMON_FLAG(bool, cloak_sanitizer_signal_handlers, false,
117+
"If set, signal/sigaction will pretend that sanitizers did not "
118+
"preinstall any signal handlers. If the user subsequently installs "
119+
"a signal handler, this will disable cloaking for the respective "
120+
"signal.")
116121
COMMON_FLAG(bool, use_sigaltstack, true,
117122
"If set, uses alternate stack for signal handling.")
118123
COMMON_FLAG(bool, detect_deadlocks, true,

compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -223,19 +223,19 @@ static void MaybeInstallSigaction(int signum,
223223
if (common_flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK;
224224
CHECK_EQ(0, internal_sigaction(signum, &sigact, nullptr));
225225
VReport(1, "Installed the sigaction for signal %d\n", signum);
226+
227+
if (common_flags()->cloak_sanitizer_signal_handlers)
228+
signal_handler_is_from_sanitizer[signum] = true;
226229
}
227230

228231
void InstallDeadlySignalHandlers(SignalHandlerType handler) {
229232
// Set the alternate signal stack for the main thread.
230233
// This will cause SetAlternateSignalStack to be called twice, but the stack
231234
// will be actually set only once.
232235
if (common_flags()->use_sigaltstack) SetAlternateSignalStack();
233-
MaybeInstallSigaction(SIGSEGV, handler);
234-
MaybeInstallSigaction(SIGBUS, handler);
235-
MaybeInstallSigaction(SIGABRT, handler);
236-
MaybeInstallSigaction(SIGFPE, handler);
237-
MaybeInstallSigaction(SIGILL, handler);
238-
MaybeInstallSigaction(SIGTRAP, handler);
236+
237+
int signals[] = {SIGSEGV, SIGBUS, SIGABRT, SIGFPE, SIGILL, SIGTRAP};
238+
for (int signum : signals) MaybeInstallSigaction(signum, handler);
239239
}
240240

241241
bool SignalContext::IsStackOverflow() const {

compiler-rt/lib/sanitizer_common/sanitizer_signal_interceptors.inc

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ using namespace __sanitizer;
2424
#endif
2525

2626
#ifndef SIGNAL_INTERCEPTOR_SIGNAL_IMPL
27-
#define SIGNAL_INTERCEPTOR_SIGNAL_IMPL(func, signum, handler) \
28-
{ return REAL(func)(signum, handler); }
27+
# define SIGNAL_INTERCEPTOR_SIGNAL_IMPL(func, signum, handler) \
28+
{ \
29+
ret = REAL(func)(signum, handler); \
30+
}
2931
#endif
3032

3133
#ifndef SIGNAL_INTERCEPTOR_SIGACTION_IMPL
@@ -35,17 +37,21 @@ using namespace __sanitizer;
3537
Printf( \
3638
"Warning: REAL(sigaction_symname) == nullptr. This may happen " \
3739
"if you link with ubsan statically. Sigaction will not work.\n"); \
38-
return -1; \
40+
ret = -1; \
41+
} else { \
42+
ret = REAL(sigaction_symname)(signum, act, oldact); \
3943
} \
40-
return REAL(sigaction_symname)(signum, act, oldact); \
4144
}
4245
#endif
4346

4447
#if SANITIZER_INTERCEPT_BSD_SIGNAL
4548
INTERCEPTOR(uptr, bsd_signal, int signum, uptr handler) {
4649
SIGNAL_INTERCEPTOR_ENTER();
4750
if (GetHandleSignalMode(signum) == kHandleSignalExclusive) return 0;
51+
52+
int ret;
4853
SIGNAL_INTERCEPTOR_SIGNAL_IMPL(bsd_signal, signum, handler);
54+
return ret;
4955
}
5056
#define INIT_BSD_SIGNAL COMMON_INTERCEPT_FUNCTION(bsd_signal)
5157
#else // SANITIZER_INTERCEPT_BSD_SIGNAL
@@ -56,19 +62,50 @@ INTERCEPTOR(uptr, bsd_signal, int signum, uptr handler) {
5662
INTERCEPTOR(uptr, signal, int signum, uptr handler) {
5763
SIGNAL_INTERCEPTOR_ENTER();
5864
if (GetHandleSignalMode(signum) == kHandleSignalExclusive)
65+
// A side-effect is that a user can never uncloak the sanitizer's
66+
// preinstalled signal handler.
5967
return (uptr) nullptr;
68+
uptr ret;
6069
SIGNAL_INTERCEPTOR_SIGNAL_IMPL(signal, signum, handler);
70+
71+
if (signum >= 0 && signum < MaxSignals &&
72+
signal_handler_is_from_sanitizer[signum] && ret != sig_err) {
73+
// If the user sets a signal handler, it is never cloaked, even if they
74+
// reuse a sanitizer's signal handler.
75+
signal_handler_is_from_sanitizer[signum] = false;
76+
77+
ret = sig_dfl;
78+
}
79+
80+
return ret;
6181
}
6282
#define INIT_SIGNAL COMMON_INTERCEPT_FUNCTION(signal)
6383

6484
INTERCEPTOR(int, sigaction_symname, int signum,
6585
const __sanitizer_sigaction *act, __sanitizer_sigaction *oldact) {
6686
SIGNAL_INTERCEPTOR_ENTER();
87+
6788
if (GetHandleSignalMode(signum) == kHandleSignalExclusive) {
6889
if (!oldact) return 0;
6990
act = nullptr;
91+
// A side-effect is that a user can never uncloak the sanitizer's
92+
// preinstalled signal handler.
7093
}
94+
int ret;
7195
SIGNAL_INTERCEPTOR_SIGACTION_IMPL(signum, act, oldact);
96+
97+
if (signum >= 0 && signum < MaxSignals &&
98+
signal_handler_is_from_sanitizer[signum] && ret == 0) {
99+
if (act)
100+
// If the user sets a signal handler, it is never cloaked, even if they
101+
// reuse a sanitizer's signal handler.
102+
signal_handler_is_from_sanitizer[signum] = false;
103+
104+
if (oldact)
105+
oldact->handler = reinterpret_cast<__sanitizer_sighandler_ptr>(sig_dfl);
106+
}
107+
108+
return ret;
72109
}
73110
#define INIT_SIGACTION COMMON_INTERCEPT_FUNCTION(sigaction_symname)
74111

compiler-rt/test/sanitizer_common/TestCases/Linux/allow_user_segv.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
// Flaky errors in debuggerd with "waitpid returned unexpected pid (0)" in logcat.
2424
// UNSUPPORTED: android && i386-target-arch
2525

26+
#include <sanitizer/common_interface_defs.h>
2627
#include <signal.h>
2728
#include <stdio.h>
2829
#include <stdlib.h>
@@ -68,6 +69,10 @@ bool InstallHandler(int signum, struct sigaction *original_sigaction) {
6869
}
6970

7071
int main() {
72+
// This test case is unusual because it retrieves the original
73+
// (ASan-installed) signal handler; thus, we don't want ASan to cloak them.
74+
__sanitizer_uncloak_preinstalled_signal_handlers();
75+
7176
// Let's install handlers for both SIGSEGV and SIGBUS, since pre-Yosemite
7277
// 32-bit Darwin triggers SIGBUS instead.
7378
if (InstallHandler(SIGSEGV, &original_sigaction_sigsegv) &&
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// XFAIL: msan
2+
// XFAIL: tsan
3+
4+
// UNSUPPORTED: android
5+
// UNSUPPORTED: hwasan
6+
7+
// RUN: %clangxx -O0 %s -o %t
8+
9+
// RUN: %env_tool_opts=handle_segv=1:cloak_sanitizer_signal_handlers=false not %run %t 2>&1 | FileCheck %s --check-prefix=UNCLOAKED
10+
// RUN: %env_tool_opts=handle_segv=1:cloak_sanitizer_signal_handlers=true not %run %t 2>&1 | FileCheck %s --check-prefix=CLOAKED
11+
12+
// RUN: %clangxx -O0 %s -DUNCLOAK_RT -o %t
13+
// RUN: %env_tool_opts=handle_segv=1:cloak_sanitizer_signal_handlers=false not %run %t 2>&1 | FileCheck %s --check-prefix=UNCLOAKED
14+
// RUN: %env_tool_opts=handle_segv=1:cloak_sanitizer_signal_handlers=true not %run %t 2>&1 | FileCheck %s --check-prefix=UNCLOAKED
15+
16+
#include <sanitizer/common_interface_defs.h>
17+
#include <signal.h>
18+
#include <stdio.h>
19+
#include <stdlib.h>
20+
21+
void handler(int signum, siginfo_t *info, void *context) {
22+
printf("Custom signal handler\n");
23+
exit(1);
24+
}
25+
26+
int main(int argc, char *argv[]) {
27+
#ifdef UNCLOAK_RT
28+
__sanitizer_uncloak_preinstalled_signal_handlers();
29+
#endif
30+
31+
struct sigaction sa = {0};
32+
struct sigaction old = {0};
33+
sa.sa_flags = SA_SIGINFO;
34+
sa.sa_sigaction = &handler;
35+
sigaction(SIGSEGV, &sa, &old);
36+
37+
if (reinterpret_cast<void *>(old.sa_sigaction) == SIG_DFL)
38+
printf("Old handler: default\n");
39+
// CLOAKED: Old handler: default
40+
else
41+
printf("Old handler: non-default\n");
42+
// UNCLOAKED: Old handler: non-default
43+
44+
char *c = (char *)0x123;
45+
printf("%d\n", *c);
46+
// UNCLOAKED,CLOAKED:Custom signal handler
47+
48+
return 0;
49+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// XFAIL: msan
2+
// XFAIL: tsan
3+
4+
// UNSUPPORTED: android
5+
// UNSUPPORTED: hwasan
6+
7+
// RUN: %clangxx -O0 %s -o %t
8+
9+
// RUN: %env_tool_opts=handle_segv=1:cloak_sanitizer_signal_handlers=false not %run %t 2>&1 | FileCheck %s --check-prefix=UNCLOAKED
10+
// RUN: %env_tool_opts=handle_segv=1:cloak_sanitizer_signal_handlers=true not %run %t 2>&1 | FileCheck %s --check-prefix=CLOAKED
11+
12+
// RUN: %clangxx -O0 %s -DUNCLOAK_RT -o %t
13+
// RUN: %env_tool_opts=handle_segv=1:cloak_sanitizer_signal_handlers=false not %run %t 2>&1 | FileCheck %s --check-prefix=UNCLOAKED
14+
// RUN: %env_tool_opts=handle_segv=1:cloak_sanitizer_signal_handlers=true not %run %t 2>&1 | FileCheck %s --check-prefix=UNCLOAKED
15+
16+
#include <sanitizer/common_interface_defs.h>
17+
#include <signal.h>
18+
#include <stdio.h>
19+
#include <stdlib.h>
20+
21+
void my_signal_sighandler(int signum) {
22+
printf("Custom signal handler\n");
23+
exit(1);
24+
}
25+
26+
int main(int argc, char *argv[]) {
27+
#ifdef UNCLOAK_RT
28+
__sanitizer_uncloak_preinstalled_signal_handlers();
29+
#endif
30+
31+
__sighandler_t old = signal(SIGSEGV, &my_signal_sighandler);
32+
if (old == SIG_DFL)
33+
printf("Old handler: default\n");
34+
// CLOAKED: Old handler: default
35+
else
36+
printf("Old handler: non-default\n");
37+
// UNCLOAKED: Old handler: non-default
38+
39+
char *c = (char *)0x123;
40+
printf("%d\n", *c);
41+
// UNCLOAKED,CLOAKED:Custom signal handler
42+
43+
return 0;
44+
}

0 commit comments

Comments
 (0)