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