diff options
author | Tor Didriksen <tor.didriksen@oracle.com> | 2011-12-02 14:16:48 +0100 |
---|---|---|
committer | Tor Didriksen <tor.didriksen@oracle.com> | 2011-12-02 14:16:48 +0100 |
commit | b522a6ce78bfe7bea3e52715c993c7d190159139 (patch) | |
tree | 9e4c9211a159f15e7bd8ab5ca489f53fd8b28a7c /sql | |
parent | 8e6d41e278de3264d2a6e13cbc0ed328afca7f40 (diff) | |
download | mariadb-git-b522a6ce78bfe7bea3e52715c993c7d190159139.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/mysqld.cc:
Move signal handler to separate file.
sql/set_var.h:
Add missing #include dependency.
sql/sys_vars.cc:
Cleanup .h and .cc files.
sql/sys_vars.h:
Cleanup .h and .cc files.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/CMakeLists.txt | 1 | ||||
-rw-r--r-- | sql/mysqld.cc | 187 | ||||
-rw-r--r-- | sql/set_var.h | 1 | ||||
-rw-r--r-- | sql/signal_handler.cc | 6 | ||||
-rw-r--r-- | sql/sys_vars.cc | 73 | ||||
-rw-r--r-- | sql/sys_vars.h | 71 |
6 files changed, 97 insertions, 242 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 2e02e67c2c9..09672561c11 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -41,6 +41,7 @@ SET (SQL_SOURCE ../sql-common/client.c derror.cc des_key_file.cc discover.cc ../libmysql/errmsg.c field.cc field_conv.cc filesort.cc gstream.cc sha2.cc + signal_handler.cc handler.cc hash_filo.h sql_plugin_services.h hostname.cc init.cc item.cc item_buff.cc item_cmpfunc.cc item_create.cc item_func.cc item_geofunc.cc item_row.cc diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 285b10154ce..7363128ec04 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -144,9 +144,6 @@ extern "C" { // Because of SCO 3.2V4.2 #ifdef __WIN__ #include <crtdbg.h> -#define SIGNAL_FMT "exception 0x%x" -#else -#define SIGNAL_FMT "signal %d" #endif #ifdef HAVE_SOLARIS_LARGE_PAGES @@ -256,7 +253,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 @@ -323,6 +320,10 @@ static PSI_rwlock_key key_rwlock_openssl; #endif #endif /* HAVE_PSI_INTERFACE */ +#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; @@ -333,7 +334,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; -static ulong max_used_connections; + ulong max_used_connections; static volatile ulong cached_thread_count= 0; static char *mysqld_user, *mysqld_chroot; static char *default_character_set_name; @@ -442,7 +443,7 @@ my_bool sp_automatic_privileges= 1; ulong opt_binlog_rows_event_max_size; const char *binlog_format_names[]= {"MIXED", "STATEMENT", "ROW", NullS}; #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; @@ -656,7 +657,9 @@ char *opt_logname, *opt_slow_logname, *opt_bin_logname; /* Static variables */ -static bool kill_in_progress, segfaulted; +static volatile sig_atomic_t kill_in_progress; + + static my_bool opt_bootstrap, opt_myisam_log; static int cleanup_done; static ulong opt_specialflag; @@ -1703,9 +1706,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) { @@ -2335,7 +2338,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) { @@ -2409,161 +2412,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, "thread_count=%u\n", thread_count); - fprintf(stderr, "connection_count=%u\n", connection_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__) #ifndef SA_RESETHAND @@ -2593,9 +2441,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); @@ -4282,6 +4130,9 @@ int mysqld_main(int argc, char **argv) to be able to read defaults files and parse options. */ my_progname= argv[0]; +#ifdef HAVE_NPTL + ld_assume_kernel_is_set= (getenv("LD_ASSUME_KERNEL") != 0); +#endif #ifndef _WIN32 // For windows, my_init() is called from the win specific mysqld_main if (my_init()) // init my_sys library & pthreads @@ -6778,7 +6629,7 @@ static int mysql_init_variables(void) opt_secure_auth= 0; opt_bootstrap= opt_myisam_log= 0; mqh_used= 0; - segfaulted= kill_in_progress= 0; + kill_in_progress= 0; cleanup_done= 0; server_id_supplied= 0; test_flags= select_errors= dropping_tables= ha_open_options=0; diff --git a/sql/set_var.h b/sql/set_var.h index 52679aa636c..990112362f8 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -34,6 +34,7 @@ class Item_func_set_user_var; // This include needs to be here since item.h requires enum_var_type :-P #include "item.h" /* Item */ +#include "sql_class.h" /* THD */ extern TYPELIB bool_typelib; diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc index 54b456ec2c4..27fcf741e2a 100644 --- a/sql/signal_handler.cc +++ b/sql/signal_handler.cc @@ -16,7 +16,7 @@ #include "my_global.h" #include <signal.h> -#include "mysql_priv.h" +#include "sys_vars.h" #include "my_stacktrace.h" #ifdef __WIN__ @@ -111,7 +111,7 @@ extern "C" sig_handler handle_fatal_signal(int sig) (ulong) max_used_connections); my_safe_printf_stderr("max_threads=%u\n", - (uint) thread_scheduler.max_threads); + (uint) thread_scheduler->max_threads); my_safe_printf_stderr("thread_count=%u\n", (uint) thread_count); @@ -124,7 +124,7 @@ extern "C" sig_handler handle_fatal_signal(int sig) ((ulong) dflt_key_cache->key_cache_mem_size + (global_system_variables.read_buff_size + global_system_variables.sortbuff_size) * - thread_scheduler.max_threads + + thread_scheduler->max_threads + max_connections * sizeof(THD)) / 1024); my_safe_printf_stderr("%s", diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 7a04015dc93..65cc4d4c4ab 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -49,6 +49,8 @@ #include "../storage/perfschema/pfs_server.h" #endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */ +TYPELIB bool_typelib={ array_elements(bool_values)-1, "", bool_values, 0 }; + /* This forward declaration is needed because including sql_base.h causes further includes. [TODO] Eliminate this forward declaration @@ -56,6 +58,77 @@ */ extern void close_thread_tables(THD *thd); + +static bool update_buffer_size(THD *thd, KEY_CACHE *key_cache, + ptrdiff_t offset, ulonglong new_value) +{ + bool error= false; + DBUG_ASSERT(offset == offsetof(KEY_CACHE, param_buff_size)); + + if (new_value == 0) + { + if (key_cache == dflt_key_cache) + { + my_error(ER_WARN_CANT_DROP_DEFAULT_KEYCACHE, MYF(0)); + return true; + } + + if (key_cache->key_cache_inited) // If initied + { + /* + Move tables using this key cache to the default key cache + and clear the old key cache. + */ + key_cache->in_init= 1; + mysql_mutex_unlock(&LOCK_global_system_variables); + key_cache->param_buff_size= 0; + ha_resize_key_cache(key_cache); + ha_change_key_cache(key_cache, dflt_key_cache); + /* + We don't delete the key cache as some running threads my still be in + the key cache code with a pointer to the deleted (empty) key cache + */ + mysql_mutex_lock(&LOCK_global_system_variables); + key_cache->in_init= 0; + } + return error; + } + + key_cache->param_buff_size= new_value; + + /* If key cache didn't exist initialize it, else resize it */ + key_cache->in_init= 1; + mysql_mutex_unlock(&LOCK_global_system_variables); + + if (!key_cache->key_cache_inited) + error= ha_init_key_cache(0, key_cache); + else + error= ha_resize_key_cache(key_cache); + + mysql_mutex_lock(&LOCK_global_system_variables); + key_cache->in_init= 0; + + return error; +} + +static bool update_keycache_param(THD *thd, KEY_CACHE *key_cache, + ptrdiff_t offset, ulonglong new_value) +{ + bool error= false; + DBUG_ASSERT(offset != offsetof(KEY_CACHE, param_buff_size)); + + keycache_var(key_cache, offset)= new_value; + + key_cache->in_init= 1; + mysql_mutex_unlock(&LOCK_global_system_variables); + error= ha_resize_key_cache(key_cache); + + mysql_mutex_lock(&LOCK_global_system_variables); + key_cache->in_init= 0; + + return error; +} + /* The rule for this file: everything should be 'static'. When a sys_var variable or a function from this file is - in very rare cases - needed diff --git a/sql/sys_vars.h b/sql/sys_vars.h index 14d99407f37..98ff5f9fa02 100644 --- a/sql/sys_vars.h +++ b/sql/sys_vars.h @@ -72,7 +72,6 @@ enum charset_enum {IN_SYSTEM_CHARSET, IN_FS_CHARSET}; static const char *bool_values[3]= {"OFF", "ON", 0}; -TYPELIB bool_typelib={ array_elements(bool_values)-1, "", bool_values, 0 }; /** A small wrapper class to pass getopt arguments as a pair @@ -710,76 +709,6 @@ public: } }; -static bool update_buffer_size(THD *thd, KEY_CACHE *key_cache, - ptrdiff_t offset, ulonglong new_value) -{ - bool error= false; - DBUG_ASSERT(offset == offsetof(KEY_CACHE, param_buff_size)); - - if (new_value == 0) - { - if (key_cache == dflt_key_cache) - { - my_error(ER_WARN_CANT_DROP_DEFAULT_KEYCACHE, MYF(0)); - return true; - } - - if (key_cache->key_cache_inited) // If initied - { - /* - Move tables using this key cache to the default key cache - and clear the old key cache. - */ - key_cache->in_init= 1; - mysql_mutex_unlock(&LOCK_global_system_variables); - key_cache->param_buff_size= 0; - ha_resize_key_cache(key_cache); - ha_change_key_cache(key_cache, dflt_key_cache); - /* - We don't delete the key cache as some running threads my still be in - the key cache code with a pointer to the deleted (empty) key cache - */ - mysql_mutex_lock(&LOCK_global_system_variables); - key_cache->in_init= 0; - } - return error; - } - - key_cache->param_buff_size= new_value; - - /* If key cache didn't exist initialize it, else resize it */ - key_cache->in_init= 1; - mysql_mutex_unlock(&LOCK_global_system_variables); - - if (!key_cache->key_cache_inited) - error= ha_init_key_cache(0, key_cache); - else - error= ha_resize_key_cache(key_cache); - - mysql_mutex_lock(&LOCK_global_system_variables); - key_cache->in_init= 0; - - return error; -} - -static bool update_keycache_param(THD *thd, KEY_CACHE *key_cache, - ptrdiff_t offset, ulonglong new_value) -{ - bool error= false; - DBUG_ASSERT(offset != offsetof(KEY_CACHE, param_buff_size)); - - keycache_var(key_cache, offset)= new_value; - - key_cache->in_init= 1; - mysql_mutex_unlock(&LOCK_global_system_variables); - error= ha_resize_key_cache(key_cache); - - mysql_mutex_lock(&LOCK_global_system_variables); - key_cache->in_init= 0; - - return error; -} - /** The class for floating point variables |