1 /* Copyright (c) 2011, 2012, Oracle and/or its affiliates.
2    Copyright (c) 2011, 2021, MariaDB Corporation.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; version 2 of the License.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1335  USA */
16 
17 #include "mariadb.h"
18 #include "my_dbug.h"
19 #include <signal.h>
20 
21 //#include "sys_vars.h"
22 #include <keycache.h>
23 #include "mysqld.h"
24 #include "sql_class.h"
25 #include "my_stacktrace.h"
26 
27 #ifdef __WIN__
28 #include <crtdbg.h>
29 #define SIGNAL_FMT "exception 0x%x"
30 #else
31 #define SIGNAL_FMT "signal %d"
32 #endif
33 
34 
35 #if defined(__APPLE__) || defined(__FreeBSD__)
36 #include <sys/sysctl.h>
37 #endif
38 
39 #ifndef PATH_MAX
40 #define PATH_MAX 4096
41 #endif
42 
43 /*
44   We are handling signals/exceptions in this file.
45   Any global variables we read should be 'volatile sig_atomic_t'
46   to guarantee that we read some consistent value.
47  */
48 static volatile sig_atomic_t segfaulted= 0;
49 extern ulong max_used_connections;
50 extern volatile sig_atomic_t calling_initgroups;
51 
52 extern const char *optimizer_switch_names[];
53 
output_core_info()54 static inline void output_core_info()
55 {
56   /* proc is optional on some BSDs so it can't hurt to look */
57 #if defined(HAVE_READLINK) && !defined(__APPLE__) && !defined(__FreeBSD__)
58   char buff[PATH_MAX];
59   ssize_t len;
60   int fd;
61   if ((len= readlink("/proc/self/cwd", buff, sizeof(buff)-1)) >= 0)
62   {
63     buff[len]= 0;
64     my_safe_printf_stderr("Writing a core file...\nWorking directory at %.*s\n",
65                           (int) len, buff);
66   }
67 #ifdef __FreeBSD__
68   if ((fd= my_open("/proc/curproc/rlimit", O_RDONLY, MYF(0))) >= 0)
69 #else
70   if ((fd= my_open("/proc/self/limits", O_RDONLY, MYF(0))) >= 0)
71 #endif
72   {
73     my_safe_printf_stderr("Resource Limits:\n");
74     while ((len= my_read(fd, (uchar*)buff, sizeof(buff),  MYF(0))) > 0)
75     {
76       my_write_stderr(buff, len);
77     }
78     my_close(fd, MYF(0));
79   }
80 #ifdef __linux__
81   if ((fd= my_open("/proc/sys/kernel/core_pattern", O_RDONLY, MYF(0))) >= 0)
82   {
83     len= my_read(fd, (uchar*)buff, sizeof(buff),  MYF(0));
84     my_safe_printf_stderr("Core pattern: %.*s\n", (int) len, buff);
85     my_close(fd, MYF(0));
86   }
87 #endif
88 #elif defined(__APPLE__) || defined(__FreeBSD__)
89   char buff[PATH_MAX];
90   size_t len = sizeof(buff);
91   if (sysctlbyname("kern.corefile", buff, &len, NULL, 0) == 0)
92   {
93     my_safe_printf_stderr("Core pattern: %.*s\n", (int) len, buff);
94   }
95 #else
96   char buff[80];
97   my_getwd(buff, sizeof(buff), 0);
98   my_safe_printf_stderr("Writing a core file at %s\n", buff);
99   fflush(stderr);
100 #endif
101 }
102 
103 /**
104  * Handler for fatal signals on POSIX, exception handler on Windows.
105  *
106  * Fatal events (seg.fault, bus error etc.) will trigger
107  * this signal handler.  The handler will try to dump relevant
108  * debugging information to stderr and dump a core image.
109  *
110  * POSIX : Signal handlers should, if possible, only use a set of 'safe' system
111  * calls and library functions.  A list of safe calls in POSIX systems
112  * are available at:
113  *  http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
114  *
115  * @param sig Signal number /Exception code
116 */
handle_fatal_signal(int sig)117 extern "C" sig_handler handle_fatal_signal(int sig)
118 {
119   time_t curr_time;
120   struct tm tm;
121 #ifdef HAVE_STACKTRACE
122   THD *thd;
123   /*
124      This flag remembers if the query pointer was found invalid.
125      We will try and print the query at the end of the signal handler, in case
126      we're wrong.
127   */
128   bool print_invalid_query_pointer= false;
129 #endif
130 
131   if (segfaulted)
132   {
133     my_safe_printf_stderr("Fatal " SIGNAL_FMT " while backtracing\n", sig);
134     goto end;
135   }
136   segfaulted = 1;
137   DBUG_PRINT("error", ("handling fatal signal"));
138 
139   curr_time= my_time(0);
140   localtime_r(&curr_time, &tm);
141 
142   my_safe_printf_stderr("%02d%02d%02d %2d:%02d:%02d ",
143                         tm.tm_year % 100, tm.tm_mon+1, tm.tm_mday,
144                         tm.tm_hour, tm.tm_min, tm.tm_sec);
145   if (opt_expect_abort
146 #ifdef _WIN32
147     && sig == (int)EXCEPTION_BREAKPOINT /* __debugbreak in my_sigabrt_hander() */
148 #else
149     && sig == SIGABRT
150 #endif
151     )
152   {
153     fprintf(stderr,"[Note] mysqld did an expected abort\n");
154     goto end;
155   }
156 
157   my_safe_printf_stderr("[ERROR] mysqld got " SIGNAL_FMT " ;\n",sig);
158 
159   my_safe_printf_stderr("%s",
160     "This could be because you hit a bug. It is also possible that this binary\n"
161     "or one of the libraries it was linked against is corrupt, improperly built,\n"
162     "or misconfigured. This error can also be caused by malfunctioning hardware.\n\n");
163 
164   my_safe_printf_stderr("%s",
165                         "To report this bug, see https://mariadb.com/kb/en/reporting-bugs\n\n");
166 
167   my_safe_printf_stderr("%s",
168     "We will try our best to scrape up some info that will hopefully help\n"
169     "diagnose the problem, but since we have already crashed, \n"
170     "something is definitely wrong and this may fail.\n\n");
171 
172   set_server_version(server_version, sizeof(server_version));
173   my_safe_printf_stderr("Server version: %s\n", server_version);
174 
175   if (dflt_key_cache)
176     my_safe_printf_stderr("key_buffer_size=%zu\n",
177                           dflt_key_cache->key_cache_mem_size);
178 
179   my_safe_printf_stderr("read_buffer_size=%lu\n",
180                         global_system_variables.read_buff_size);
181 
182   my_safe_printf_stderr("max_used_connections=%lu\n",
183                         max_used_connections);
184 
185   if (thread_scheduler)
186     my_safe_printf_stderr("max_threads=%lu\n",
187                           thread_scheduler->max_threads +
188                           extra_max_connections);
189 
190   my_safe_printf_stderr("thread_count=%u\n", THD_count::value());
191 
192   if (dflt_key_cache && thread_scheduler)
193   {
194     size_t used_mem=
195         (dflt_key_cache->key_cache_mem_size +
196          (global_system_variables.read_buff_size +
197           (size_t) global_system_variables.sortbuff_size) *
198              (thread_scheduler->max_threads + extra_max_connections) +
199          (max_connections + extra_max_connections) * sizeof(THD)) / 1024;
200 
201     my_safe_printf_stderr("It is possible that mysqld could use up to \n"
202                           "key_buffer_size + "
203                           "(read_buffer_size + sort_buffer_size)*max_threads = "
204                           "%zu K  bytes of memory\n", used_mem);
205 
206     my_safe_printf_stderr("%s",
207                           "Hope that's ok; if not, decrease some variables in "
208                           "the equation.\n\n");
209   }
210 
211 #ifdef HAVE_STACKTRACE
212   thd= current_thd;
213 
214   if (opt_stack_trace)
215   {
216     my_safe_printf_stderr("Thread pointer: %p\n", thd);
217     my_safe_printf_stderr("%s",
218       "Attempting backtrace. You can use the following "
219       "information to find out\n"
220       "where mysqld died. If you see no messages after this, something went\n"
221       "terribly wrong...\n");
222     my_print_stacktrace(thd ? (uchar*) thd->thread_stack : NULL,
223                         (ulong)my_thread_stack_size, 0);
224   }
225   if (thd)
226   {
227     const char *kreason= "UNKNOWN";
228     switch (thd->killed) {
229     case NOT_KILLED:
230     case KILL_HARD_BIT:
231       kreason= "NOT_KILLED";
232       break;
233     case KILL_BAD_DATA:
234     case KILL_BAD_DATA_HARD:
235       kreason= "KILL_BAD_DATA";
236       break;
237     case KILL_CONNECTION:
238     case KILL_CONNECTION_HARD:
239       kreason= "KILL_CONNECTION";
240       break;
241     case KILL_QUERY:
242     case KILL_QUERY_HARD:
243       kreason= "KILL_QUERY";
244       break;
245     case KILL_TIMEOUT:
246     case KILL_TIMEOUT_HARD:
247       kreason= "KILL_TIMEOUT";
248       break;
249     case KILL_SYSTEM_THREAD:
250     case KILL_SYSTEM_THREAD_HARD:
251       kreason= "KILL_SYSTEM_THREAD";
252       break;
253     case KILL_SERVER:
254     case KILL_SERVER_HARD:
255       kreason= "KILL_SERVER";
256       break;
257     case ABORT_QUERY:
258     case ABORT_QUERY_HARD:
259       kreason= "ABORT_QUERY";
260       break;
261     case KILL_SLAVE_SAME_ID:
262       kreason= "KILL_SLAVE_SAME_ID";
263       break;
264     case KILL_WAIT_TIMEOUT:
265     case KILL_WAIT_TIMEOUT_HARD:
266       kreason= "KILL_WAIT_TIMEOUT";
267       break;
268     }
269     my_safe_printf_stderr("%s", "\n"
270       "Trying to get some variables.\n"
271       "Some pointers may be invalid and cause the dump to abort.\n");
272 
273     my_safe_printf_stderr("Query (%p): ", thd->query());
274     if (my_safe_print_str(thd->query(), MY_MIN(65536U, thd->query_length())))
275     {
276       // Query was found invalid. We will try to print it at the end.
277       print_invalid_query_pointer= true;
278     }
279 
280     my_safe_printf_stderr("\nConnection ID (thread ID): %lu\n",
281                           (ulong) thd->thread_id);
282     my_safe_printf_stderr("Status: %s\n\n", kreason);
283     my_safe_printf_stderr("%s", "Optimizer switch: ");
284     ulonglong optsw= thd->variables.optimizer_switch;
285     for (uint i= 0; optimizer_switch_names[i+1]; i++, optsw >>= 1)
286     {
287       if (i)
288         my_safe_printf_stderr("%s", ",");
289       my_safe_printf_stderr("%s=%s",
290               optimizer_switch_names[i], optsw & 1 ? "on" : "off");
291     }
292     my_safe_printf_stderr("%s", "\n\n");
293   }
294   my_safe_printf_stderr("%s",
295     "The manual page at "
296     "https://mariadb.com/kb/en/how-to-produce-a-full-stack-trace-for-mysqld/ contains\n"
297     "information that should help you find out what is causing the crash.\n");
298 
299 #endif /* HAVE_STACKTRACE */
300 
301 #ifdef HAVE_INITGROUPS
302   if (calling_initgroups)
303   {
304     my_safe_printf_stderr("%s", "\n"
305       "This crash occurred while the server was calling initgroups(). This is\n"
306       "often due to the use of a mysqld that is statically linked against \n"
307       "glibc and configured to use LDAP in /etc/nsswitch.conf.\n"
308       "You will need to either upgrade to a version of glibc that does not\n"
309       "have this problem (2.3.4 or later when used with nscd),\n"
310       "disable LDAP in your nsswitch.conf, or use a "
311       "mysqld that is not statically linked.\n");
312   }
313 #endif
314 
315   if (locked_in_memory)
316   {
317     my_safe_printf_stderr("%s", "\n"
318       "The \"--memlock\" argument, which was enabled, "
319       "uses system calls that are\n"
320       "unreliable and unstable on some operating systems and "
321       "operating-system versions (notably, some versions of Linux).\n"
322       "This crash could be due to use of those buggy OS calls.\n"
323       "You should consider whether you really need the "
324       "\"--memlock\" parameter and/or consult the OS distributer about "
325       "\"mlockall\" bugs.\n");
326   }
327 
328 #ifdef HAVE_STACKTRACE
329   if (print_invalid_query_pointer)
330   {
331     my_safe_printf_stderr(
332         "\nWe think the query pointer is invalid, but we will try "
333         "to print it anyway. \n"
334         "Query: ");
335     my_write_stderr(thd->query(), MY_MIN(65536U, thd->query_length()));
336     my_safe_printf_stderr("\n\n");
337   }
338 #endif
339 
340   output_core_info();
341 #ifdef HAVE_WRITE_CORE
342   if (test_flags & TEST_CORE_ON_SIGNAL)
343   {
344     my_write_core(sig);
345   }
346 #endif
347 
348 end:
349 #ifndef __WIN__
350   /*
351      Quit, without running destructors (etc.)
352      Use a signal, because the parent (systemd) can check that with WIFSIGNALED
353      On Windows, do not terminate, but pass control to exception filter.
354   */
355   signal(sig, SIG_DFL);
356   kill(getpid(), sig);
357 #else
358   return;
359 #endif
360 }
361