xref: /aosp_15_r20/external/google-breakpad/src/common/linux/tests/crash_generator.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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