1 /* Copyright (c) 2005-2008, 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  * ---
31  * Author: Markus Gutschke, Carl Crous
32  *
33  * Code to extract a core dump snapshot of the current process.
34  */
35 #include "coredumper/coredumper.h"
36 
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdint.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 #include <unistd.h>
45 
46 #include "elfcore.h"
47 #include "linux_syscall_support.h"
48 #include "linuxthreads.h"
49 #include "thread_lister.h"
50 
51 static const char *const no_args_bzip2[] = {"bzip2", NULL};
52 static const char *const no_args_gzip[] = {"gzip", NULL};
53 static const char *const no_args_compress[] = {"compress", NULL};
54 const struct CoredumperCompressor COREDUMPER_COMPRESSED[] = {{"/bin/bzip2", no_args_bzip2, ".bz2"},
55                                                              {"/usr/bin/bzip2", no_args_bzip2, ".bz2"},
56                                                              {"bzip2", no_args_bzip2, ".bz2"},
57                                                              {"/bin/gzip", no_args_gzip, ".gz"},
58                                                              {"/usr/bin/gzip", no_args_gzip, ".gz"},
59                                                              {"gzip", no_args_gzip, ".gz"},
60                                                              {"/bin/compress", no_args_compress, ".Z"},
61                                                              {"/usr/bin/compress", no_args_compress, ".Z"},
62                                                              {"compress", no_args_compress, ".Z"},
63                                                              {"", 0, ""},
64                                                              {0, 0, 0}};
65 const struct CoredumperCompressor COREDUMPER_BZIP2_COMPRESSED[] = {{"/bin/bzip2", no_args_bzip2, ".bz2"},
66                                                                    {"/usr/bin/bzip2", no_args_bzip2, ".bz2"},
67                                                                    {"bzip2", no_args_bzip2, ".bz2"},
68                                                                    {0, 0, 0}};
69 const struct CoredumperCompressor COREDUMPER_GZIP_COMPRESSED[] = {{"/bin/gzip", no_args_gzip, ".gz"},
70                                                                   {"/usr/bin/gzip", no_args_gzip, ".gz"},
71                                                                   {"gzip", no_args_gzip, ".gz"},
72                                                                   {0, 0, 0}};
73 const struct CoredumperCompressor COREDUMPER_COMPRESS_COMPRESSED[] = {{"/bin/compress", no_args_compress, ".Z"},
74                                                                       {"/usr/bin/compress", no_args_compress, ".Z"},
75                                                                       {"compress", no_args_compress, ".Z"},
76                                                                       {0, 0, 0}};
77 const struct CoredumperCompressor COREDUMPER_TRY_BZIP2_COMPRESSED[] = {{"/bin/bzip2", no_args_bzip2, ".bz2"},
78                                                                        {"/usr/bin/bzip2", no_args_bzip2, ".bz2"},
79                                                                        {"bzip2", no_args_bzip2, ".bz2"},
80                                                                        {"", 0, ""},
81                                                                        {0, 0, 0}};
82 const struct CoredumperCompressor COREDUMPER_TRY_GZIP_COMPRESSED[] = {{"/bin/gzip", no_args_gzip, ".gz"},
83                                                                       {"/usr/bin/gzip", no_args_gzip, ".gz"},
84                                                                       {"gzip", no_args_gzip, ".gz"},
85                                                                       {"", 0, ""},
86                                                                       {0, 0, 0}};
87 const struct CoredumperCompressor COREDUMPER_TRY_COMPRESS_COMPRESSED[] = {{"/bin/compress", no_args_compress, ".Z"},
88                                                                           {"/usr/bin/compress", no_args_compress, ".Z"},
89                                                                           {"compress", no_args_compress, ".Z"},
90                                                                           {"", 0, ""},
91                                                                           {0, 0, 0}};
92 const struct CoredumperCompressor COREDUMPER_UNCOMPRESSED[] = {{"", 0, ""}, {0, 0, 0}};
93 
94 #ifndef DUMPER
95 /* If the target platform lacks the necessary support for generating core dumps
96  * on the fly, or if nobody has ported the code, then return an error.
97  */
98 typedef void *Frame;
99 #define FRAME(f)     \
100   void *f = &&label; \
101   label:
102 
InternalGetCoreDump(void * frame,int num_threads,pid_t * thread_pids,va_list ap)103 int InternalGetCoreDump(void *frame, int num_threads, pid_t *thread_pids, va_list ap) {
104   errno = EINVAL;
105   return -1;
106 }
107 #endif
108 
109 /* Internal helper method used by GetCoreDump().
110  */
GetCoreDumpFunction(void * frame,const struct CoreDumpParameters * params)111 static int GetCoreDumpFunction(void *frame, const struct CoreDumpParameters *params) {
112   return ListAllProcessThreads(frame, InternalGetCoreDump, params, NULL, getenv("PATH"));
113 }
114 
115 /* Returns a file handle that can be read to obtain a snapshot of the
116  * current state of this process. If a core file could not be
117  * generated for any reason, -1 is returned.
118  *
119  * This function momentarily suspends all threads, while creating a
120  * COW copy of the process's address space.
121  *
122  * This function is neither reentrant nor async signal safe. Callers
123  * should wrap a mutex around the invocation of this function, if necessary.
124  *
125  * The current implementation tries very hard to do behave reasonably when
126  * called from a signal handler, but no guarantees are made that this will
127  * always work.
128  */
GetCoreDump(void)129 int GetCoreDump(void) {
130   FRAME(frame);
131   struct CoreDumpParameters params;
132   ClearCoreDumpParameters(&params);
133   return GetCoreDumpFunction(&frame, &params);
134 }
135 
GetCoreDumpWith(const struct CoreDumpParameters * params)136 int GetCoreDumpWith(const struct CoreDumpParameters *params) {
137   FRAME(frame);
138   if ((params->flags & COREDUMPER_FLAG_LIMITED) || (params->flags & COREDUMPER_FLAG_LIMITED_BY_PRIORITY)) {
139     errno = EINVAL;
140     return -1;
141   }
142   return GetCoreDumpFunction(&frame, params);
143 }
144 
145 /* Attempts to compress the core file on the fly, if a suitable compressor
146  * could be located. Sets "selected_compressor" to the compressor that
147  * was picked.
148  */
GetCompressedCoreDump(const struct CoredumperCompressor compressors[],struct CoredumperCompressor ** selected_compressor)149 int GetCompressedCoreDump(const struct CoredumperCompressor compressors[],
150                           struct CoredumperCompressor **selected_compressor) {
151   FRAME(frame);
152   struct CoreDumpParameters params;
153   ClearCoreDumpParameters(&params);
154   SetCoreDumpCompressed(&params, compressors, selected_compressor);
155   return GetCoreDumpFunction(&frame, &params);
156 }
157 
158 /* Re-runs fn until it doesn't cause EINTR.
159  */
160 #define NO_INTR(fn) \
161   do {              \
162   } while ((fn) < 0 && errno == EINTR)
163 
164 /* Internal helper method used by WriteCoreDump().
165  */
WriteCoreDumpFunction(void * frame,const struct CoreDumpParameters * params,const char * file_name)166 static int WriteCoreDumpFunction(void *frame, const struct CoreDumpParameters *params, const char *file_name) {
167   return ListAllProcessThreads(frame, InternalGetCoreDump, params, file_name, getenv("PATH"));
168 }
169 
170 /* Writes the core file to disk. This is a convenience method wrapping
171  * GetCoreDump(). If a core file could not be generated for any reason,
172  * -1 is returned. On success, zero is returned.
173  */
WriteCoreDump(const char * file_name)174 int WriteCoreDump(const char *file_name) {
175   FRAME(frame);
176   struct CoreDumpParameters params;
177   ClearCoreDumpParameters(&params);
178   return WriteCoreDumpFunction(&frame, &params, file_name);
179 }
180 
WriteCoreDumpWith(const struct CoreDumpParameters * params,const char * file_name)181 int WriteCoreDumpWith(const struct CoreDumpParameters *params, const char *file_name) {
182   FRAME(frame);
183   return WriteCoreDumpFunction(&frame, params, file_name);
184 }
185 
186 /* Callers might need to restrict the maximum size of the core file. This
187  * convenience method provides the necessary support to emulate "ulimit -c".
188  */
WriteCoreDumpLimited(const char * file_name,size_t max_length)189 int WriteCoreDumpLimited(const char *file_name, size_t max_length) {
190   FRAME(frame);
191   struct CoreDumpParameters params;
192   ClearCoreDumpParameters(&params);
193   SetCoreDumpLimited(&params, max_length);
194   return WriteCoreDumpFunction(&frame, &params, file_name);
195 }
196 
197 /* This will limit the size of the core file by reducing or removing the
198  * largest memory segments first, effectively prioritizing the smaller memory
199  * segments. This behavior is preferred when the process has a large heap and
200  * you would like to preserve the relatively small stack.
201  */
WriteCoreDumpLimitedByPriority(const char * file_name,size_t max_length)202 int WriteCoreDumpLimitedByPriority(const char *file_name, size_t max_length) {
203   FRAME(frame);
204   struct CoreDumpParameters params;
205   ClearCoreDumpParameters(&params);
206   SetCoreDumpLimitedByPriority(&params, max_length);
207   return WriteCoreDumpFunction(&frame, &params, file_name);
208 }
209 
210 /* Attempts to compress the core file on the fly, if a suitable compressor
211  * could be located. Sets "selected_compressor" to the compressor that
212  * was picked. The filename automatically has a suitable suffix appended
213  * to it. Normally this would be ".bz2" for bzip2 compression ".gz" for
214  * gzip compression, or ".Z" for compress compression. This behavior can
215  * be changed by defining custom CoredumperCompressor descriptions.
216  */
WriteCompressedCoreDump(const char * file_name,size_t max_length,const struct CoredumperCompressor compressors[],struct CoredumperCompressor ** selected_compressor)217 int WriteCompressedCoreDump(const char *file_name, size_t max_length, const struct CoredumperCompressor compressors[],
218                             struct CoredumperCompressor **selected_compressor) {
219   FRAME(frame);
220   struct CoreDumpParameters params;
221   ClearCoreDumpParameters(&params);
222   SetCoreDumpCompressed(&params, compressors, selected_compressor);
223   SetCoreDumpLimited(&params, max_length);
224   return WriteCoreDumpFunction(&frame, &params, file_name);
225 }
226 
ClearCoreDumpParametersInternal(struct CoreDumpParameters * params,size_t size)227 void ClearCoreDumpParametersInternal(struct CoreDumpParameters *params, size_t size) {
228   memset(params, 0, size);
229   params->size = size;
230   SetCoreDumpParameter(params, max_length, SIZE_MAX);
231 }
232 
SetCoreDumpLimited(struct CoreDumpParameters * params,size_t max_length)233 int SetCoreDumpLimited(struct CoreDumpParameters *params, size_t max_length) {
234   if (params->flags & COREDUMPER_FLAG_LIMITED_BY_PRIORITY) {
235     errno = EINVAL;
236     return -1;
237   }
238   params->flags |= COREDUMPER_FLAG_LIMITED;
239   SetCoreDumpParameter(params, max_length, max_length);
240   return 0;
241 }
242 
SetCoreDumpCompressed(struct CoreDumpParameters * params,const struct CoredumperCompressor * compressors,struct CoredumperCompressor ** selected_compressor)243 int SetCoreDumpCompressed(struct CoreDumpParameters *params, const struct CoredumperCompressor *compressors,
244                           struct CoredumperCompressor **selected_compressor) {
245   if (params->flags & COREDUMPER_FLAG_LIMITED_BY_PRIORITY) {
246     errno = EINVAL;
247     return -1;
248   }
249   SetCoreDumpParameter(params, compressors, compressors);
250   SetCoreDumpParameter(params, selected_compressor, selected_compressor);
251   return 0;
252 }
253 
SetCoreDumpLimitedByPriority(struct CoreDumpParameters * params,size_t max_length)254 int SetCoreDumpLimitedByPriority(struct CoreDumpParameters *params, size_t max_length) {
255   if (((params->flags & COREDUMPER_FLAG_LIMITED) && !(params->flags & COREDUMPER_FLAG_LIMITED_BY_PRIORITY)) ||
256       params->compressors != NULL) {
257     errno = EINVAL;
258     return -1;
259   }
260   SetCoreDumpParameter(params, flags, params->flags | COREDUMPER_FLAG_LIMITED | COREDUMPER_FLAG_LIMITED_BY_PRIORITY);
261   SetCoreDumpParameter(params, max_length, max_length);
262   return 0;
263 }
264 
SetCoreDumpNotes(struct CoreDumpParameters * params,struct CoredumperNote * notes,int note_count)265 int SetCoreDumpNotes(struct CoreDumpParameters *params, struct CoredumperNote *notes, int note_count) {
266   SetCoreDumpParameter(params, notes, notes);
267   SetCoreDumpParameter(params, note_count, note_count);
268   return 0;
269 }
270 
SetCoreDumpCallback(struct CoreDumpParameters * params,int (* fn)(void *),void * arg)271 int SetCoreDumpCallback(struct CoreDumpParameters *params, int (*fn)(void *), void *arg) {
272   SetCoreDumpParameter(params, callback_fn, fn);
273   SetCoreDumpParameter(params, callback_arg, arg);
274   return 0;
275 }
276