Use alternative stack for signal handler to allow catching stack overflow induced SIGSEGV/SIGILL

This commit is contained in:
Alexander Mai 2015-03-07 18:15:25 +01:00
parent 0131bda065
commit abde80f0d9
1 changed files with 44 additions and 9 deletions

View File

@ -254,16 +254,21 @@ static const char *signal_name(int signo)
* That is very sensitive to the operating system, hardware, compiler and runtime! * That is very sensitive to the operating system, hardware, compiler and runtime!
* The code is not meant for production environment, it's using functions not whitelisted for usage in a signal handler function. * The code is not meant for production environment, it's using functions not whitelisted for usage in a signal handler function.
*/ */
static void print_stacktrace(FILE* f, bool demangling) static void print_stacktrace(FILE* f, bool demangling, int maxdepth)
{ {
#if defined(USE_UNIX_BACKTRACE_SUPPORT) #if defined(USE_UNIX_BACKTRACE_SUPPORT)
void *array[32]= {0}; // the less resources the better... void *array[32]= {0}; // the less resources the better...
const int depth = backtrace(array, (int)GetArrayLength(array)); const int depth = backtrace(array, (int)GetArrayLength(array));
const int offset=3; // the first two entries are simply within our own exception handling code, third is within libc
if (maxdepth<0)
maxdepth=depth+offset;
else
maxdepth+=offset;
printf("maxdepth=%d\n", maxdepth);
char **symbolstrings = backtrace_symbols(array, depth); char **symbolstrings = backtrace_symbols(array, depth);
if (symbolstrings) { if (symbolstrings) {
fputs("Callstack:\n", f); fputs("Callstack:\n", f);
const int offset=3; // the first two entries are simply within our own exception handling code, third is within libc for (int i = offset; i < maxdepth; ++i) {
for (int i = offset; i < depth; ++i) {
const char * const symbol = symbolstrings[i]; const char * const symbol = symbolstrings[i];
char * realname = nullptr; char * realname = nullptr;
const char * const firstBracketName = strchr(symbol, '('); const char * const firstBracketName = strchr(symbol, '(');
@ -306,6 +311,22 @@ static void print_stacktrace(FILE* f, bool demangling)
#endif #endif
} }
/*
* Neither conclusive, nor portable
* Though one might to make it work beyond Linux x64
* \return true if address is supposed to be on stack (contrary to heap or elswhere).
* If unknown better retun false.
*/
static bool isAddressOnStack(const void* ptr)
{
#if defined(__linux) && defined(__amd64)
char a;
return ptr > &a;
#else
return false;
#endif
}
/* /*
* Entry pointer for signal handlers * Entry pointer for signal handlers
* It uses functions which are not safe to be called from a signal handler, * It uses functions which are not safe to be called from a signal handler,
@ -326,6 +347,7 @@ static void CppcheckSignalHandler(int signo, siginfo_t * info, void * context)
const char * const signame = signal_name(signo); const char * const signame = signal_name(signo);
const char * const sigtext = strsignal(signo); const char * const sigtext = strsignal(signo);
bool bPrintCallstack=true; bool bPrintCallstack=true;
const bool isaddressonstack = isAddressOnStack(info->si_addr);
FILE* f=CppCheckExecutor::getExceptionOutput()=="stderr" ? stderr : stdout; FILE* f=CppCheckExecutor::getExceptionOutput()=="stderr" ? stderr : stdout;
fputs("Internal error: cppcheck received signal ", f); fputs("Internal error: cppcheck received signal ", f);
fputs(signame, f); fputs(signame, f);
@ -420,8 +442,9 @@ static void CppcheckSignalHandler(int signo, siginfo_t * info, void * context)
default: default:
break; break;
} }
fprintf(f, " (at 0x%p).\n", fprintf(f, " (at 0x%p).%s\n",
info->si_addr); info->si_addr,
(isaddressonstack)?" Stackoverflow?":"");
break; break;
case SIGINT: case SIGINT:
bPrintCallstack=false; bPrintCallstack=false;
@ -438,17 +461,19 @@ static void CppcheckSignalHandler(int signo, siginfo_t * info, void * context)
default: default:
break; break;
} }
fprintf(f, " (%sat 0x%p).\n", fprintf(f, " (%sat 0x%p).%s\n",
(type==-1)? "" : (type==-1)? "" :
(type==0) ? "reading " : "writing ", (type==0) ? "reading " : "writing ",
info->si_addr); info->si_addr,
(isaddressonstack)?" Stackoverflow?":""
);
break; break;
default: default:
fputs(".\n", f); fputs(".\n", f);
break; break;
} }
if (bPrintCallstack) { if (bPrintCallstack) {
print_stacktrace(f, true); print_stacktrace(f, true, -1 /*(isaddressonstack)?8:-1*/);
fputs("\nPlease report this to the cppcheck developers!\n", f); fputs("\nPlease report this to the cppcheck developers!\n", f);
} }
@ -681,6 +706,10 @@ static int filterException(int code, PEXCEPTION_POINTERS ex)
* TODO Check for multi-threading issues! * TODO Check for multi-threading issues!
* *
*/ */
#if defined(USE_UNIX_SIGNAL_HANDLING)
const size_t MYSTACKSIZE = 64*1024+SIGSTKSZ;
char mytstack[MYSTACKSIZE];
#endif
int CppCheckExecutor::check_wrapper(CppCheck& cppcheck, int argc, const char* const argv[]) int CppCheckExecutor::check_wrapper(CppCheck& cppcheck, int argc, const char* const argv[])
{ {
#ifdef USE_WINDOWS_SEH #ifdef USE_WINDOWS_SEH
@ -693,9 +722,15 @@ int CppCheckExecutor::check_wrapper(CppCheck& cppcheck, int argc, const char* co
return -1; return -1;
} }
#elif defined(USE_UNIX_SIGNAL_HANDLING) #elif defined(USE_UNIX_SIGNAL_HANDLING)
stack_t segv_stack;
segv_stack.ss_sp = mytstack;
segv_stack.ss_flags = 0;
segv_stack.ss_size = MYSTACKSIZE;
sigaltstack(&segv_stack, NULL);
struct sigaction act; struct sigaction act;
memset(&act, 0, sizeof(act)); memset(&act, 0, sizeof(act));
act.sa_flags=SA_SIGINFO; act.sa_flags=SA_SIGINFO|SA_ONSTACK;
act.sa_sigaction=CppcheckSignalHandler; act.sa_sigaction=CppcheckSignalHandler;
for (std::size_t s=0; s<GetArrayLength(listofsignals); ++s) { for (std::size_t s=0; s<GetArrayLength(listofsignals); ++s) {
sigaction(listofsignals[s].signalnumber, &act, NULL); sigaction(listofsignals[s].signalnumber, &act, NULL);