1 // Copyright 2011 Google LLC
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
5 // met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
12 // distribution.
13 // * Neither the name of Google LLC nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 // crash_generator.cc: Implement google_breakpad::CrashGenerator.
30 // See crash_generator.h for details.
31
32 #ifdef HAVE_CONFIG_H
33 #include <config.h> // Must come first
34 #endif
35
36 #include "common/linux/tests/crash_generator.h"
37
38 #include <pthread.h>
39 #include <sched.h>
40 #include <signal.h>
41 #include <stdio.h>
42 #include <sys/mman.h>
43 #include <sys/resource.h>
44 #include <sys/syscall.h>
45 #include <sys/wait.h>
46 #include <unistd.h>
47
48 #include <string>
49
50 #if defined(__ANDROID__)
51 #include "common/android/testing/pthread_fixes.h"
52 #endif
53 #include "common/linux/eintr_wrapper.h"
54 #include "common/tests/auto_tempdir.h"
55 #include "common/tests/file_utils.h"
56 #include "common/using_std_string.h"
57
58 namespace {
59
60 struct ThreadData {
61 pthread_t thread;
62 pthread_barrier_t* barrier;
63 pid_t* thread_id_ptr;
64 };
65
66 const char* const kProcFilesToCopy[] = {
67 "auxv", "cmdline", "environ", "maps", "status"
68 };
69 const size_t kNumProcFilesToCopy =
70 sizeof(kProcFilesToCopy) / sizeof(kProcFilesToCopy[0]);
71
gettid()72 int gettid() {
73 // Glibc does not provide a wrapper for this.
74 return syscall(__NR_gettid);
75 }
76
tkill(pid_t tid,int sig)77 int tkill(pid_t tid, int sig) {
78 // Glibc does not provide a wrapper for this.
79 return syscall(__NR_tkill, tid, sig);
80 }
81
82 // Core file size limit set to 1 MB, which is big enough for test purposes.
83 const rlim_t kCoreSizeLimit = 1024 * 1024;
84
thread_function(void * data)85 void* thread_function(void* data) {
86 ThreadData* thread_data = reinterpret_cast<ThreadData*>(data);
87 volatile pid_t thread_id = gettid();
88 *(thread_data->thread_id_ptr) = thread_id;
89 int result = pthread_barrier_wait(thread_data->barrier);
90 if (result != 0 && result != PTHREAD_BARRIER_SERIAL_THREAD) {
91 perror("Failed to wait for sync barrier");
92 exit(1);
93 }
94 while (true) {
95 sched_yield();
96 }
97 }
98
99 } // namespace
100
101 namespace google_breakpad {
102
CrashGenerator()103 CrashGenerator::CrashGenerator()
104 : shared_memory_(NULL),
105 shared_memory_size_(0) {
106 }
107
~CrashGenerator()108 CrashGenerator::~CrashGenerator() {
109 UnmapSharedMemory();
110 }
111
HasDefaultCorePattern() const112 bool CrashGenerator::HasDefaultCorePattern() const {
113 char buffer[8];
114 ssize_t buffer_size = sizeof(buffer);
115 return ReadFile("/proc/sys/kernel/core_pattern", buffer, &buffer_size) &&
116 buffer_size == 5 && memcmp(buffer, "core", 4) == 0;
117 }
118
GetCoreFilePath() const119 string CrashGenerator::GetCoreFilePath() const {
120 return temp_dir_.path() + "/core";
121 }
122
GetDirectoryOfProcFilesCopy() const123 string CrashGenerator::GetDirectoryOfProcFilesCopy() const {
124 return temp_dir_.path() + "/proc";
125 }
126
GetThreadId(unsigned index) const127 pid_t CrashGenerator::GetThreadId(unsigned index) const {
128 return reinterpret_cast<pid_t*>(shared_memory_)[index];
129 }
130
GetThreadIdPointer(unsigned index)131 pid_t* CrashGenerator::GetThreadIdPointer(unsigned index) {
132 return reinterpret_cast<pid_t*>(shared_memory_) + index;
133 }
134
MapSharedMemory(size_t memory_size)135 bool CrashGenerator::MapSharedMemory(size_t memory_size) {
136 if (!UnmapSharedMemory())
137 return false;
138
139 void* mapped_memory = mmap(0, memory_size, PROT_READ | PROT_WRITE,
140 MAP_SHARED | MAP_ANONYMOUS, -1, 0);
141 if (mapped_memory == MAP_FAILED) {
142 perror("CrashGenerator: Failed to map shared memory");
143 return false;
144 }
145
146 memset(mapped_memory, 0, memory_size);
147 shared_memory_ = mapped_memory;
148 shared_memory_size_ = memory_size;
149 return true;
150 }
151
UnmapSharedMemory()152 bool CrashGenerator::UnmapSharedMemory() {
153 if (!shared_memory_)
154 return true;
155
156 if (munmap(shared_memory_, shared_memory_size_) == 0) {
157 shared_memory_ = NULL;
158 shared_memory_size_ = 0;
159 return true;
160 }
161
162 perror("CrashGenerator: Failed to unmap shared memory");
163 return false;
164 }
165
SetCoreFileSizeLimit(rlim_t limit) const166 bool CrashGenerator::SetCoreFileSizeLimit(rlim_t limit) const {
167 struct rlimit limits = { limit, limit };
168 if (setrlimit(RLIMIT_CORE, &limits) == -1) {
169 perror("CrashGenerator: Failed to set core file size limit");
170 return false;
171 }
172 return true;
173 }
174
HasResourceLimitsAmenableToCrashCollection() const175 bool CrashGenerator::HasResourceLimitsAmenableToCrashCollection() const {
176 struct rlimit limits;
177 if (getrlimit(RLIMIT_CORE, &limits) == -1) {
178 perror("CrashGenerator: Failed to get core file size limit");
179 return false;
180 }
181 return limits.rlim_max >= kCoreSizeLimit;
182 }
183
CreateChildCrash(unsigned num_threads,unsigned crash_thread,int crash_signal,pid_t * child_pid)184 bool CrashGenerator::CreateChildCrash(
185 unsigned num_threads, unsigned crash_thread, int crash_signal,
186 pid_t* child_pid) {
187 if (num_threads == 0 || crash_thread >= num_threads) {
188 fprintf(stderr, "CrashGenerator: Invalid thread counts; num_threads=%u"
189 " crash_thread=%u\n", num_threads, crash_thread);
190 return false;
191 }
192
193 if (!MapSharedMemory(num_threads * sizeof(pid_t))) {
194 perror("CrashGenerator: Unable to map shared memory");
195 return false;
196 }
197
198 pid_t pid = fork();
199 if (pid == 0) {
200 // Custom signal handlers, which may have been installed by a test launcher,
201 // are undesirable in this child.
202 if (signal(crash_signal, SIG_DFL) == SIG_ERR) {
203 perror("CrashGenerator: signal");
204 exit(1);
205 }
206 if (chdir(temp_dir_.path().c_str()) == -1) {
207 perror("CrashGenerator: Failed to change directory");
208 exit(1);
209 }
210 if (SetCoreFileSizeLimit(kCoreSizeLimit)) {
211 CreateThreadsInChildProcess(num_threads);
212 string proc_dir = GetDirectoryOfProcFilesCopy();
213 if (mkdir(proc_dir.c_str(), 0755) == -1) {
214 perror("CrashGenerator: Failed to create proc directory");
215 exit(1);
216 }
217 if (!CopyProcFiles(getpid(), proc_dir.c_str())) {
218 fprintf(stderr, "CrashGenerator: Failed to copy proc files\n");
219 exit(1);
220 }
221 // On Android the signal sometimes doesn't seem to get sent even though
222 // tkill returns '0'. Retry a couple of times if the signal doesn't get
223 // through on the first go:
224 // https://bugs.chromium.org/p/google-breakpad/issues/detail?id=579
225 #if defined(__ANDROID__)
226 const int kRetries = 60;
227 const unsigned int kSleepTimeInSeconds = 1;
228 #else
229 const int kRetries = 1;
230 const unsigned int kSleepTimeInSeconds = 600;
231 #endif
232 for (int i = 0; i < kRetries; i++) {
233 if (tkill(*GetThreadIdPointer(crash_thread), crash_signal) == -1) {
234 perror("CrashGenerator: Failed to kill thread by signal");
235 } else {
236 // At this point, we've queued the signal for delivery, but there's no
237 // guarantee when it'll be delivered. We don't want the main thread to
238 // race and exit before the thread we signaled is processed. So sleep
239 // long enough that we won't flake even under fairly high load.
240 // TODO: See if we can't be a bit more deterministic. There doesn't
241 // seem to be an API to check on signal delivery status, so we can't
242 // really poll and wait for the kernel to declare the signal has been
243 // delivered. If it has, and things worked, we'd be killed, so the
244 // sleep length doesn't really matter.
245 sleep(kSleepTimeInSeconds);
246 }
247 }
248 } else {
249 perror("CrashGenerator: Failed to set core limit");
250 }
251 exit(1);
252 } else if (pid == -1) {
253 perror("CrashGenerator: Failed to create child process");
254 return false;
255 }
256
257 int status;
258 if (HANDLE_EINTR(waitpid(pid, &status, 0)) == -1) {
259 perror("CrashGenerator: Failed to wait for child process");
260 return false;
261 }
262 if (!WIFSIGNALED(status) || WTERMSIG(status) != crash_signal) {
263 fprintf(stderr, "CrashGenerator: Child process not killed by the expected signal\n"
264 " exit status=0x%x pid=%u signaled=%s sig=%d expected=%d\n",
265 status, pid, WIFSIGNALED(status) ? "true" : "false",
266 WTERMSIG(status), crash_signal);
267 return false;
268 }
269
270 if (child_pid)
271 *child_pid = pid;
272 return true;
273 }
274
CopyProcFiles(pid_t pid,const char * path) const275 bool CrashGenerator::CopyProcFiles(pid_t pid, const char* path) const {
276 char from_path[PATH_MAX], to_path[PATH_MAX];
277 for (size_t i = 0; i < kNumProcFilesToCopy; ++i) {
278 int num_chars = snprintf(from_path, PATH_MAX, "/proc/%d/%s",
279 pid, kProcFilesToCopy[i]);
280 if (num_chars < 0 || num_chars >= PATH_MAX)
281 return false;
282
283 num_chars = snprintf(to_path, PATH_MAX, "%s/%s",
284 path, kProcFilesToCopy[i]);
285 if (num_chars < 0 || num_chars >= PATH_MAX)
286 return false;
287
288 if (!CopyFile(from_path, to_path))
289 return false;
290 }
291 return true;
292 }
293
CreateThreadsInChildProcess(unsigned num_threads)294 void CrashGenerator::CreateThreadsInChildProcess(unsigned num_threads) {
295 *GetThreadIdPointer(0) = getpid();
296
297 if (num_threads <= 1)
298 return;
299
300 // This method does not clean up any pthread resource, as the process
301 // is expected to be killed anyway.
302 ThreadData* thread_data = new ThreadData[num_threads];
303
304 // Create detached threads so that we do not worry about pthread_join()
305 // later being called or not.
306 pthread_attr_t thread_attributes;
307 if (pthread_attr_init(&thread_attributes) != 0 ||
308 pthread_attr_setdetachstate(&thread_attributes,
309 PTHREAD_CREATE_DETACHED) != 0) {
310 fprintf(stderr, "CrashGenerator: Failed to initialize thread attribute\n");
311 exit(1);
312 }
313
314 pthread_barrier_t thread_barrier;
315 if (pthread_barrier_init(&thread_barrier, NULL, num_threads) != 0) {
316 fprintf(stderr, "CrashGenerator: Failed to initialize thread barrier\n");
317 exit(1);
318 }
319
320 for (unsigned i = 1; i < num_threads; ++i) {
321 thread_data[i].barrier = &thread_barrier;
322 thread_data[i].thread_id_ptr = GetThreadIdPointer(i);
323 if (pthread_create(&thread_data[i].thread, &thread_attributes,
324 thread_function, &thread_data[i]) != 0) {
325 fprintf(stderr, "CrashGenerator: Failed to create thread %d\n", i);
326 exit(1);
327 }
328 }
329
330 int result = pthread_barrier_wait(&thread_barrier);
331 if (result != 0 && result != PTHREAD_BARRIER_SERIAL_THREAD) {
332 fprintf(stderr, "CrashGenerator: Failed to wait for thread barrier\n");
333 exit(1);
334 }
335
336 pthread_barrier_destroy(&thread_barrier);
337 pthread_attr_destroy(&thread_attributes);
338 delete[] thread_data;
339 }
340
341 } // namespace google_breakpad
342