/* * libthud -- detect and fix dereferencing a null pointer, * notify a parent process for error-reporting purposes. * Optionally produce a report on any signal. * * Copyright (C) 2004 David Collier-Brown * * This program is free software; you can redistribute it and/or modify * it under the terms of your choice of the GNU Lesser (Library) General * Public License or the Sun Common Development and Distribution License . */ #include /* For open(). */ #include /* For fprintf() */ #include /* For mmap(). */ #include /* For PAGESIZE. */ #include /* For getpid(). */ #include /* For errno. */ #include /* For strerror(). */ #include /* For sigaction(). */ #include /* For sigaction(). */ #include /* For putenv(). */ #include "thud.h" /* Just debugging code. */ #define MAXLINE 128 static char *ProgName = "libthud"; static void reportError(void); static char *signalLookup(int s); /* * signals, as per /usr/include. */ static struct sig_t { int signal; char *name; } Sig[] = { /* Signal Name Action Event */ { SIGHUP, "SIGHUP" }, /* Exit Hangup */ { SIGINT, "SIGINT" }, /* Exit Interrupt */ { SIGQUIT, "SIGQUIT" }, /* Core Quit */ { SIGILL, "SIGILL" }, /* Core Illegal Instruction */ { SIGTRAP, "SIGTRAP" }, /* Core Trace or Breakpoint Trap */ { SIGABRT, "SIGABRT" }, /* Core Abort */ { SIGEMT, "SIGEMT" }, /* Core Emulation Trap */ { SIGFPE, "SIGFPE" }, /* Core Arithmetic Exception */ { SIGBUS, "SIGBUS" }, /* Core Bus Error */ { SIGSEGV, "SIGSEGV" }, /* Core Segmentation Fault */ { SIGSYS, "SIGSYS" }, /* Core Bad System Call */ { SIGPIPE, "SIGPIPE" }, /* Exit Broken Pipe */ /* SIGTERM, "SIGTERM" Exit Terminated by ^C */ { SIGPWR, "SIGPWR" }, /* Ignore Power Fail or Restart */ { SIGXCPU, "SIGXCPU" }, /* Core CPU time exceeded */ { SIGXFSZ, "SIGXFSZ" }, /* Core File size limit exceeded*/ { SIGXRES, "SIGXRES" }, /* Ignore Resource exhaustion (SRM) */ { 0, "Zero" } /* None. Not an error. */ }; /* * sigHandler -- signal parent then map in zeros. */ /*ARGSUSED*/ void sigHandler(int s, siginfo_t *reason, void *context) { int fd; caddr_t address; int code; struct sigaction act; static int initialized = 0; /* Print information only we have. */ (void) fprintf(stderr, "%s: signal %d (%s) detected\n", ProgName, s, signalLookup(s)); address = (caddr_t)-1; code = -1; if (reason != NULL) { /* Look at faulting address. */ if (reason->si_code == SEGV_MAPERR ) { address = reason->si_addr; code = SEGV_MAPERR; (void) fprintf(stderr, " Address %p " "not mapped.\n", reason->si_addr); } else if (reason->si_code == SEGV_ACCERR) { address = reason->si_addr; code = SEGV_ACCERR; (void) fprintf(stderr, " Invalid " "permissions for address %p.\n", reason->si_addr); } /* Report errno if there is one. */ if (reason->si_errno != 0) { (void) fprintf(stderr, " errno " "= %d (%s)\n", errno, strerror(errno)); } } else { (void) fprintf(stderr, " reason == NULL\n"); } (void) fflush(stderr); reportError(); /* SEGVs at zero with no memory there are special. */ say(("past signalling\n")); if (s == SIGSEGV && address == 0 && code == SEGV_MAPERR) { say(("mmapping\n")); if (initialized != 0) { (void) fprintf(stderr, "%s, second " "dereference of address zero, " "can't happen?\n", ProgName); } /* Try to mmap in memory to make null pointers safe. */ if ((fd = open("/dev/zero", O_RDONLY)) == -1) { /* Force the default to be taken. */ (void) fprintf(stderr, "%s, can't " "open /dev/zero, unable to fix " "null-pointer dereferences.\n", ProgName); } else if (mmap(0, (size_t) PAGESIZE, PROT_READ, MAP_PRIVATE|MAP_FIXED,fd, 0) == MAP_FAILED) { (void) close(fd); (void) fprintf(stderr, "%s, can't " "mmap 0@0, unable to fix " "null-pointer dereferences.\n", ProgName); } else { /* Success. */ say(("mmap succeeded\n")); (void) close(fd); } initialized = 1; /* We now need to turn off the signal handler */ /* so it can fail normally. */ say(("not mmaping, turning off signal\n")); act.sa_handler = SIG_DFL; act.sa_flags = 0; (void) sigemptyset(&act.sa_mask); if (sigaction(s, &act, NULL) == -1) { (void) fprintf(stderr, "sigaction(%d, SIG_DFL) " "failed.\n", s); } } say(("done handling signal\n")); return; } /* * reortError -- tell the world what hapened and where. */ static void reportError() { pid_t pid; char procCmd[MAXLINE], oldEnv[MAXLINE]; static int nCores = 1; pid = getpid(); /* Save LD_PRELOAD so we can call system. */ (void) snprintf(oldEnv, MAXLINE, "LD_PRELOAD=%s", getenv("LD_PRELOAD")); (void) putenv("LD_PRELOAD="); (void) fprintf(stderr, "%s: additional information follows, and a core will be\n" " written to /usr/tmp/thud_core.%d.%ld\n", ProgName, nCores, pid); (void) fprintf(stderr, "\npstack:\n"); (void) snprintf(procCmd, (int)sizeof(procCmd), "pstack %ld 1>&2'", pid); (void) system(procCmd); (void) fprintf(stderr, "\npmap:\n"); (void) snprintf(procCmd, (int)sizeof(procCmd), "pmap -x %ld 1>&2", pid); (void) system(procCmd); (void) fprintf(stderr, "\npsig:\n"); (void) snprintf(procCmd, (int)sizeof(procCmd), "psig %ld 1>&2", pid); (void) system(procCmd); (void) fprintf(stderr, "\npfiles:\n"); (void) snprintf(procCmd, (int)sizeof(procCmd), "pfiles %ld 1>&2", pid); (void) system(procCmd); (void) fprintf(stderr, "\npwdx:\n"); (void) snprintf(procCmd, (int)sizeof(procCmd), "pwdx %ld 1>&2", pid); (void) system(procCmd); (void) fprintf(stderr, "\nptree:\n"); (void) snprintf(procCmd, (int)sizeof(procCmd), "ptree %ld 1>&2", pid); (void) system(procCmd); (void) fprintf(stderr, "\nsaving core to /var/tmp/thud_core.%d.%ld\n", nCores, pid); (void) snprintf(procCmd, (int)sizeof(procCmd), "gcore -o /var/tmp/thud_core.%d %ld 1>&2", nCores, pid); (void) system(procCmd); (void) fprintf(stderr, "\n"); nCores++; (void) putenv(oldEnv); return; } /* * initThud -- install the handler. Currently only defined for * SIGSEGV, as that is what a null-pointer dereference causes. * */ void initThud() { struct sigaction newAct, oldAct; int i; char *p; newAct.sa_sigaction = sigHandler; (void) sigemptyset(&newAct.sa_mask); newAct.sa_flags = SA_SIGINFO|SA_RESTART; if ((p = getenv("THUD")) != NULL && strcmp(p, "-a") == 0) { /* Thud passed us the -a option */ for (i=0; Sig[i].signal != 0; i++) { if (sigaction(Sig[i].signal, &newAct, &oldAct) == -1) { (void) fprintf(stderr, "Sigaction failed " "to set signal %s, ignored\n", Sig[i].name); } } } else { if (sigaction(SIGSEGV, &newAct, &oldAct) == -1) { (void) fprintf(stderr, "Sigaction failed, halting.\n"); (void) exit(-1); } } } static char * signalLookup(int s) { struct sig_t *p = Sig; for (p=Sig; p->signal != 0; p++) { if (p->signal == s) { return p->name; } } return "Unexpected signal"; }