diff options
author | Tor Didriksen <tor.didriksen@oracle.com> | 2011-11-30 15:39:29 +0100 |
---|---|---|
committer | Tor Didriksen <tor.didriksen@oracle.com> | 2011-11-30 15:39:29 +0100 |
commit | 9a15f2492b1bccfa085a19c252ed5224c1fde3b2 (patch) | |
tree | 96463acbe03a8468be26d0cc712065897cb8ac97 /sql | |
parent | 98adda50958504db0439ad6c72c96d663fb99bec (diff) | |
download | mariadb-git-9a15f2492b1bccfa085a19c252ed5224c1fde3b2.tar.gz |
Bug#11761576 54082: HANDLE_SEGFAULT MAKES USE OF UNSAFE FUNCTIONS
handle_segfault is the signal handler code of mysqld. however, it makes
calls to potentially unsafe functions localtime_r, fprintf, fflush.
include/my_stacktrace.h:
Add safe versions of itoa() write() and snprintf().
libmysqld/CMakeLists.txt:
Move signal handler to separate file.
mysys/stacktrace.c:
Remove unsafe function calls.
sql/CMakeLists.txt:
Move signal handler to separate file.
sql/Makefile.am:
Move signal handler to separate file.
sql/mysqld.cc:
Move signal handler to separate file.
sql/signal_handler.cc:
Remove unsafe function calls.
Diffstat (limited to 'sql')
-rwxr-xr-x | sql/CMakeLists.txt | 3 | ||||
-rw-r--r-- | sql/Makefile.am | 1 | ||||
-rw-r--r-- | sql/mysqld.cc | 190 | ||||
-rw-r--r-- | sql/signal_handler.cc | 257 |
4 files changed, 278 insertions, 173 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index c050b329787..8f6e9cc7b32 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -65,6 +65,7 @@ SET (SQL_SOURCE sql_list.cc sql_load.cc sql_manager.cc sql_map.cc sql_parse.cc sql_partition.cc sql_plugin.cc sql_prepare.cc sql_rename.cc debug_sync.cc debug_sync.h + signal_handler.cc sql_repl.cc sql_select.cc sql_show.cc sql_state.c sql_string.cc sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc @@ -113,7 +114,7 @@ IF(MSVC AND NOT WITHOUT_DYNAMIC_PLUGINS) ADD_CUSTOM_COMMAND(TARGET mysqld PRE_LINK COMMAND cscript ARGS //nologo ${PROJECT_SOURCE_DIR}/win/create_def_file.js ${PLATFORM} ${LIB_LOCATIONS} > mysqld.def - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/sql) + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) ENDIF(MSVC AND NOT WITHOUT_DYNAMIC_PLUGINS) ADD_DEPENDENCIES(sql GenError) diff --git a/sql/Makefile.am b/sql/Makefile.am index 6040e4d498d..829523c7ba3 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -103,6 +103,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ records.cc filesort.cc handler.cc \ ha_partition.cc \ debug_sync.cc \ + signal_handler.cc \ sql_db.cc sql_table.cc sql_rename.cc sql_crypt.cc \ sql_load.cc mf_iocache.cc field_conv.cc sql_show.cc \ sql_udf.cc sql_analyse.cc sql_analyse.h sql_cache.cc \ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 37eb6d95ce8..73b107e4746 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -122,13 +122,6 @@ extern "C" { // Because of SCO 3.2V4.2 #include <sys/mman.h> #endif -#ifdef __WIN__ -#include <crtdbg.h> -#define SIGNAL_FMT "exception 0x%x" -#else -#define SIGNAL_FMT "signal %d" -#endif - #ifdef __NETWARE__ #define zVOLSTATE_ACTIVE 6 #define zVOLSTATE_DEACTIVE 2 @@ -269,7 +262,7 @@ inline void setup_fpu() extern "C" int gethostname(char *name, int namelen); #endif -extern "C" sig_handler handle_segfault(int sig); +extern "C" sig_handler handle_fatal_signal(int sig); #if defined(__linux__) #define ENABLE_TEMP_POOL 1 @@ -415,6 +408,10 @@ TYPELIB log_output_typelib= {array_elements(log_output_names)-1,"", /* static variables */ +#ifdef HAVE_NPTL +volatile sig_atomic_t ld_assume_kernel_is_set= 0; +#endif + /* the default log output is log tables */ static bool lower_case_table_names_used= 0; static bool max_long_data_size_used= false; @@ -424,7 +421,7 @@ static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0; static my_bool opt_short_log_format= 0; static uint kill_cached_threads, wake_thread; static ulong killed_threads, thread_created; -static ulong max_used_connections; + ulong max_used_connections; static ulong my_bind_addr; /**< the address we bind to */ static volatile ulong cached_thread_count= 0; static const char *sql_mode_str= "OFF"; @@ -553,7 +550,7 @@ TYPELIB binlog_format_typelib= ulong opt_binlog_format_id= (ulong) BINLOG_FORMAT_UNSPEC; const char *opt_binlog_format= binlog_format_names[opt_binlog_format_id]; #ifdef HAVE_INITGROUPS -static bool calling_initgroups= FALSE; /**< Used in SIGSEGV handler. */ +volatile sig_atomic_t calling_initgroups= 0; /**< Used in SIGSEGV handler. */ #endif uint mysqld_port, test_flags, select_errors, dropping_tables, ha_open_options; uint mysqld_port_timeout; @@ -727,7 +724,7 @@ char *opt_logname, *opt_slow_logname; /* Static variables */ -static bool kill_in_progress, segfaulted; +static volatile sig_atomic_t kill_in_progress; #ifdef HAVE_STACK_TRACE_ON_SEGV static my_bool opt_do_pstack; #endif /* HAVE_STACK_TRACE_ON_SEGV */ @@ -1616,9 +1613,9 @@ static void set_user(const char *user, struct passwd *user_info_arg) calling_initgroups as a flag to the SIGSEGV handler that is then used to output a specific message to help the user resolve this problem. */ - calling_initgroups= TRUE; + calling_initgroups= 1; initgroups((char*) user, user_info_arg->pw_gid); - calling_initgroups= FALSE; + calling_initgroups= 0; #endif if (setgid(user_info_arg->pw_gid) == -1) { @@ -2170,7 +2167,7 @@ LONG WINAPI my_unhandler_exception_filter(EXCEPTION_POINTERS *ex_pointers) __try { my_set_exception_pointers(ex_pointers); - handle_segfault(ex_pointers->ExceptionRecord->ExceptionCode); + handle_fatal_signal(ex_pointers->ExceptionRecord->ExceptionCode); } __except(EXCEPTION_EXECUTE_HANDLER) { @@ -2481,161 +2478,6 @@ extern "C" char *my_demangle(const char *mangled_name, int *status) #endif -extern "C" sig_handler handle_segfault(int sig) -{ - time_t curr_time; - struct tm tm; - - /* - Strictly speaking, one needs a mutex here - but since we have got SIGSEGV already, things are a mess - so not having the mutex is not as bad as possibly using a buggy - mutex - so we keep things simple - */ - if (segfaulted) - { - fprintf(stderr, "Fatal " SIGNAL_FMT " while backtracing\n", sig); - exit(1); - } - - segfaulted = 1; - - curr_time= my_time(0); - localtime_r(&curr_time, &tm); - - fprintf(stderr,"\ -%02d%02d%02d %2d:%02d:%02d - mysqld got " SIGNAL_FMT " ;\n\ -This could be because you hit a bug. It is also possible that this binary\n\ -or one of the libraries it was linked against is corrupt, improperly built,\n\ -or misconfigured. This error can also be caused by malfunctioning hardware.\n", - tm.tm_year % 100, tm.tm_mon+1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec, - sig); - fprintf(stderr, "\ -We will try our best to scrape up some info that will hopefully help diagnose\n\ -the problem, but since we have already crashed, something is definitely wrong\n\ -and this may fail.\n\n"); - fprintf(stderr, "key_buffer_size=%lu\n", - (ulong) dflt_key_cache->key_cache_mem_size); - fprintf(stderr, "read_buffer_size=%ld\n", (long) global_system_variables.read_buff_size); - fprintf(stderr, "max_used_connections=%lu\n", max_used_connections); - fprintf(stderr, "max_threads=%u\n", thread_scheduler.max_threads); - fprintf(stderr, "threads_connected=%u\n", thread_count); - fprintf(stderr, "It is possible that mysqld could use up to \n\ -key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = %lu K\n\ -bytes of memory\n", ((ulong) dflt_key_cache->key_cache_mem_size + - (global_system_variables.read_buff_size + - global_system_variables.sortbuff_size) * - thread_scheduler.max_threads + - max_connections * sizeof(THD)) / 1024); - fprintf(stderr, "Hope that's ok; if not, decrease some variables in the equation.\n\n"); - -#if defined(HAVE_LINUXTHREADS) - if (sizeof(char*) == 4 && thread_count > UNSAFE_DEFAULT_LINUX_THREADS) - { - fprintf(stderr, "\ -You seem to be running 32-bit Linux and have %d concurrent connections.\n\ -If you have not changed STACK_SIZE in LinuxThreads and built the binary \n\ -yourself, LinuxThreads is quite likely to steal a part of the global heap for\n\ -the thread stack. Please read http://dev.mysql.com/doc/mysql/en/linux.html\n\n", - thread_count); - } -#endif /* HAVE_LINUXTHREADS */ - -#ifdef HAVE_STACKTRACE - THD *thd=current_thd; - - if (!(test_flags & TEST_NO_STACKTRACE)) - { - fprintf(stderr, "Thread pointer: 0x%lx\n", (long) thd); - fprintf(stderr, "Attempting backtrace. You can use the following " - "information to find out\nwhere mysqld died. If " - "you see no messages after this, something went\n" - "terribly wrong...\n"); - my_print_stacktrace(thd ? (uchar*) thd->thread_stack : NULL, - my_thread_stack_size); - } - if (thd) - { - const char *kreason= "UNKNOWN"; - switch (thd->killed) { - case THD::NOT_KILLED: - kreason= "NOT_KILLED"; - break; - case THD::KILL_BAD_DATA: - kreason= "KILL_BAD_DATA"; - break; - case THD::KILL_CONNECTION: - kreason= "KILL_CONNECTION"; - break; - case THD::KILL_QUERY: - kreason= "KILL_QUERY"; - break; - case THD::KILLED_NO_VALUE: - kreason= "KILLED_NO_VALUE"; - break; - } - fprintf(stderr, "\nTrying to get some variables.\n" - "Some pointers may be invalid and cause the dump to abort.\n"); - fprintf(stderr, "Query (%p): ", thd->query()); - my_safe_print_str(thd->query(), min(1024, thd->query_length())); - fprintf(stderr, "Connection ID (thread ID): %lu\n", (ulong) thd->thread_id); - fprintf(stderr, "Status: %s\n", kreason); - fputc('\n', stderr); - } - fprintf(stderr, "\ -The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html contains\n\ -information that should help you find out what is causing the crash.\n"); - fflush(stderr); -#endif /* HAVE_STACKTRACE */ - -#ifdef HAVE_INITGROUPS - if (calling_initgroups) - fprintf(stderr, "\n\ -This crash occured while the server was calling initgroups(). This is\n\ -often due to the use of a mysqld that is statically linked against glibc\n\ -and configured to use LDAP in /etc/nsswitch.conf. You will need to either\n\ -upgrade to a version of glibc that does not have this problem (2.3.4 or\n\ -later when used with nscd), disable LDAP in your nsswitch.conf, or use a\n\ -mysqld that is not statically linked.\n"); -#endif - -#ifdef HAVE_NPTL - if (thd_lib_detected == THD_LIB_LT && !getenv("LD_ASSUME_KERNEL")) - fprintf(stderr,"\n\ -You are running a statically-linked LinuxThreads binary on an NPTL system.\n\ -This can result in crashes on some distributions due to LT/NPTL conflicts.\n\ -You should either build a dynamically-linked binary, or force LinuxThreads\n\ -to be used with the LD_ASSUME_KERNEL environment variable. Please consult\n\ -the documentation for your distribution on how to do that.\n"); -#endif - - if (locked_in_memory) - { - fprintf(stderr, "\n\ -The \"--memlock\" argument, which was enabled, uses system calls that are\n\ -unreliable and unstable on some operating systems and operating-system\n\ -versions (notably, some versions of Linux). This crash could be due to use\n\ -of those buggy OS calls. You should consider whether you really need the\n\ -\"--memlock\" parameter and/or consult the OS distributer about \"mlockall\"\n\ -bugs.\n"); - } - -#ifdef HAVE_WRITE_CORE - if (test_flags & TEST_CORE_ON_SIGNAL) - { - fprintf(stderr, "Writing a core file\n"); - fflush(stderr); - my_write_core(sig); - } -#endif - -#ifndef __WIN__ - /* On Windows, do not terminate, but pass control to exception filter */ - exit(1); -#endif -} - #if !defined(__WIN__) && !defined(__NETWARE__) #ifndef SA_RESETHAND #define SA_RESETHAND 0 @@ -2664,9 +2506,9 @@ static void init_signals(void) my_init_stacktrace(); #endif #if defined(__amiga__) - sa.sa_handler=(void(*)())handle_segfault; + sa.sa_handler=(void(*)())handle_fatal_signal; #else - sa.sa_handler=handle_segfault; + sa.sa_handler=handle_fatal_signal; #endif sigaction(SIGSEGV, &sa, NULL); sigaction(SIGABRT, &sa, NULL); @@ -4363,6 +4205,10 @@ int win_main(int argc, char **argv) int main(int argc, char **argv) #endif { +#ifdef HAVE_NPTL + ld_assume_kernel_is_set= (getenv("LD_ASSUME_KERNEL") != 0); +#endif + MY_INIT(argv[0]); // init my_sys library & pthreads /* nothing should come before this line ^^^ */ @@ -7876,7 +7722,7 @@ static int mysql_init_variables(void) opt_secure_file_priv= 0; opt_bootstrap= opt_myisam_log= 0; mqh_used= 0; - segfaulted= kill_in_progress= 0; + kill_in_progress= 0; cleanup_done= 0; defaults_argc= 0; defaults_argv= 0; diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc new file mode 100644 index 00000000000..54b456ec2c4 --- /dev/null +++ b/sql/signal_handler.cc @@ -0,0 +1,257 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */ + +#include "my_global.h" +#include <signal.h> + +#include "mysql_priv.h" +#include "my_stacktrace.h" + +#ifdef __WIN__ +#include <crtdbg.h> +#define SIGNAL_FMT "exception 0x%x" +#else +#define SIGNAL_FMT "signal %d" +#endif + +/* + We are handling signals in this file. + Any global variables we read should be 'volatile sig_atomic_t' + to guarantee that we read some consistent value. + */ +static volatile sig_atomic_t segfaulted= 0; +extern ulong max_used_connections; +extern volatile sig_atomic_t calling_initgroups; +#ifdef HAVE_NPTL +extern volatile sig_atomic_t ld_assume_kernel_is_set; +#endif + +/** + * Handler for fatal signals + * + * Fatal events (seg.fault, bus error etc.) will trigger + * this signal handler. The handler will try to dump relevant + * debugging information to stderr and dump a core image. + * + * Signal handlers can only use a set of 'safe' system calls + * and library functions. A list of safe calls in POSIX systems + * are available at: + * http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html + * For MS Windows, guidelines are available at: + * http://msdn.microsoft.com/en-us/library/xdkz3x12(v=vs.71).aspx + * + * @param sig Signal number +*/ +extern "C" sig_handler handle_fatal_signal(int sig) +{ + if (segfaulted) + { + my_safe_printf_stderr("Fatal " SIGNAL_FMT " while backtracing\n", sig); + _exit(1); /* Quit without running destructors */ + } + + segfaulted = 1; + +#ifdef __WIN__ + SYSTEMTIME utc_time; + GetSystemTime(&utc_time); + const long hrs= utc_time.wHour; + const long mins= utc_time.wMinute; + const long secs= utc_time.wSecond; +#else + /* Using time() instead of my_time() to avoid looping */ + const time_t curr_time= time(NULL); + /* Calculate time of day */ + const long tmins = curr_time / 60; + const long thrs = tmins / 60; + const long hrs = thrs % 24; + const long mins = tmins % 60; + const long secs = curr_time % 60; +#endif + + char hrs_buf[3]= "00"; + char mins_buf[3]= "00"; + char secs_buf[3]= "00"; + my_safe_itoa(10, hrs, &hrs_buf[2]); + my_safe_itoa(10, mins, &mins_buf[2]); + my_safe_itoa(10, secs, &secs_buf[2]); + + my_safe_printf_stderr("%s:%s:%s UTC - mysqld got " SIGNAL_FMT " ;\n", + hrs_buf, mins_buf, secs_buf, sig); + + my_safe_printf_stderr("%s", + "This could be because you hit a bug. It is also possible that this binary\n" + "or one of the libraries it was linked against is corrupt, improperly built,\n" + "or misconfigured. This error can also be caused by malfunctioning hardware.\n"); + + my_safe_printf_stderr("%s", + "We will try our best to scrape up some info that will hopefully help\n" + "diagnose the problem, but since we have already crashed, \n" + "something is definitely wrong and this may fail.\n\n"); + + my_safe_printf_stderr("key_buffer_size=%lu\n", + (ulong) dflt_key_cache->key_cache_mem_size); + + my_safe_printf_stderr("read_buffer_size=%ld\n", + (long) global_system_variables.read_buff_size); + + my_safe_printf_stderr("max_used_connections=%lu\n", + (ulong) max_used_connections); + + my_safe_printf_stderr("max_threads=%u\n", + (uint) thread_scheduler.max_threads); + + my_safe_printf_stderr("thread_count=%u\n", (uint) thread_count); + + my_safe_printf_stderr("connection_count=%u\n", (uint) connection_count); + + my_safe_printf_stderr("It is possible that mysqld could use up to \n" + "key_buffer_size + " + "(read_buffer_size + sort_buffer_size)*max_threads = " + "%lu K bytes of memory\n", + ((ulong) dflt_key_cache->key_cache_mem_size + + (global_system_variables.read_buff_size + + global_system_variables.sortbuff_size) * + thread_scheduler.max_threads + + max_connections * sizeof(THD)) / 1024); + + my_safe_printf_stderr("%s", + "Hope that's ok; if not, decrease some variables in the equation.\n\n"); + +#if defined(HAVE_LINUXTHREADS) + if (sizeof(char*) == 4 && thread_count > UNSAFE_DEFAULT_LINUX_THREADS) + { + my_safe_printf_stderr( + "You seem to be running 32-bit Linux and have " + "%d concurrent connections.\n" + "If you have not changed STACK_SIZE in LinuxThreads " + "and built the binary \n" + "yourself, LinuxThreads is quite likely to steal " + "a part of the global heap for\n" + "the thread stack. Please read " + "http://dev.mysql.com/doc/mysql/en/linux-installation.html\n\n" + thread_count); + } +#endif /* HAVE_LINUXTHREADS */ + +#ifdef HAVE_STACKTRACE + THD *thd=current_thd; + + if (!(test_flags & TEST_NO_STACKTRACE)) + { + my_safe_printf_stderr("Thread pointer: 0x%p\n", thd); + my_safe_printf_stderr("%s", + "Attempting backtrace. You can use the following " + "information to find out\n" + "where mysqld died. If you see no messages after this, something went\n" + "terribly wrong...\n"); + my_print_stacktrace(thd ? (uchar*) thd->thread_stack : NULL, + my_thread_stack_size); + } + if (thd) + { + const char *kreason= "UNKNOWN"; + switch (thd->killed) { + case THD::NOT_KILLED: + kreason= "NOT_KILLED"; + break; + case THD::KILL_BAD_DATA: + kreason= "KILL_BAD_DATA"; + break; + case THD::KILL_CONNECTION: + kreason= "KILL_CONNECTION"; + break; + case THD::KILL_QUERY: + kreason= "KILL_QUERY"; + break; + case THD::KILLED_NO_VALUE: + kreason= "KILLED_NO_VALUE"; + break; + } + my_safe_printf_stderr("%s", "\n" + "Trying to get some variables.\n" + "Some pointers may be invalid and cause the dump to abort.\n"); + + my_safe_printf_stderr("Query (%p): ", thd->query()); + my_safe_print_str(thd->query(), min(1024U, thd->query_length())); + my_safe_printf_stderr("Connection ID (thread ID): %lu\n", + (ulong) thd->thread_id); + my_safe_printf_stderr("Status: %s\n\n", kreason); + } + my_safe_printf_stderr("%s", + "The manual page at " + "http://dev.mysql.com/doc/mysql/en/crashing.html contains\n" + "information that should help you find out what is causing the crash.\n"); + +#endif /* HAVE_STACKTRACE */ + +#ifdef HAVE_INITGROUPS + if (calling_initgroups) + { + my_safe_printf_stderr("%s", "\n" + "This crash occured while the server was calling initgroups(). This is\n" + "often due to the use of a mysqld that is statically linked against \n" + "glibc and configured to use LDAP in /etc/nsswitch.conf.\n" + "You will need to either upgrade to a version of glibc that does not\n" + "have this problem (2.3.4 or later when used with nscd),\n" + "disable LDAP in your nsswitch.conf, or use a " + "mysqld that is not statically linked.\n"); + } +#endif + +#ifdef HAVE_NPTL + if (thd_lib_detected == THD_LIB_LT && !ld_assume_kernel_is_set) + { + my_safe_printf_stderr("%s", + "You are running a statically-linked LinuxThreads binary on an NPTL\n" + "system. This can result in crashes on some distributions due to " + "LT/NPTL conflicts.\n" + "You should either build a dynamically-linked binary, " + "or force LinuxThreads\n" + "to be used with the LD_ASSUME_KERNEL environment variable.\n" + "Please consult the documentation for your distribution " + "on how to do that.\n"); + } +#endif + + if (locked_in_memory) + { + my_safe_printf_stderr("%s", "\n" + "The \"--memlock\" argument, which was enabled, " + "uses system calls that are\n" + "unreliable and unstable on some operating systems and " + "operating-system versions (notably, some versions of Linux).\n" + "This crash could be due to use of those buggy OS calls.\n" + "You should consider whether you really need the " + "\"--memlock\" parameter and/or consult the OS distributer about " + "\"mlockall\" bugs.\n"); + } + +#ifdef HAVE_WRITE_CORE + if (test_flags & TEST_CORE_ON_SIGNAL) + { + my_safe_printf_stderr("%s", "Writing a core file\n"); + my_write_core(sig); + } +#endif + +#ifndef __WIN__ + /* + Quit, without running destructors (etc.) + On Windows, do not terminate, but pass control to exception filter. + */ + _exit(1); // Using _exit(), since exit() is not async signal safe +#endif +} |