/* This is for linux 2.2.x (tested with 2.2.14); I have no idea how well * it'll work for anything else... * - Rob Seace * * Note: basic idea blatently stolen from Antirez's nifty "11logger" * module and kernel patch... Also, bits of code (mainly just as a * template for a valid module; this is my first LKM)... That code is: * "Copyright (C) 2000 Salvatore Sanfilippo " * and, it can be found at "http://www.kyuzz.org/antirez/sigsegv/"... * * Compile with: * gcc -O2 -Wall -D__KERNEL__ -DMODULE -c coredump.c * * Usage: * insmod coredump.o [quiet=1] [nocore=1] * * Then, all core dumps from ELF binaries (no a.out binary handling yet, * I'm afraid) should cause a message to be logged via printk(), with * info much like "11logger" logged... * * If you specify "quiet=1", then no load/unload messages will be * printed/logged when the module is started/stopped... * * If you specify "nocore=1", then the normal core dump handling * will be totally suppressed, and we'll just log the message and * return, without calling the real handler... This is one method * you could use to eliminate all core dumps on your system... * * To disable: * rmmod coredump */ /* __SMP__ auto definition */ #include #ifdef CONFIG_SMP #ifndef __SMP__ #define __SMP__ #endif #endif #include #include #include #include #include #include #include #include #include #include #include #define CDVERSION "0.0.5" /* just informational module stuff; for "modinfo" to read */ MODULE_AUTHOR ("Rob Seace and Salvatore Sanfilippo "); MODULE_DESCRIPTION ("Log all (attempted) core dumps on a system"); /* module parameters */ static int quiet = 0; static int nocore = 0; MODULE_PARM (quiet, "i"); MODULE_PARM_DESC (quiet, "Don't show module load/unload messages"); MODULE_PARM (nocore, "i"); MODULE_PARM_DESC (nocore, "Prevent real core dump from happening, after logging"); /* old saved info for the real core dump handler */ static struct linux_binfmt *old_binfmt; static int (*old_dump_handler) (long signr, struct pt_regs *regs); #ifndef NO_FULL_CMDLINE_ARGS /* These two functions stolen from "/usr/src/linux/fs/proc/array.c", where they are unfortunately declared "static", and not available for calling directly, here... *sigh* I hate duplicating code like this... */ static unsigned long get_phys_addr(struct task_struct * p, unsigned long ptr) { pgd_t *page_dir; pmd_t *page_middle; pte_t pte; if (!p || !p->mm || ptr >= TASK_SIZE) return 0; /* Check for NULL pgd .. shouldn't happen! */ if (!p->mm->pgd) { printk("get_phys_addr: pid %d has NULL pgd!\n", p->pid); return 0; } page_dir = pgd_offset(p->mm,ptr); if (pgd_none(*page_dir)) return 0; if (pgd_bad(*page_dir)) { printk("bad page directory entry %08lx\n", pgd_val(*page_dir)); pgd_clear(page_dir); return 0; } page_middle = pmd_offset(page_dir,ptr); if (pmd_none(*page_middle)) return 0; if (pmd_bad(*page_middle)) { printk("bad page middle entry %08lx\n", pmd_val(*page_middle)); pmd_clear(page_middle); return 0; } pte = *pte_offset(page_middle,ptr); if (!pte_present(pte)) return 0; return pte_page(pte) + (ptr & ~PAGE_MASK); } static int get_array(struct task_struct *p, unsigned long start, unsigned long end, char * buffer) { unsigned long addr; int size = 0, result = 0; char c; if (start >= end) return result; for (;;) { addr = get_phys_addr(p, start); if (!addr) return result; addr = kmap(addr, KM_READ); do { c = *(char *) addr; if (!c) result = size; if (size < PAGE_SIZE) buffer[size++] = c; else { kunmap(addr, KM_READ); return result; } addr++; start++; if (!c && start >= end) { kunmap(addr, KM_READ); return result; } } while (addr & ~PAGE_MASK); kunmap(addr-1, KM_READ); } return result; } #endif /* NO_FULL_CMDLINE_ARGS */ static void log_coredump (int sig, struct task_struct *t, struct pt_regs *regs) { char *p = NULL; #ifndef NO_FULL_CMDLINE_ARGS unsigned long page; int i, l; if ((page = __get_free_page(GFP_KERNEL))) { /* try to get full list of command-line args */ /* basic code ideas from "/usr/src/linux/fs/proc/array.c" */ p = (char*)page; l = get_array (t, t->mm->arg_start, t->mm->arg_end, p); if (l >= PAGE_SIZE) l = PAGE_SIZE - 1; /* leave room for '\0' */ for (i = 0; i < l; i++) { /* change inter-arg '\0's to spaces */ if (p[i] == '\0') p[i] = ' '; } p[i] = '\0'; /* null-terminate */ if (p[0] == '\0') p = NULL; /* just use "t->comm" if no args available */ } #endif /* NO_FULL_CMDLINE_ARGS */ if ((sig == SIGSEGV) || (sig == SIGBUS)) { /* these two seem to have the memory location in "t->tss.cr2" */ if ((!(t->tss.error_code & 2)) && (t->tss.cr2 == regs->eip)) { /* must be executing, if EIP = memory fault address */ printk (KERN_NOTICE \ "coredump: PID %d (%s), UID %d/%d, GID %d/%d exited on signal %d executing 0x%08lx\n", t->pid, p ? p : t->comm, t->uid, t->euid, t->gid, t->egid, sig, t->tss.cr2); } else { printk (KERN_NOTICE \ "coredump: PID %d (%s), UID %d/%d, GID %d/%d exited on signal %d %s 0x%08lx at EIP 0x%08lx\n", t->pid, p ? p : t->comm, t->uid, t->euid, t->gid, t->egid, sig, (t->tss.error_code & 2) ? "writing to" : "reading from", t->tss.cr2, regs->eip); } } else { /* for anything else (eg: SIGILL, SIGABRT, etc.), just spit out EIP */ printk (KERN_NOTICE \ "coredump: PID %d (%s), UID %d/%d, GID %d/%d exited on signal %d at EIP 0x%08lx\n", t->pid, p ? p : t->comm, t->uid, t->euid, t->gid, t->egid, sig, regs->eip); } #ifndef NO_FULL_CMDLINE_ARGS if (page) free_page (page); #endif /* NO_FULL_CMDLINE_ARGS */ } static int my_dump_handler (long signr, struct pt_regs *regs) { int ret; log_coredump (signr, current, regs); if (nocore) { ret = 0; } else { ret = (*old_dump_handler) (signr, regs); } return (ret); } int init_module(void) { if (!current) { printk ("coredump logger: Unable to initialize: I don't exist??\n"); return (-EFAULT); } else if (!(current->binfmt)) { printk ("coredump logger: Unable to initialize: I'm not an ELF binary??\n"); return (-EFAULT); } else if (!(current->binfmt->core_dump)) { printk ("coredump logger: Unable to initialize: no existing coredump handler!\n"); return (-EFAULT); } old_binfmt = current->binfmt; old_dump_handler = old_binfmt->core_dump; old_binfmt->core_dump = my_dump_handler; if (!quiet) printk ("coredump logger module version " CDVERSION " loaded\n"); return (0); } void cleanup_module(void) { old_binfmt->core_dump = old_dump_handler; if (!quiet) printk ("coredump logger module removed\n"); }