blob: bb997877a1c864bf13cc140eb1c2bbad7ab0d474 [file] [log] [blame]
Josh Gao1eac77e2018-02-26 15:54:41 -08001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
Josh Gao3bef5272018-09-04 14:33:23 -070029#include <dlfcn.h>
30#include <pthread.h>
Josh Gao1eac77e2018-02-26 15:54:41 -080031#include <signal.h>
32#include <sys/syscall.h>
33
34#include <functional>
35
36#include <gtest/gtest.h>
37
38#include "sigchain.h"
39
40#if !defined(__BIONIC__)
Andreas Gampec55bb392018-09-21 00:02:02 +000041using sigset64_t = sigset_t;
Josh Gao1eac77e2018-02-26 15:54:41 -080042
43static int sigemptyset64(sigset64_t* set) {
44 return sigemptyset(set);
45}
46
47static int sigismember64(sigset64_t* set, int member) {
48 return sigismember(set, member);
49}
50#endif
51
52static int RealSigprocmask(int how, const sigset64_t* new_sigset, sigset64_t* old_sigset) {
53 // glibc's sigset_t is overly large, so sizeof(*new_sigset) doesn't work.
Goran Jakovljevic247ff372018-03-07 10:34:05 +010054 return syscall(__NR_rt_sigprocmask, how, new_sigset, old_sigset, NSIG/8);
Josh Gao1eac77e2018-02-26 15:54:41 -080055}
56
57class SigchainTest : public ::testing::Test {
58 void SetUp() final {
59 art::AddSpecialSignalHandlerFn(SIGSEGV, &action);
60 }
61
62 void TearDown() final {
63 art::RemoveSpecialSignalHandlerFn(SIGSEGV, action.sc_sigaction);
64 }
65
66 art::SigchainAction action = {
Josh Gao3bef5272018-09-04 14:33:23 -070067 .sc_sigaction = [](int, siginfo_t* info, void*) -> bool {
68 return info->si_value.sival_ptr;
69 },
Josh Gao1eac77e2018-02-26 15:54:41 -080070 .sc_mask = {},
71 .sc_flags = 0,
72 };
Josh Gao3bef5272018-09-04 14:33:23 -070073
74 protected:
75 void RaiseHandled() {
76 sigval_t value;
77 value.sival_ptr = &value;
78 pthread_sigqueue(pthread_self(), SIGSEGV, value);
79 }
80
81 void RaiseUnhandled() {
82 sigval_t value;
83 value.sival_ptr = nullptr;
84 pthread_sigqueue(pthread_self(), SIGSEGV, value);
85 }
Josh Gao1eac77e2018-02-26 15:54:41 -080086};
87
88
Andreas Gampebc802de2018-06-20 17:24:11 -070089static void TestSignalBlocking(const std::function<void()>& fn) {
Josh Gao1eac77e2018-02-26 15:54:41 -080090 // Unblock SIGSEGV, make sure it stays unblocked.
91 sigset64_t mask;
92 sigemptyset64(&mask);
93 ASSERT_EQ(0, RealSigprocmask(SIG_SETMASK, &mask, nullptr)) << strerror(errno);
94
95 fn();
96
97 if (testing::Test::HasFatalFailure()) return;
98 ASSERT_EQ(0, RealSigprocmask(SIG_SETMASK, nullptr, &mask));
99 ASSERT_FALSE(sigismember64(&mask, SIGSEGV));
100}
101
102TEST_F(SigchainTest, sigprocmask_setmask) {
103 TestSignalBlocking([]() {
104 sigset_t mask;
105 sigfillset(&mask);
106 ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &mask, nullptr));
107 });
108}
109
110TEST_F(SigchainTest, sigprocmask_block) {
111 TestSignalBlocking([]() {
112 sigset_t mask;
113 sigfillset(&mask);
114 ASSERT_EQ(0, sigprocmask(SIG_BLOCK, &mask, nullptr));
115 });
116}
117
118// bionic-only wide variants for LP32.
119#if defined(__BIONIC__)
120TEST_F(SigchainTest, sigprocmask64_setmask) {
121 TestSignalBlocking([]() {
122 sigset64_t mask;
123 sigfillset64(&mask);
124 ASSERT_EQ(0, sigprocmask64(SIG_SETMASK, &mask, nullptr));
125 });
126}
127
128TEST_F(SigchainTest, sigprocmask64_block) {
129 TestSignalBlocking([]() {
130 sigset64_t mask;
131 sigfillset64(&mask);
132 ASSERT_EQ(0, sigprocmask64(SIG_BLOCK, &mask, nullptr));
133 });
134}
135
136TEST_F(SigchainTest, pthread_sigmask64_setmask) {
137 TestSignalBlocking([]() {
138 sigset64_t mask;
139 sigfillset64(&mask);
140 ASSERT_EQ(0, pthread_sigmask64(SIG_SETMASK, &mask, nullptr));
141 });
142}
143
144TEST_F(SigchainTest, pthread_sigmask64_block) {
145 TestSignalBlocking([]() {
146 sigset64_t mask;
147 sigfillset64(&mask);
148 ASSERT_EQ(0, pthread_sigmask64(SIG_BLOCK, &mask, nullptr));
149 });
150}
151#endif
152
153// glibc doesn't implement most of these in terms of sigprocmask, which we rely on.
154#if defined(__BIONIC__)
155TEST_F(SigchainTest, pthread_sigmask_setmask) {
156 TestSignalBlocking([]() {
157 sigset_t mask;
158 sigfillset(&mask);
159 ASSERT_EQ(0, pthread_sigmask(SIG_SETMASK, &mask, nullptr));
160 });
161}
162
163TEST_F(SigchainTest, pthread_sigmask_block) {
164 TestSignalBlocking([]() {
165 sigset_t mask;
166 sigfillset(&mask);
167 ASSERT_EQ(0, pthread_sigmask(SIG_BLOCK, &mask, nullptr));
168 });
169}
170
171TEST_F(SigchainTest, sigset_mask) {
172 TestSignalBlocking([]() {
173 sigset(SIGSEGV, SIG_HOLD);
174 });
175}
176
177TEST_F(SigchainTest, sighold) {
178 TestSignalBlocking([]() {
179 sighold(SIGSEGV);
180 });
181}
182
183#if defined(__BIONIC__)
184// Not exposed via headers, but the symbols are available if you declare them yourself.
185extern "C" int sigblock(int);
186extern "C" int sigsetmask(int);
187#endif
188
189TEST_F(SigchainTest, sigblock) {
190 TestSignalBlocking([]() {
191 int mask = ~0U;
192 ASSERT_EQ(0, sigblock(mask));
193 });
194}
195
196TEST_F(SigchainTest, sigsetmask) {
197 TestSignalBlocking([]() {
198 int mask = ~0U;
199 ASSERT_EQ(0, sigsetmask(mask));
200 });
201}
202
203#endif
Josh Gao3bef5272018-09-04 14:33:23 -0700204
205// Make sure that we properly put ourselves back in front if we get circumvented.
206TEST_F(SigchainTest, EnsureFrontOfChain) {
207#if defined(__BIONIC__)
208 constexpr char kLibcSoName[] = "libc.so";
209#elif defined(__GNU_LIBRARY__) && __GNU_LIBRARY__ == 6
210 constexpr char kLibcSoName[] = "libc.so.6";
211#else
212 #error Unknown libc
213#endif
214 void* libc = dlopen(kLibcSoName, RTLD_LAZY | RTLD_NOLOAD);
215 ASSERT_TRUE(libc);
216
217 static sig_atomic_t called = 0;
218 struct sigaction action = {};
219 action.sa_flags = SA_SIGINFO;
220 action.sa_sigaction = [](int, siginfo_t*, void*) { called = 1; };
221
222 ASSERT_EQ(0, sigaction(SIGSEGV, &action, nullptr));
223
224 // Try before EnsureFrontOfChain.
225 RaiseHandled();
226 ASSERT_EQ(0, called);
227
228 RaiseUnhandled();
229 ASSERT_EQ(1, called);
230 called = 0;
231
232 // ...and after.
233 art::EnsureFrontOfChain(SIGSEGV);
234 ASSERT_EQ(0, called);
235 called = 0;
236
237 RaiseUnhandled();
238 ASSERT_EQ(1, called);
239 called = 0;
240}