1 /* Catch segmentation faults and print backtrace.
2    Copyright (C) 1998-2021 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <alloca.h>
20 #include <ctype.h>
21 #include <errno.h>
22 #include <execinfo.h>
23 #include <fcntl.h>
24 #include <signal.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <_itoa.h>
31 #include <ldsodefs.h>
32 
33 /* This file defines macros to access the content of the sigcontext element
34    passed up by the signal handler.  */
35 #include <sigcontextinfo.h>
36 
37 #ifdef SA_SIGINFO
38 # define SIGCONTEXT siginfo_t *info, void *
39 #endif
40 
41 /* Get code to possibly dump the content of all registers.  */
42 #include <register-dump.h>
43 
44 /* We'll use this a lot.  */
45 #define WRITE_STRING(s) write (fd, s, strlen (s))
46 
47 /* Name of the output file.  */
48 static const char *fname;
49 
50 
51 /* Print the signal number SIGNAL.  Either strerror or strsignal might
52    call local internal functions and these in turn call far too many
53    other functions and might even allocate memory which might fail.  */
54 static void
write_strsignal(int fd,int signal)55 write_strsignal (int fd, int signal)
56 {
57   char buf[30];
58   char *ptr = _itoa_word (signal, &buf[sizeof (buf)], 10, 0);
59   WRITE_STRING ("signal ");
60   write (fd, ptr, &buf[sizeof (buf)] - ptr);
61 }
62 
63 
64 /* This function is called when a segmentation fault is caught.  The system
65    is in an unstable state now.  This means especially that malloc() might
66    not work anymore.  */
67 static void
catch_segfault(int signal,SIGCONTEXT ctx)68 catch_segfault (int signal, SIGCONTEXT ctx)
69 {
70   int fd, cnt, i;
71   void **arr;
72   struct sigaction sa;
73   uintptr_t pc;
74 
75   /* This is the name of the file we are writing to.  If none is given
76      or we cannot write to this file write to stderr.  */
77   fd = 2;
78   if (fname != NULL)
79     {
80       fd = open (fname, O_TRUNC | O_WRONLY | O_CREAT, 0666);
81       if (fd == -1)
82 	fd = 2;
83     }
84 
85   WRITE_STRING ("*** ");
86   write_strsignal (fd, signal);
87   WRITE_STRING ("\n");
88 
89 #ifdef REGISTER_DUMP
90   REGISTER_DUMP;
91 #endif
92 
93   WRITE_STRING ("\nBacktrace:\n");
94 
95   /* Get the backtrace.  */
96   arr = alloca (256 * sizeof (void *));
97   cnt = backtrace (arr, 256);
98 
99   /* Now try to locate the PC from signal context in the backtrace.
100      Normally it will be found at arr[2], but it might appear later
101      if there were some signal handler wrappers.  Allow a few bytes
102      difference to cope with as many arches as possible.  */
103   pc = sigcontext_get_pc (ctx);
104   for (i = 0; i < cnt; ++i)
105     if ((uintptr_t) arr[i] >= pc - 16 && (uintptr_t) arr[i] <= pc + 16)
106       break;
107 
108   /* If we haven't found it, better dump full backtrace even including
109      the signal handler frames instead of not dumping anything.  */
110   if (i == cnt)
111     i = 0;
112 
113   /* Now generate nicely formatted output.  */
114   __backtrace_symbols_fd (arr + i, cnt - i, fd);
115 
116 #ifdef HAVE_PROC_SELF
117   /* Now the link map.  */
118   int mapfd = open ("/proc/self/maps", O_RDONLY);
119   if (mapfd != -1)
120     {
121       write (fd, "\nMemory map:\n\n", 14);
122 
123       char buf[256];
124       ssize_t n;
125 
126       while ((n = TEMP_FAILURE_RETRY (read (mapfd, buf, sizeof (buf)))) > 0)
127 	TEMP_FAILURE_RETRY (write (fd, buf, n));
128 
129       close (mapfd);
130     }
131 #endif
132 
133   /* Pass on the signal (so that a core file is produced).  */
134   sa.sa_handler = SIG_DFL;
135   sigemptyset (&sa.sa_mask);
136   sa.sa_flags = 0;
137   sigaction (signal, &sa, NULL);
138   raise (signal);
139 }
140 
141 
142 static void
143 __attribute__ ((constructor))
install_handler(void)144 install_handler (void)
145 {
146   struct sigaction sa;
147   const char *sigs = getenv ("SEGFAULT_SIGNALS");
148   const char *name;
149 
150 #ifdef SA_SIGINFO
151   sa.sa_sigaction = catch_segfault;
152   sa.sa_flags = SA_SIGINFO;
153 #else
154   sa.sa_handler = (void*) catch_segfault;
155   sa.sa_flags = 0;
156 #endif
157   sigemptyset (&sa.sa_mask);
158   sa.sa_flags |= SA_RESTART;
159 
160   /* Maybe we are expected to use an alternative stack.  */
161   if (getenv ("SEGFAULT_USE_ALTSTACK") != 0)
162     {
163       void *stack_mem = malloc (2 * SIGSTKSZ);
164       stack_t ss;
165 
166       if (stack_mem != NULL)
167 	{
168 	  ss.ss_sp = stack_mem;
169 	  ss.ss_flags = 0;
170 	  ss.ss_size = 2 * SIGSTKSZ;
171 
172 	  if (sigaltstack (&ss, NULL) == 0)
173 	    sa.sa_flags |= SA_ONSTACK;
174 	}
175     }
176 
177   if (sigs == NULL)
178     sigaction (SIGSEGV, &sa, NULL);
179   else if (sigs[0] == '\0')
180     /* Do not do anything.  */
181     return;
182   else
183     {
184       const char *where;
185       int all = __strcasecmp (sigs, "all") == 0;
186 
187 #define INSTALL_FOR_SIG(sig, name) \
188       where = __strcasestr (sigs, name);				      \
189       if (all || (where != NULL						      \
190 		  && (where == sigs || !isalnum (where[-1]))		      \
191 		  && !isalnum (where[sizeof (name) - 1])))		      \
192 	sigaction (sig, &sa, NULL);
193 
194       INSTALL_FOR_SIG (SIGSEGV, "segv");
195       INSTALL_FOR_SIG (SIGILL, "ill");
196 #ifdef SIGBUS
197       INSTALL_FOR_SIG (SIGBUS, "bus");
198 #endif
199 #ifdef SIGSTKFLT
200       INSTALL_FOR_SIG (SIGSTKFLT, "stkflt");
201 #endif
202       INSTALL_FOR_SIG (SIGABRT, "abrt");
203       INSTALL_FOR_SIG (SIGFPE, "fpe");
204     }
205 
206   /* Preserve the output file name if there is any given.  */
207   name = getenv ("SEGFAULT_OUTPUT_NAME");
208   if (name != NULL && name[0] != '\0')
209     {
210       int ret = access (name, R_OK | W_OK);
211 
212       if (ret == 0 || (ret == -1 && errno == ENOENT))
213 	fname = __strdup (name);
214     }
215 }
216