| /* ----------------------------------------------------------------------------- |
| * signal.c |
| * |
| * WAD signal handler. |
| * |
| * Author(s) : David Beazley (beazley@cs.uchicago.edu) |
| * |
| * Copyright (C) 2000. The University of Chicago. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library 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 |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| * |
| * See the file COPYING for a complete copy of the LGPL. |
| * ----------------------------------------------------------------------------- */ |
| |
| #include "wad.h" |
| |
| static char cvs[] = "$Header$"; |
| |
| extern void wad_stab_debug(); |
| |
| /* For some odd reason, certain linux distributions do not seem to define the |
| register constants in a way that is easily accessible to us. This is a hack */ |
| |
| #ifdef WAD_LINUX |
| #ifndef ESP |
| #define ESP 7 |
| #endif |
| #ifndef EBP |
| #define EBP 6 |
| #endif |
| #ifndef EIP |
| #define EIP 14 |
| #endif |
| #ifndef ESI |
| #define ESI 5 |
| #endif |
| #ifndef EDI |
| #define EDI 4 |
| #endif |
| #ifndef EBX |
| #define EBX 8 |
| #endif |
| |
| #endif |
| |
| /* Signal handling stack */ |
| #define STACK_SIZE 4*SIGSTKSZ |
| char wad_sig_stack[STACK_SIZE]; |
| |
| /* This variable is set if the signal handler thinks that the |
| heap has overflowed */ |
| |
| int wad_heap_overflow = 0; |
| |
| static void (*sig_callback)(int signo, WadFrame *data, char *ret) = 0; |
| |
| void wad_set_callback(void (*s)(int,WadFrame *,char *ret)) { |
| sig_callback = s; |
| } |
| |
| /* This bit of nastiness is used to make a non-local return from the |
| signal handler to a configurable location on the call stack. In a nutshell, |
| this works by repeatedly calling "restore" to roll back the |
| register windows and stack pointer. Then we fake a return value and |
| return to the caller as if the function had actually completed |
| normally. */ |
| |
| int wad_nlr_levels = 0; |
| static volatile int *volatile nlr_p = &wad_nlr_levels; |
| long wad_nlr_value = 0; |
| void (*wad_nlr_func)(void) = 0; |
| |
| /* Set the return value from another module */ |
| void wad_set_return_value(long value) { |
| wad_nlr_value = value; |
| } |
| |
| /* Set the return function */ |
| void wad_set_return_func(void(*f)(void)) { |
| wad_nlr_func = f; |
| } |
| |
| #ifdef WAD_SOLARIS |
| static void nonlocalret() { |
| long a; |
| |
| a = wad_nlr_value; |
| /* We never call this procedure as a function. This code |
| causes an immediate return if someone does this */ |
| |
| asm("jmp %i7 + 8"); |
| asm("restore"); |
| |
| /* This is the real entry point */ |
| /* asm(".globl _returnsignal");*/ |
| asm(".type _returnsignal,2"); |
| asm("_returnsignal:"); |
| |
| while (*nlr_p > 0) { |
| (*nlr_p)--; |
| asm("restore"); |
| } |
| |
| asm("sethi %hi(wad_nlr_value), %o0"); |
| asm("or %o0, %lo(wad_nlr_value), %o0"); |
| asm("ld [%o0], %i0"); |
| |
| /* If there is a non-local return function. We're going to go ahead |
| and transfer control to it */ |
| |
| if (wad_nlr_func) |
| (*wad_nlr_func)(); |
| |
| asm("jmp %i7 + 8"); |
| asm("restore"); |
| asm(".size _returnsignal,(.-_returnsignal)"); |
| } |
| #endif |
| |
| #ifdef WAD_LINUX |
| |
| /* Saved values of the machine registers */ |
| |
| long wad_saved_esi = 0; |
| long wad_saved_edi = 0; |
| long wad_saved_ebx = 0; |
| |
| static void nonlocalret() { |
| asm("_returnsignal:"); |
| while (*nlr_p > 0) { |
| (*nlr_p)--; |
| asm("leave"); |
| } |
| |
| if (wad_nlr_func) |
| (*wad_nlr_func)(); |
| |
| /* Restore the registers */ |
| asm("movl wad_saved_esi, %esi"); |
| asm("movl wad_saved_edi, %edi"); |
| asm("movl wad_saved_ebx, %ebx"); |
| asm("movl wad_nlr_value, %eax"); |
| asm("leave"); |
| asm("ret"); |
| } |
| |
| /* This function uses a heuristic to restore the callee-save registers on i386. |
| According to the Linux Assembly HOWTO, the %esi, %edi, %ebx, and %ebp registers |
| are callee-saved. All others are caller saved. To restore the callee-save |
| registers, we use the fact that the C compiler saves the callee-save registers |
| (if any) at the beginning of function execution. Therefore, we can scan the |
| instructions at the start of each function in the stack trace to try and find |
| where they are. |
| |
| The following heuristic is used: |
| |
| 1. Each function starts with a preamble like this which saves the %ebp |
| register: |
| |
| 55 89 e5 ---> push %ebp |
| mov %esp, %ebp |
| |
| 2. Next, space is allocated for local variables, using one of two schemes: |
| |
| 83 ec xx ---> Less than 256 bytes of local storage |
| ^^^ |
| length |
| |
| 81 ec xx xx xx xx --> More than 256 bytes of local storage |
| ^^^^^^^^^^^ |
| length |
| |
| 3. After this, a collection of 1-byte stack push op codes might appear |
| |
| 56 = pushl %esi |
| 57 = pushl %edi |
| 53 = pushl %ebx |
| |
| |
| Based on the size of local variable storage and the order in which |
| the %esi, %edi, and %ebx registers are pushed on the stack, we can |
| determine where in memory the registers are saved and restore them to |
| their proper values. |
| */ |
| |
| void wad_restore_i386_registers(WadFrame *f, int nlevels) { |
| WadFrame *lastf = f; |
| int localsize = 0; |
| unsigned char *pc; |
| unsigned long *saved; |
| int i, j; |
| int pci; |
| for (i = 0; i <= nlevels; i++, f=f->next) { |
| |
| /* This gets the starting instruction for the stack frame */ |
| pc = (unsigned char *) f->sym_base; |
| /* printf("pc = %x, base = %x, %s\n", f->pc, f->sym_base, SYMBOL(f)); */ |
| if (!pc) continue; |
| |
| /* Look for the standard prologue 0x55 0x89 0xe5 */ |
| if ((pc[0] == 0x55) && (pc[1] == 0x89) && (pc[2] == 0xe5)) { |
| /* Determine the size */ |
| pci = 3; |
| if ((pc[3] == 0x83) && (pc[4] == 0xec)) { |
| /* printf("8-bit size\n");*/ |
| localsize = (int) pc[5]; |
| pci = 6; |
| } |
| if ((pc[3] == 0x81) && (pc[4] == 0xec)) { |
| /* printf("32-bit size\n"); */ |
| localsize = (int) *((long *) (pc+5)); |
| pci = 10; |
| } |
| saved = (long *) (f->fp - localsize - sizeof(long)); |
| /* printf("saved = %x, fp = %x\n", saved, f->fp); |
| printf("localsize = %d\n", localsize); |
| */ |
| for (j = 0; j < 3; j++, saved--, pci++) { |
| if (pc[pci] == 0x57) { |
| wad_saved_edi = *saved; |
| /* printf("restored edi = %x\n", wad_saved_edi); */ |
| } |
| else if (pc[pci] == 0x56) { |
| wad_saved_esi = *saved; |
| /* printf("restored esi = %x\n", wad_saved_esi); */ |
| } |
| else if (pc[pci] == 0x53) { |
| wad_saved_ebx = *saved; |
| /* printf("restored ebx = %x\n", wad_saved_ebx); */ |
| } |
| else break; |
| } |
| } |
| } |
| } |
| |
| #endif |
| |
| void wad_signalhandler(int sig, siginfo_t *si, void *vcontext) { |
| greg_t *pc; |
| greg_t *npc; |
| greg_t *sp; |
| greg_t *fp; |
| #ifdef WAD_LINUX |
| greg_t *esi; |
| greg_t *edi; |
| greg_t *ebx; |
| #endif |
| |
| unsigned long addr; |
| ucontext_t *context; |
| unsigned long p_sp; /* process stack pointer */ |
| unsigned long p_pc; /* Process program counter */ |
| unsigned long p_fp; /* Process frame pointer */ |
| int nlevels = 0; |
| int found = 0; |
| void _returnsignal(); |
| WadFrame *frame, *origframe; |
| char *framedata; |
| char *retname = 0; |
| unsigned long current_brk; |
| |
| /* Reset all of the signals while running WAD */ |
| wad_signal_clear(); |
| |
| wad_nlr_func = 0; |
| |
| context = (ucontext_t *) vcontext; |
| |
| wad_printf("WAD: Collecting debugging information...\n"); |
| |
| /* Read the segments */ |
| if (wad_segment_read() < 0) { |
| wad_printf("WAD: Unable to read segment map\n"); |
| return; |
| } |
| |
| if (wad_debug_mode & DEBUG_SIGNAL) { |
| wad_printf("WAD: siginfo = %x, context = %x\n", si, vcontext); |
| } |
| |
| current_brk = (long) sbrk(0); |
| |
| /* Get some information about the current context */ |
| |
| #ifdef WAD_SOLARIS |
| pc = &((context->uc_mcontext).gregs[REG_PC]); |
| npc = &((context->uc_mcontext).gregs[REG_nPC]); |
| sp = &((context->uc_mcontext).gregs[REG_SP]); |
| #endif |
| |
| #ifdef WAD_LINUX |
| sp = &((context->uc_mcontext).gregs[ESP]); /* Top of stack */ |
| fp = &((context->uc_mcontext).gregs[EBP]); /* Stack base - frame pointer */ |
| pc = &((context->uc_mcontext).gregs[EIP]); /* Current instruction */ |
| esi = &((context->uc_mcontext).gregs[ESI]); |
| edi = &((context->uc_mcontext).gregs[EDI]); |
| ebx = &((context->uc_mcontext).gregs[EBX]); |
| |
| wad_saved_esi = (unsigned long) (*esi); |
| wad_saved_edi = (unsigned long) (*edi); |
| wad_saved_ebx = (unsigned long) (*ebx); |
| |
| /* printf("esi = %x, edi = %x, ebx = %x\n", wad_saved_esi, wad_saved_edi, wad_saved_ebx); */ |
| |
| /* printf("&sp = %x, &pc = %x\n", sp, pc); */ |
| #endif |
| |
| /* Get some information out of the signal handler stack */ |
| addr = (unsigned long) si->si_addr; |
| |
| /* See if this might be a stack overflow */ |
| |
| p_pc = (unsigned long) (*pc); |
| p_sp = (unsigned long) (*sp); |
| #ifdef WAD_LINUX |
| p_fp = (unsigned long) (*fp); |
| #endif |
| #ifdef WAD_SOLARIS |
| p_fp = (unsigned long) *(((long *) p_sp) + 14); |
| #endif |
| |
| if (wad_debug_mode & DEBUG_SIGNAL) { |
| wad_printf("fault at address %x, pc = %x, sp = %x, fp = %x\n", addr, p_pc, p_sp, p_fp); |
| } |
| frame = wad_stack_trace(p_pc, p_sp, p_fp); |
| |
| if (!frame) { |
| /* We're really hosed. Not possible to generate a stack trace */ |
| wad_printf("WAD: Unable to generate stack trace.\n"); |
| wad_printf("WAD: Maybe the call stack has been corrupted by buffer overflow.\n"); |
| wad_signal_clear(); |
| return; |
| } |
| |
| { |
| WadFrame *f = frame; |
| while (f) { |
| wad_find_object(f); |
| wad_find_symbol(f); |
| f = f->next; |
| } |
| f = frame; |
| while (f) { |
| wad_find_debug(f); |
| wad_build_vars(f); |
| f = f->next; |
| } |
| } |
| wad_heap_overflow = 0; |
| if (sig == SIGSEGV) { |
| if (addr >= current_brk) wad_heap_overflow = 1; |
| } |
| |
| wad_stack_debug(frame); |
| |
| /* Generate debugging strings */ |
| wad_debug_make_strings(frame); |
| |
| wad_stab_debug(); |
| |
| /* Walk the exception frames and try to find a return point */ |
| origframe = frame; |
| while (frame) { |
| WadReturnFunc *wr = wad_check_return(frame->sym_name); |
| if (wr) { |
| found = 1; |
| wad_nlr_value = wr->value; |
| retname = wr->name; |
| } |
| if (found) { |
| frame->last = 1; /* Cut off top of the stack trace */ |
| break; |
| } |
| frame = frame->next; |
| nlevels++; |
| } |
| |
| |
| if (found) { |
| wad_nlr_levels = nlevels - 1; |
| #ifdef WAD_LINUX |
| wad_restore_i386_registers(origframe, wad_nlr_levels); |
| #endif |
| } else { |
| wad_nlr_levels = -1; |
| } |
| |
| wad_string_debug(); |
| wad_memory_debug(); |
| |
| /* Before we do anything with callbacks, we are going |
| to attempt to dump a wad-core */ |
| |
| { |
| int fd; |
| static int already = 0; |
| fd = open("wadtrace",O_WRONLY | O_CREAT | (already*O_APPEND) | ((already==0)*O_TRUNC),0666); |
| if (fd > 0) { |
| wad_dump_trace(fd,sig,origframe,retname); |
| close(fd); |
| already=1; |
| } |
| } |
| |
| if (sig_callback) { |
| (*sig_callback)(sig,origframe,retname); |
| } else { |
| /* No signal handler defined. Go invoke the default */ |
| |
| wad_default_callback(sig, origframe,retname); |
| } |
| |
| if (wad_debug_mode & DEBUG_HOLD) while(1); |
| |
| /* If we found a function to which we should return, we jump to |
| an alternative piece of code that unwinds the stack and |
| initiates a non-local return. */ |
| |
| if (wad_nlr_levels >= 0) { |
| *(pc) = (greg_t) _returnsignal; |
| #ifdef WAD_SOLARIS |
| *(npc) = *(pc) + 4; |
| #endif |
| if (!(wad_debug_mode & DEBUG_ONESHOT)) { |
| wad_signal_init(); |
| } |
| return; |
| } |
| exit(1); |
| } |
| |
| |
| /* ----------------------------------------------------------------------------- |
| * wad_signal_init() |
| * |
| * Resets the signal handler. |
| * ----------------------------------------------------------------------------- */ |
| |
| void wad_signal_init() { |
| struct sigaction newvec; |
| static stack_t sigstk; |
| static int initstack = 0; |
| |
| if (wad_debug_mode & DEBUG_INIT) { |
| wad_printf("WAD: Initializing signal handler.\n"); |
| } |
| /* This is buggy in Linux and threads. disabled by default */ |
| |
| #ifndef WAD_LINUX |
| |
| if (!initstack) { |
| /* Set up an alternative stack */ |
| |
| sigstk.ss_sp = (char *) wad_sig_stack; |
| sigstk.ss_size = STACK_SIZE; |
| sigstk.ss_flags = 0; |
| if (!(wad_debug_mode & DEBUG_NOSTACK)) { |
| if (sigaltstack(&sigstk, (stack_t*)0) < 0) { |
| perror("sigaltstack"); |
| } |
| } |
| initstack=1; |
| } |
| #endif |
| |
| sigemptyset(&newvec.sa_mask); |
| sigaddset(&newvec.sa_mask, SIGSEGV); |
| sigaddset(&newvec.sa_mask, SIGBUS); |
| sigaddset(&newvec.sa_mask, SIGABRT); |
| sigaddset(&newvec.sa_mask, SIGILL); |
| sigaddset(&newvec.sa_mask, SIGFPE); |
| newvec.sa_flags = SA_SIGINFO; |
| |
| if (wad_debug_mode & DEBUG_ONESHOT) { |
| newvec.sa_flags |= SA_RESETHAND; |
| } |
| #ifndef WAD_LINUX |
| if (!(wad_debug_mode & DEBUG_NOSTACK)) { |
| newvec.sa_flags |= SA_ONSTACK; |
| } |
| #endif |
| newvec.sa_sigaction = ((void (*)(int,siginfo_t *, void *)) wad_signalhandler); |
| if (sigaction(SIGSEGV, &newvec, NULL) < 0) goto werror; |
| if (sigaction(SIGBUS, &newvec, NULL) < 0) goto werror; |
| if (sigaction(SIGABRT, &newvec, NULL) < 0) goto werror; |
| if (sigaction(SIGFPE, &newvec, NULL) < 0) goto werror; |
| if (sigaction(SIGILL, &newvec, NULL) < 0) goto werror; |
| |
| return; |
| werror: |
| wad_printf("WAD: Couldn't install signal handler!\n"); |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * clear signals |
| * ----------------------------------------------------------------------------- */ |
| |
| void wad_signal_clear() { |
| signal(SIGSEGV, SIG_DFL); |
| signal(SIGBUS, SIG_DFL); |
| signal(SIGILL, SIG_DFL); |
| signal(SIGFPE, SIG_DFL); |
| signal(SIGABRT, SIG_DFL); |
| } |
| |
| |
| |