Skip to content

Commit

Permalink
Reapply "[sanitizer_common] AND signals in BlockSignals instead of de…
Browse files Browse the repository at this point in the history
…leting (#113443)" for non-Android Linux only (#115790)

The original patch (25fd366) was
reverted in 083a5cd because it broke
some buildbots.

This revised patch makes two changes:
- Reverts to *pre-#98200* behavior for Android. This avoids a build
breakage on Android.
- Only define KeepUnblocked if SANITIZER_LINUX: this avoids a build
breakage on solaris, which does not support internal_sigdelset.
N.B. Other buildbot failures were non-sanitizer tests and are therefore
unrelated.

Original commit message:
    My earlier patch #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
  • Loading branch information
thurstond authored Nov 14, 2024
1 parent 02018cf commit 531acf9
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 16 deletions.
55 changes: 39 additions & 16 deletions compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,33 +164,56 @@ void SetSigProcMask(__sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset) {
CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, set, oldset));
}

# if SANITIZER_LINUX
// 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) {
// FIXME: https://github.com/google/sanitizers/issues/1816
if (SANITIZER_ANDROID || !internal_sigismember(&oldset, signum))
internal_sigdelset(&newset, signum);
}
# endif

// Block asynchronous signals
void BlockSignals(__sanitizer_sigset_t *oldset) {
__sanitizer_sigset_t set;
internal_sigfillset(&set);
# if SANITIZER_LINUX && !SANITIZER_ANDROID
__sanitizer_sigset_t newset;
internal_sigfillset(&newset);

# if SANITIZER_LINUX
__sanitizer_sigset_t currentset;

# if !SANITIZER_ANDROID
// FIXME: https://github.com/google/sanitizers/issues/1816
SetSigProcMask(NULL, &currentset);

// 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);
# endif
# if SANITIZER_LINUX
KeepUnblocked(newset, currentset, 33);
# endif // !SANITIZER_ANDROID

// 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);

# if !SANITIZER_ANDROID
// 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);
# endif
// but also don't unblock signals that the user had deliberately blocked.
// FIXME: https://github.com/google/sanitizers/issues/1816
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 //! SANITIZER_ANDROID

# endif // SANITIZER_LINUX

SetSigProcMask(&set, oldset);
SetSigProcMask(&newset, oldset);
}

ScopedBlockSignals::ScopedBlockSignals(__sanitizer_sigset_t *copy) {
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
76 changes: 76 additions & 0 deletions compiler-rt/lib/sanitizer_common/tests/sanitizer_block_signals.cpp
Original file line number Diff line number Diff line change
@@ -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 && !SANITIZER_ANDROID
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 && !SANITIZER_ANDROID

} // namespace __sanitizer

0 comments on commit 531acf9

Please sign in to comment.