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.
This commit is contained in:
parent
8e6d41e278
commit
b522a6ce78
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
/* Copyright (c) 2001, 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
|
||||
@ -59,6 +59,69 @@ void my_set_exception_pointers(EXCEPTION_POINTERS *ep);
|
||||
void my_write_core(int sig);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Async-signal-safe utility functions used by signal handler routines.
|
||||
Declared here in order to unit-test them.
|
||||
These are not general-purpose, but tailored to the signal handling routines.
|
||||
*/
|
||||
/**
|
||||
Converts a longlong value to string.
|
||||
@param base 10 for decimal, 16 for hex values (0..9a..f)
|
||||
@param val The value to convert
|
||||
@param buf Assumed to point to the *end* of the buffer.
|
||||
@returns Pointer to the first character of the converted string.
|
||||
Negative values:
|
||||
for base-10 the return string will be prepended with '-'
|
||||
for base-16 the return string will contain 16 characters
|
||||
Implemented with simplicity, and async-signal-safety in mind.
|
||||
*/
|
||||
char *my_safe_itoa(int base, longlong val, char *buf);
|
||||
|
||||
/**
|
||||
Converts a ulonglong value to string.
|
||||
@param base 10 for decimal, 16 for hex values (0..9a..f)
|
||||
@param val The value to convert
|
||||
@param buf Assumed to point to the *end* of the buffer.
|
||||
@returns Pointer to the first character of the converted string.
|
||||
Implemented with simplicity, and async-signal-safety in mind.
|
||||
*/
|
||||
char *my_safe_utoa(int base, ulonglong val, char *buf);
|
||||
|
||||
/**
|
||||
A (very) limited version of snprintf.
|
||||
@param to Destination buffer.
|
||||
@param n Size of destination buffer.
|
||||
@param fmt printf() style format string.
|
||||
@returns Number of bytes written, including terminating '\0'
|
||||
Supports 'd' 'i' 'u' 'x' 'p' 's' conversion.
|
||||
Supports 'l' and 'll' modifiers for integral types.
|
||||
Does not support any width/precision.
|
||||
Implemented with simplicity, and async-signal-safety in mind.
|
||||
*/
|
||||
size_t my_safe_snprintf(char* to, size_t n, const char* fmt, ...)
|
||||
ATTRIBUTE_FORMAT(printf, 3, 4);
|
||||
|
||||
/**
|
||||
A (very) limited version of snprintf, which writes the result to STDERR.
|
||||
@sa my_safe_snprintf
|
||||
Implemented with simplicity, and async-signal-safety in mind.
|
||||
@note Has an internal buffer capacity of 512 bytes,
|
||||
which should suffice for our signal handling routines.
|
||||
*/
|
||||
size_t my_safe_printf_stderr(const char* fmt, ...)
|
||||
ATTRIBUTE_FORMAT(printf, 1, 2);
|
||||
|
||||
/**
|
||||
Writes up to count bytes from buffer to STDERR.
|
||||
Implemented with simplicity, and async-signal-safety in mind.
|
||||
@param buf Buffer containing data to be written.
|
||||
@param count Number of bytes to write.
|
||||
@returns Number of bytes written.
|
||||
*/
|
||||
size_t my_write_stderr(const void *buf, size_t count);
|
||||
|
||||
C_MODE_END
|
||||
|
||||
#endif /* _my_stacktrace_h_ */
|
||||
|
@ -45,6 +45,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
|
||||
../sql/password.c ../sql/discover.cc ../sql/derror.cc
|
||||
../sql/field.cc ../sql/field_conv.cc
|
||||
../sql/filesort.cc ../sql/gstream.cc
|
||||
../sql/signal_handler.cc
|
||||
../sql/handler.cc ../sql/hash_filo.cc ../sql/hostname.cc
|
||||
../sql/init.cc ../sql/item_buff.cc ../sql/item_cmpfunc.cc
|
||||
../sql/item.cc ../sql/item_create.cc ../sql/item_func.cc
|
||||
|
@ -52,10 +52,11 @@ void my_init_stacktrace()
|
||||
|
||||
static void print_buffer(char *buffer, size_t count)
|
||||
{
|
||||
const char s[]= " ";
|
||||
for (; count && *buffer; --count)
|
||||
{
|
||||
int c= (int) *buffer++;
|
||||
fputc(isprint(c) ? c : ' ', stderr);
|
||||
my_write_stderr(isprint(*buffer) ? buffer : s, 1);
|
||||
++buffer;
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,10 +120,10 @@ static int safe_print_str(const char *addr, int max_len)
|
||||
|
||||
/* Output a new line if something was printed. */
|
||||
if (total != (size_t) max_len)
|
||||
fputc('\n', stderr);
|
||||
my_safe_printf_stderr("%s", "\n");
|
||||
|
||||
if (nbytes == -1)
|
||||
fprintf(stderr, "Can't read from address %p: %m.\n", addr);
|
||||
my_safe_printf_stderr("Can't read from address %p\n", addr);
|
||||
|
||||
close(fd);
|
||||
|
||||
@ -144,13 +145,13 @@ void my_safe_print_str(const char* val, int max_len)
|
||||
|
||||
if (!PTR_SANE(val))
|
||||
{
|
||||
fprintf(stderr, "is an invalid pointer\n");
|
||||
my_safe_printf_stderr("%s", "is an invalid pointer\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (; max_len && PTR_SANE(val) && *val; --max_len)
|
||||
fputc(*val++, stderr);
|
||||
fputc('\n', stderr);
|
||||
my_write_stderr((val++), 1);
|
||||
my_safe_printf_stderr("%s", "\n");
|
||||
}
|
||||
|
||||
#if defined(HAVE_PRINTSTACK)
|
||||
@ -162,9 +163,10 @@ void my_print_stacktrace(uchar* stack_bottom __attribute__((unused)),
|
||||
ulong thread_stack __attribute__((unused)))
|
||||
{
|
||||
if (printstack(fileno(stderr)) == -1)
|
||||
fprintf(stderr, "Error when traversing the stack, stack appears corrupt.\n");
|
||||
my_safe_printf_stderr("%s",
|
||||
"Error when traversing the stack, stack appears corrupt.\n");
|
||||
else
|
||||
fprintf(stderr,
|
||||
my_safe_printf_stderr("%s"
|
||||
"Please read "
|
||||
"http://dev.mysql.com/doc/refman/5.1/en/resolve-stack-dump.html\n"
|
||||
"and follow instructions on how to resolve the stack trace.\n"
|
||||
@ -207,9 +209,9 @@ static void my_demangle_symbols(char **addrs, int n)
|
||||
}
|
||||
|
||||
if (demangled)
|
||||
fprintf(stderr, "%s(%s+%s\n", addrs[i], demangled, end);
|
||||
my_safe_printf_stderr("%s(%s+%s\n", addrs[i], demangled, end);
|
||||
else
|
||||
fprintf(stderr, "%s\n", addrs[i]);
|
||||
my_safe_printf_stderr("%s\n", addrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,7 +222,7 @@ void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack)
|
||||
void *addrs[128];
|
||||
char **strings= NULL;
|
||||
int n = backtrace(addrs, array_elements(addrs));
|
||||
fprintf(stderr, "stack_bottom = %p thread_stack 0x%lx\n",
|
||||
my_safe_printf_stderr("stack_bottom = %p thread_stack 0x%lx\n",
|
||||
stack_bottom, thread_stack);
|
||||
#if BACKTRACE_DEMANGLE
|
||||
if ((strings= backtrace_symbols(addrs, n)))
|
||||
@ -314,8 +316,9 @@ void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack)
|
||||
#endif
|
||||
if (!fp)
|
||||
{
|
||||
fprintf(stderr, "frame pointer is NULL, did you compile with\n\
|
||||
-fomit-frame-pointer? Aborting backtrace!\n");
|
||||
my_safe_printf_stderr("%s",
|
||||
"frame pointer is NULL, did you compile with\n"
|
||||
"-fomit-frame-pointer? Aborting backtrace!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -323,24 +326,28 @@ void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack)
|
||||
{
|
||||
ulong tmp= min(0x10000,thread_stack);
|
||||
/* Assume that the stack starts at the previous even 65K */
|
||||
stack_bottom= (uchar*) (((ulong) &fp + tmp) &
|
||||
~(ulong) 0xFFFF);
|
||||
fprintf(stderr, "Cannot determine thread, fp=%p, backtrace may not be correct.\n", fp);
|
||||
stack_bottom= (uchar*) (((ulong) &fp + tmp) & ~(ulong) 0xFFFF);
|
||||
my_safe_printf_stderr("Cannot determine thread, fp=%p, "
|
||||
"backtrace may not be correct.\n", fp);
|
||||
}
|
||||
if (fp > (uchar**) stack_bottom ||
|
||||
fp < (uchar**) stack_bottom - thread_stack)
|
||||
{
|
||||
fprintf(stderr, "Bogus stack limit or frame pointer,\
|
||||
fp=%p, stack_bottom=%p, thread_stack=%ld, aborting backtrace.\n",
|
||||
my_safe_printf_stderr("Bogus stack limit or frame pointer, "
|
||||
"fp=%p, stack_bottom=%p, thread_stack=%ld, "
|
||||
"aborting backtrace.\n",
|
||||
fp, stack_bottom, thread_stack);
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Stack range sanity check OK, backtrace follows:\n");
|
||||
my_safe_printf_stderr("%s",
|
||||
"Stack range sanity check OK, backtrace follows:\n");
|
||||
#if defined(__alpha__) && defined(__GNUC__)
|
||||
fprintf(stderr, "Warning: Alpha stacks are difficult -\
|
||||
will be taking some wild guesses, stack trace may be incorrect or \
|
||||
terminate abruptly\n");
|
||||
my_safe_printf_stderr("%s",
|
||||
"Warning: Alpha stacks are difficult -"
|
||||
"will be taking some wild guesses, stack trace may be incorrect or "
|
||||
"terminate abruptly\n");
|
||||
|
||||
/* On Alpha, we need to get pc */
|
||||
__asm __volatile__ ("bsr %0, do_next; do_next: "
|
||||
:"=r"(pc)
|
||||
@ -354,7 +361,8 @@ void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack)
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
uchar** new_fp = (uchar**)*fp;
|
||||
fprintf(stderr, "%p\n", frame_count == sigreturn_frame_count ?
|
||||
my_safe_printf_stderr("%p\n",
|
||||
frame_count == sigreturn_frame_count ?
|
||||
*(fp + SIGRETURN_FRAME_OFFSET) : *(fp + 1));
|
||||
#endif /* defined(__386__) || defined(__x86_64__) */
|
||||
|
||||
@ -369,35 +377,37 @@ void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack)
|
||||
{
|
||||
pc = find_prev_pc(pc, fp);
|
||||
if (pc)
|
||||
fprintf(stderr, "%p\n", pc);
|
||||
my_safe_printf_stderr("%p\n", pc);
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Not smart enough to deal with the rest\
|
||||
of this stack\n");
|
||||
my_safe_printf_stderr("%s",
|
||||
"Not smart enough to deal with the rest of this stack\n");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Not smart enough to deal with the rest of this stack\n");
|
||||
my_safe_printf_stderr("%s",
|
||||
"Not smart enough to deal with the rest of this stack\n");
|
||||
goto end;
|
||||
}
|
||||
#endif /* defined(__alpha__) && defined(__GNUC__) */
|
||||
if (new_fp <= fp )
|
||||
{
|
||||
fprintf(stderr, "New value of fp=%p failed sanity check,\
|
||||
terminating stack trace!\n", new_fp);
|
||||
my_safe_printf_stderr("New value of fp=%p failed sanity check, "
|
||||
"terminating stack trace!\n", new_fp);
|
||||
goto end;
|
||||
}
|
||||
fp = new_fp;
|
||||
++frame_count;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Stack trace seems successful - bottom reached\n");
|
||||
my_safe_printf_stderr("%s",
|
||||
"Stack trace seems successful - bottom reached\n");
|
||||
|
||||
end:
|
||||
fprintf(stderr,
|
||||
"Please read http://dev.mysql.com/doc/refman/5.1/en/resolve-stack-dump.html\n"
|
||||
my_safe_printf_stderr("%s",
|
||||
"Please read "
|
||||
"http://dev.mysql.com/doc/refman/5.1/en/resolve-stack-dump.html\n"
|
||||
"and follow instructions on how to resolve the stack trace.\n"
|
||||
"Resolved stack trace is much more helpful in diagnosing the\n"
|
||||
"problem, so please do resolve it\n");
|
||||
@ -618,7 +628,7 @@ void my_print_stacktrace(uchar* unused1, ulong unused2)
|
||||
&(package.sym));
|
||||
have_source= SymGetLineFromAddr64(hProcess, addr, &line_offset, &line);
|
||||
|
||||
fprintf(stderr, "%p ", addr);
|
||||
my_safe_printf_stderr("%p ", addr);
|
||||
if(have_module)
|
||||
{
|
||||
char *base_image_name= strrchr(module.ImageName, '\\');
|
||||
@ -626,12 +636,13 @@ void my_print_stacktrace(uchar* unused1, ulong unused2)
|
||||
base_image_name++;
|
||||
else
|
||||
base_image_name= module.ImageName;
|
||||
fprintf(stderr, "%s!", base_image_name);
|
||||
my_safe_printf_stderr("%s!", base_image_name);
|
||||
}
|
||||
if(have_symbol)
|
||||
fprintf(stderr, "%s()", package.sym.Name);
|
||||
my_safe_printf_stderr("%s()", package.sym.Name);
|
||||
|
||||
else if(have_module)
|
||||
fprintf(stderr, "???");
|
||||
my_safe_printf_stderr("%s", "???");
|
||||
|
||||
if(have_source)
|
||||
{
|
||||
@ -640,11 +651,11 @@ void my_print_stacktrace(uchar* unused1, ulong unused2)
|
||||
base_file_name++;
|
||||
else
|
||||
base_file_name= line.FileName;
|
||||
fprintf(stderr,"[%s:%u]", base_file_name, line.LineNumber);
|
||||
my_safe_printf_stderr("[%s:%u]",
|
||||
base_file_name, line.LineNumber);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
my_safe_printf_stderr("%s", "\n");
|
||||
}
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
|
||||
@ -681,22 +692,22 @@ void my_write_core(int unused)
|
||||
if(MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
|
||||
hFile, MiniDumpNormal, &info, 0, 0))
|
||||
{
|
||||
fprintf(stderr, "Minidump written to %s\n",
|
||||
_fullpath(path, dump_fname, sizeof(path)) ? path : dump_fname);
|
||||
my_safe_printf_stderr("Minidump written to %s\n",
|
||||
_fullpath(path, dump_fname, sizeof(path)) ?
|
||||
path : dump_fname);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr,"MiniDumpWriteDump() failed, last error %u\n",
|
||||
GetLastError());
|
||||
my_safe_printf_stderr("MiniDumpWriteDump() failed, last error %u\n",
|
||||
(uint) GetLastError());
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "CreateFile(%s) failed, last error %u\n", dump_fname,
|
||||
GetLastError());
|
||||
my_safe_printf_stderr("CreateFile(%s) failed, last error %u\n",
|
||||
dump_fname, (uint) GetLastError());
|
||||
}
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
|
||||
@ -704,11 +715,212 @@ void my_safe_print_str(const char *val, int len)
|
||||
{
|
||||
__try
|
||||
{
|
||||
fprintf(stderr, "%.*s\n", len, val);
|
||||
my_write_stderr(val, len);
|
||||
}
|
||||
__except(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
fprintf(stderr, "is an invalid string pointer\n");
|
||||
my_safe_printf_stderr("%s", "is an invalid string pointer\n");
|
||||
}
|
||||
}
|
||||
#endif /*__WIN__*/
|
||||
|
||||
|
||||
#ifdef __WIN__
|
||||
size_t my_write_stderr(const void *buf, size_t count)
|
||||
{
|
||||
DWORD bytes_written;
|
||||
SetFilePointer(GetStdHandle(STD_ERROR_HANDLE), 0, NULL, FILE_END);
|
||||
WriteFile(GetStdHandle(STD_ERROR_HANDLE), buf, count, &bytes_written, NULL);
|
||||
return bytes_written;
|
||||
}
|
||||
#else
|
||||
size_t my_write_stderr(const void *buf, size_t count)
|
||||
{
|
||||
return (size_t) write(STDERR_FILENO, buf, count);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static const char digits[]= "0123456789abcdef";
|
||||
|
||||
char *my_safe_utoa(int base, ulonglong val, char *buf)
|
||||
{
|
||||
*buf--= 0;
|
||||
do {
|
||||
*buf--= digits[val % base];
|
||||
} while ((val /= base) != 0);
|
||||
return buf + 1;
|
||||
}
|
||||
|
||||
|
||||
char *my_safe_itoa(int base, longlong val, char *buf)
|
||||
{
|
||||
char *orig_buf= buf;
|
||||
const my_bool is_neg= (val < 0);
|
||||
*buf--= 0;
|
||||
|
||||
if (is_neg)
|
||||
val= -val;
|
||||
if (is_neg && base == 16)
|
||||
{
|
||||
int ix;
|
||||
val-= 1;
|
||||
for (ix= 0; ix < 16; ++ix)
|
||||
buf[-ix]= '0';
|
||||
}
|
||||
|
||||
do {
|
||||
*buf--= digits[val % base];
|
||||
} while ((val /= base) != 0);
|
||||
|
||||
if (is_neg && base == 10)
|
||||
*buf--= '-';
|
||||
|
||||
if (is_neg && base == 16)
|
||||
{
|
||||
int ix;
|
||||
buf= orig_buf - 1;
|
||||
for (ix= 0; ix < 16; ++ix, --buf)
|
||||
{
|
||||
switch (*buf)
|
||||
{
|
||||
case '0': *buf= 'f'; break;
|
||||
case '1': *buf= 'e'; break;
|
||||
case '2': *buf= 'd'; break;
|
||||
case '3': *buf= 'c'; break;
|
||||
case '4': *buf= 'b'; break;
|
||||
case '5': *buf= 'a'; break;
|
||||
case '6': *buf= '9'; break;
|
||||
case '7': *buf= '8'; break;
|
||||
case '8': *buf= '7'; break;
|
||||
case '9': *buf= '6'; break;
|
||||
case 'a': *buf= '5'; break;
|
||||
case 'b': *buf= '4'; break;
|
||||
case 'c': *buf= '3'; break;
|
||||
case 'd': *buf= '2'; break;
|
||||
case 'e': *buf= '1'; break;
|
||||
case 'f': *buf= '0'; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf+1;
|
||||
}
|
||||
|
||||
|
||||
static const char *check_longlong(const char *fmt, my_bool *have_longlong)
|
||||
{
|
||||
*have_longlong= FALSE;
|
||||
if (*fmt == 'l')
|
||||
{
|
||||
fmt++;
|
||||
if (*fmt != 'l')
|
||||
*have_longlong= (sizeof(long) == sizeof(longlong));
|
||||
else
|
||||
{
|
||||
fmt++;
|
||||
*have_longlong= TRUE;
|
||||
}
|
||||
}
|
||||
return fmt;
|
||||
}
|
||||
|
||||
static size_t my_safe_vsnprintf(char *to, size_t size,
|
||||
const char* format, va_list ap)
|
||||
{
|
||||
char *start= to;
|
||||
char *end= start + size - 1;
|
||||
for (; *format; ++format)
|
||||
{
|
||||
my_bool have_longlong = FALSE;
|
||||
if (*format != '%')
|
||||
{
|
||||
if (to == end) /* end of buffer */
|
||||
break;
|
||||
*to++= *format; /* copy ordinary char */
|
||||
continue;
|
||||
}
|
||||
++format; /* skip '%' */
|
||||
|
||||
format= check_longlong(format, &have_longlong);
|
||||
|
||||
switch (*format)
|
||||
{
|
||||
case 'd':
|
||||
case 'i':
|
||||
case 'u':
|
||||
case 'x':
|
||||
case 'p':
|
||||
{
|
||||
longlong ival= 0;
|
||||
ulonglong uval = 0;
|
||||
if (*format == 'p')
|
||||
have_longlong= (sizeof(void *) == sizeof(longlong));
|
||||
if (have_longlong)
|
||||
{
|
||||
if (*format == 'u')
|
||||
uval= va_arg(ap, ulonglong);
|
||||
else
|
||||
ival= va_arg(ap, longlong);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*format == 'u')
|
||||
uval= va_arg(ap, unsigned int);
|
||||
else
|
||||
ival= va_arg(ap, int);
|
||||
}
|
||||
|
||||
{
|
||||
char buff[22];
|
||||
const int base= (*format == 'x' || *format == 'p') ? 16 : 10;
|
||||
char *val_as_str= (*format == 'u') ?
|
||||
my_safe_utoa(base, uval, &buff[sizeof(buff)-1]) :
|
||||
my_safe_itoa(base, ival, &buff[sizeof(buff)-1]);
|
||||
|
||||
/* Strip off "ffffffff" if we have 'x' format without 'll' */
|
||||
if (*format == 'x' && !have_longlong && ival < 0)
|
||||
val_as_str+= 8;
|
||||
|
||||
while (*val_as_str && to < end)
|
||||
*to++= *val_as_str++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
case 's':
|
||||
{
|
||||
const char *val= va_arg(ap, char*);
|
||||
if (!val)
|
||||
val= "(null)";
|
||||
while (*val && to < end)
|
||||
*to++= *val++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
*to= 0;
|
||||
return to - start;
|
||||
}
|
||||
|
||||
|
||||
size_t my_safe_snprintf(char* to, size_t n, const char* fmt, ...)
|
||||
{
|
||||
size_t result;
|
||||
va_list args;
|
||||
va_start(args,fmt);
|
||||
result= my_safe_vsnprintf(to, n, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
size_t my_safe_printf_stderr(const char* fmt, ...)
|
||||
{
|
||||
char to[512];
|
||||
size_t result;
|
||||
va_list args;
|
||||
va_start(args,fmt);
|
||||
result= my_safe_vsnprintf(to, sizeof(to), fmt, args);
|
||||
va_end(args);
|
||||
my_write_stderr(to, result);
|
||||
return result;
|
||||
}
|
||||
|
@ -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
|
||||
|
187
sql/mysqld.cc
187
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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user