diff --git a/libc/bionic/__libc_current_sigrtmin.cpp b/libc/bionic/__libc_current_sigrtmin.cpp index 04caa8905..d2ea75d11 100644 --- a/libc/bionic/__libc_current_sigrtmin.cpp +++ b/libc/bionic/__libc_current_sigrtmin.cpp @@ -28,14 +28,8 @@ #include -// Realtime signals reserved for internal use: -// 32 (__SIGRTMIN + 0) POSIX timers -// 33 (__SIGRTMIN + 1) libbacktrace -// 34 (__SIGRTMIN + 2) libcore -// 35 (__SIGRTMIN + 3) debuggerd -b +#include "private/sigrtmin.h" -int __libc_current_sigrtmin(void) { - // If you change this, also change __ndk_legacy___libc_current_sigrtmin - // in to match. - return __SIGRTMIN + 4; +int __libc_current_sigrtmin() { + return __SIGRTMIN + __SIGRT_RESERVED; } diff --git a/libc/bionic/poll.cpp b/libc/bionic/poll.cpp index 1a54832a6..3df8b1889 100644 --- a/libc/bionic/poll.cpp +++ b/libc/bionic/poll.cpp @@ -31,6 +31,7 @@ #include #include "private/bionic_time_conversions.h" +#include "private/sigrtmin.h" #include "private/SigSetConverter.h" extern "C" int __ppoll(pollfd*, unsigned int, timespec*, const sigset64_t*, size_t); @@ -66,7 +67,15 @@ int ppoll64(pollfd* fds, nfds_t fd_count, const timespec* ts, const sigset64_t* mutable_ts = *ts; mutable_ts_ptr = &mutable_ts; } - return __ppoll(fds, fd_count, mutable_ts_ptr, ss, sizeof(*ss)); + + sigset64_t mutable_ss; + sigset64_t* mutable_ss_ptr = nullptr; + if (ss != nullptr) { + mutable_ss = filter_reserved_signals(*ss); + mutable_ss_ptr = &mutable_ss; + } + + return __ppoll(fds, fd_count, mutable_ts_ptr, mutable_ss_ptr, sizeof(*mutable_ss_ptr)); } int select(int fd_count, fd_set* read_fds, fd_set* write_fds, fd_set* error_fds, timeval* tv) { @@ -109,6 +118,13 @@ int pselect64(int fd_count, fd_set* read_fds, fd_set* write_fds, fd_set* error_f mutable_ts_ptr = &mutable_ts; } + sigset64_t mutable_ss; + sigset64_t* mutable_ss_ptr = nullptr; + if (ss != nullptr) { + mutable_ss = filter_reserved_signals(*ss); + mutable_ss_ptr = &mutable_ss; + } + // The Linux kernel only handles 6 arguments and this system call really needs 7, // so the last argument is a void* pointing to: struct pselect6_extra_data_t { @@ -116,8 +132,8 @@ int pselect64(int fd_count, fd_set* read_fds, fd_set* write_fds, fd_set* error_f size_t ss_len; }; pselect6_extra_data_t extra_data; - extra_data.ss_addr = reinterpret_cast(ss); - extra_data.ss_len = sizeof(*ss); + extra_data.ss_addr = reinterpret_cast(mutable_ss_ptr); + extra_data.ss_len = sizeof(*mutable_ss_ptr); return __pselect6(fd_count, read_fds, write_fds, error_fds, mutable_ts_ptr, &extra_data); } diff --git a/libc/bionic/posix_timers.cpp b/libc/bionic/posix_timers.cpp index 2edfe9701..1abeb1f85 100644 --- a/libc/bionic/posix_timers.cpp +++ b/libc/bionic/posix_timers.cpp @@ -35,6 +35,8 @@ #include // System calls. +extern "C" int __rt_sigprocmask(int, const sigset64_t*, sigset64_t*, size_t); +extern "C" int __rt_sigtimedwait(const sigset64_t*, siginfo_t*, const timespec*, size_t); extern "C" int __timer_create(clockid_t, sigevent*, __kernel_timer_t*); extern "C" int __timer_delete(__kernel_timer_t); extern "C" int __timer_getoverrun(__kernel_timer_t); @@ -77,7 +79,7 @@ static void* __timer_thread_start(void* arg) { while (true) { // Wait for a signal... siginfo_t si = {}; - if (sigtimedwait64(&sigset, &si, nullptr) == -1) continue; + if (__rt_sigtimedwait(&sigset, &si, nullptr, sizeof(sigset)) == -1) continue; if (si.si_code == SI_TIMER) { // This signal was sent because a timer fired, so call the callback. @@ -146,11 +148,13 @@ int timer_create(clockid_t clock_id, sigevent* evp, timer_t* timer_id) { sigset64_t sigset = {}; sigaddset64(&sigset, TIMER_SIGNAL); sigset64_t old_sigset; - sigprocmask64(SIG_BLOCK, &sigset, &old_sigset); + + // Use __rt_sigprocmask instead of sigprocmask64 to avoid filtering out TIMER_SIGNAL. + __rt_sigprocmask(SIG_BLOCK, &sigset, &old_sigset, sizeof(sigset)); int rc = pthread_create(&timer->callback_thread, &thread_attributes, __timer_thread_start, timer); - sigprocmask64(SIG_SETMASK, &old_sigset, nullptr); + __rt_sigprocmask(SIG_BLOCK, &old_sigset, nullptr, sizeof(old_sigset)); if (rc != 0) { free(timer); diff --git a/libc/bionic/sigaction.cpp b/libc/bionic/sigaction.cpp index 19a08e65c..41923cfe6 100644 --- a/libc/bionic/sigaction.cpp +++ b/libc/bionic/sigaction.cpp @@ -29,6 +29,8 @@ #include #include +#include "private/sigrtmin.h" + extern "C" void __restore_rt(void); extern "C" void __restore(void); @@ -41,7 +43,7 @@ int sigaction(int signal, const struct sigaction* bionic_new_action, struct siga if (bionic_new_action != NULL) { kernel_new_action.sa_flags = bionic_new_action->sa_flags; kernel_new_action.sa_handler = bionic_new_action->sa_handler; - kernel_new_action.sa_mask = bionic_new_action->sa_mask; + kernel_new_action.sa_mask = filter_reserved_signals(bionic_new_action->sa_mask); #if defined(SA_RESTORER) kernel_new_action.sa_restorer = bionic_new_action->sa_restorer; #if defined(__aarch64__) @@ -120,6 +122,7 @@ int sigaction64(int signal, const struct sigaction64* bionic_new, struct sigacti kernel_new.sa_restorer = (kernel_new.sa_flags & SA_SIGINFO) ? &__restore_rt : &__restore; } #endif + kernel_new.sa_mask = filter_reserved_signals(kernel_new.sa_mask); } return __rt_sigaction(signal, diff --git a/libc/bionic/signal.cpp b/libc/bionic/signal.cpp index f2e19c25e..175182b7a 100644 --- a/libc/bionic/signal.cpp +++ b/libc/bionic/signal.cpp @@ -38,6 +38,7 @@ #include "private/ErrnoRestorer.h" #include "private/SigSetConverter.h" +#include "private/sigrtmin.h" extern "C" int __rt_sigpending(const sigset64_t*, size_t); extern "C" int ___rt_sigqueueinfo(pid_t, int, siginfo_t*); @@ -255,21 +256,33 @@ extern "C" int sigsetmask(int mask) { int sigsuspend(const sigset_t* bionic_set) { SigSetConverter set = {}; set.sigset = *bionic_set; - return __rt_sigsuspend(&set.sigset64, sizeof(set.sigset64)); + return sigsuspend64(&set.sigset64); } int sigsuspend64(const sigset64_t* set) { - return __rt_sigsuspend(set, sizeof(*set)); + sigset64_t mutable_set; + sigset64_t* mutable_set_ptr = nullptr; + if (set) { + mutable_set = filter_reserved_signals(*set); + mutable_set_ptr = &mutable_set; + } + return __rt_sigsuspend(mutable_set_ptr, sizeof(*set)); } int sigtimedwait(const sigset_t* bionic_set, siginfo_t* info, const timespec* timeout) { SigSetConverter set = {}; set.sigset = *bionic_set; - return __rt_sigtimedwait(&set.sigset64, info, timeout, sizeof(set.sigset64)); + return sigtimedwait64(&set.sigset64, info, timeout); } int sigtimedwait64(const sigset64_t* set, siginfo_t* info, const timespec* timeout) { - return __rt_sigtimedwait(set, info, timeout, sizeof(*set)); + sigset64_t mutable_set; + sigset64_t* mutable_set_ptr = nullptr; + if (set) { + mutable_set = filter_reserved_signals(*set); + mutable_set_ptr = &mutable_set; + } + return __rt_sigtimedwait(mutable_set_ptr, info, timeout, sizeof(*set)); } int sigwait(const sigset_t* bionic_set, int* sig) { diff --git a/libc/bionic/sigprocmask.cpp b/libc/bionic/sigprocmask.cpp index 00b5df4c9..36866f36f 100644 --- a/libc/bionic/sigprocmask.cpp +++ b/libc/bionic/sigprocmask.cpp @@ -28,6 +28,7 @@ #include +#include "private/sigrtmin.h" #include "private/SigSetConverter.h" extern "C" int __rt_sigprocmask(int, const sigset64_t*, sigset64_t*, size_t); @@ -64,5 +65,11 @@ int sigprocmask(int how, int sigprocmask64(int how, const sigset64_t* new_set, sigset64_t* old_set) __attribute__((__noinline__)) { - return __rt_sigprocmask(how, new_set, old_set, sizeof(*new_set)); + sigset64_t mutable_new_set; + sigset64_t* mutable_new_set_ptr = nullptr; + if (new_set) { + mutable_new_set = filter_reserved_signals(*new_set); + mutable_new_set_ptr = &mutable_new_set; + } + return __rt_sigprocmask(how, mutable_new_set_ptr, old_set, sizeof(*new_set)); } diff --git a/libc/private/sigrtmin.h b/libc/private/sigrtmin.h new file mode 100644 index 000000000..ea8673db8 --- /dev/null +++ b/libc/private/sigrtmin.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#pragma once + +#include + +#include + +// Realtime signals reserved for internal use: +// 32 (__SIGRTMIN + 0) POSIX timers +// 33 (__SIGRTMIN + 1) libbacktrace +// 34 (__SIGRTMIN + 2) libcore +// 35 (__SIGRTMIN + 3) debuggerd -b +// +// If you change this, also change __ndk_legacy___libc_current_sigrtmin +// in to match. + +#define __SIGRT_RESERVED 4 +static inline __always_inline sigset64_t filter_reserved_signals(sigset64_t sigset) { + for (int signo = __SIGRTMIN; signo < __SIGRTMIN + __SIGRT_RESERVED; ++signo) { + sigdelset64(&sigset, signo); + } + return sigset; +} diff --git a/tests/signal_test.cpp b/tests/signal_test.cpp index d374c500e..b37d06f34 100644 --- a/tests/signal_test.cpp +++ b/tests/signal_test.cpp @@ -337,6 +337,214 @@ TEST(signal, sigaction64_SIGRTMIN) { TestSigAction(sigaction64, sigaddset64, SIGRTMIN); } +static void ClearSignalMask() { + uint64_t sigset = 0; + if (syscall(__NR_rt_sigprocmask, SIG_SETMASK, &sigset, nullptr, sizeof(sigset)) != 0) { + abort(); + } +} + +static uint64_t GetSignalMask() { + uint64_t sigset; + if (syscall(__NR_rt_sigprocmask, SIG_SETMASK, nullptr, &sigset, sizeof(sigset)) != 0) { + abort(); + } + return sigset; +} + +enum class SignalMaskFunctionType { + RtAware, + RtNonaware, +}; + +#if defined(__LP64__) || !defined(__BIONIC__) +constexpr SignalMaskFunctionType sigset_type = SignalMaskFunctionType::RtAware; +#else +constexpr SignalMaskFunctionType sigset_type = SignalMaskFunctionType::RtNonaware; +#endif + +static void TestSignalMaskFiltered(uint64_t sigset, SignalMaskFunctionType type) { + for (int signo = 1; signo <= 64; ++signo) { + bool signal_blocked = sigset & (1ULL << (signo - 1)); + if (signo == SIGKILL || signo == SIGSTOP) { + // SIGKILL and SIGSTOP shouldn't be blocked. + EXPECT_EQ(false, signal_blocked) << "signal " << signo; + } else if (signo < __SIGRTMIN) { + // Everything else should be blocked. + EXPECT_EQ(true, signal_blocked) << "signal " << signo; + } else if (signo >= __SIGRTMIN && signo < SIGRTMIN) { + // Reserved signals must not be blocked. + EXPECT_EQ(false, signal_blocked) << "signal " << signo; + } else if (type == SignalMaskFunctionType::RtAware) { + // Realtime signals should be blocked, unless we blocked using a non-rt aware function. + EXPECT_EQ(true, signal_blocked) << "signal " << signo; + } + } +} + +static void TestSignalMaskFunction(std::function fn, SignalMaskFunctionType fn_type) { + ClearSignalMask(); + fn(); + TestSignalMaskFiltered(GetSignalMask(), fn_type); +} + +TEST(signal, sigaction_filter) { + ClearSignalMask(); + static uint64_t sigset; + struct sigaction sa = {}; + sa.sa_handler = [](int) { sigset = GetSignalMask(); }; + sigfillset(&sa.sa_mask); + sigaction(SIGUSR1, &sa, nullptr); + raise(SIGUSR1); + ASSERT_NE(0ULL, sigset); + TestSignalMaskFiltered(sigset, sigset_type); +} + +TEST(signal, sigaction64_filter) { + ClearSignalMask(); + static uint64_t sigset; + struct sigaction64 sa = {}; + sa.sa_handler = [](int) { sigset = GetSignalMask(); }; + sigfillset64(&sa.sa_mask); + sigaction64(SIGUSR1, &sa, nullptr); + raise(SIGUSR1); + ASSERT_NE(0ULL, sigset); + TestSignalMaskFiltered(sigset, SignalMaskFunctionType::RtAware); +} + +TEST(signal, sigprocmask_setmask_filter) { + TestSignalMaskFunction( + []() { + sigset_t sigset_libc; + sigfillset(&sigset_libc); + ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &sigset_libc, nullptr)); + }, + sigset_type); +} + +TEST(signal, sigprocmask64_setmask_filter) { + TestSignalMaskFunction( + []() { + sigset64_t sigset_libc; + sigfillset64(&sigset_libc); + ASSERT_EQ(0, sigprocmask64(SIG_SETMASK, &sigset_libc, nullptr)); + }, + SignalMaskFunctionType::RtAware); +} + +TEST(signal, pthread_sigmask_setmask_filter) { + TestSignalMaskFunction( + []() { + sigset_t sigset_libc; + sigfillset(&sigset_libc); + ASSERT_EQ(0, pthread_sigmask(SIG_SETMASK, &sigset_libc, nullptr)); + }, + sigset_type); +} + +TEST(signal, pthread_sigmask64_setmask_filter) { + TestSignalMaskFunction( + []() { + sigset64_t sigset_libc; + sigfillset64(&sigset_libc); + ASSERT_EQ(0, pthread_sigmask64(SIG_SETMASK, &sigset_libc, nullptr)); + }, + SignalMaskFunctionType::RtAware); +} + +TEST(signal, sigprocmask_block_filter) { + TestSignalMaskFunction( + []() { + sigset_t sigset_libc; + sigfillset(&sigset_libc); + ASSERT_EQ(0, sigprocmask(SIG_BLOCK, &sigset_libc, nullptr)); + }, + sigset_type); +} + +TEST(signal, sigprocmask64_block_filter) { + TestSignalMaskFunction( + []() { + sigset64_t sigset_libc; + sigfillset64(&sigset_libc); + ASSERT_EQ(0, sigprocmask64(SIG_BLOCK, &sigset_libc, nullptr)); + }, + SignalMaskFunctionType::RtAware); +} + +TEST(signal, pthread_sigmask_block_filter) { + TestSignalMaskFunction( + []() { + sigset_t sigset_libc; + sigfillset(&sigset_libc); + ASSERT_EQ(0, pthread_sigmask(SIG_BLOCK, &sigset_libc, nullptr)); + }, + sigset_type); +} + +TEST(signal, pthread_sigmask64_block_filter) { + TestSignalMaskFunction( + []() { + sigset64_t sigset_libc; + sigfillset64(&sigset_libc); + ASSERT_EQ(0, pthread_sigmask64(SIG_BLOCK, &sigset_libc, nullptr)); + }, + SignalMaskFunctionType::RtAware); +} + +// glibc filters out signals via sigfillset, not the actual underlying functions. +TEST(signal, sigset_filter) { +#if defined(__BIONIC__) + TestSignalMaskFunction( + []() { + for (int i = 1; i <= 64; ++i) { + sigset(i, SIG_HOLD); + } + }, + SignalMaskFunctionType::RtAware); +#endif +} + +TEST(signal, sighold_filter) { +#if defined(__BIONIC__) + TestSignalMaskFunction( + []() { + for (int i = 1; i <= 64; ++i) { + sighold(i); + } + }, + SignalMaskFunctionType::RtAware); +#endif +} + +#if defined(__BIONIC__) +// Not exposed via headers, but the symbols are available if you declare them yourself. +extern "C" int sigblock(int); +extern "C" int sigsetmask(int); +#endif + +TEST(signal, sigblock_filter) { +#if defined(__BIONIC__) + TestSignalMaskFunction( + []() { + int mask = ~0U; + ASSERT_EQ(0, sigblock(mask)); + }, + SignalMaskFunctionType::RtNonaware); +#endif +} + +TEST(signal, sigsetmask_filter) { +#if defined(__BIONIC__) + TestSignalMaskFunction( + []() { + int mask = ~0U; + ASSERT_EQ(0, sigsetmask(mask)); + }, + SignalMaskFunctionType::RtNonaware); +#endif +} + TEST(signal, sys_signame) { #if defined(__BIONIC__) ASSERT_TRUE(sys_signame[0] == NULL);