parisc: ratelimit userspace segfault printing
authorHelge Deller <deller@gmx.de>
Mon, 5 May 2014 16:07:12 +0000 (18:07 +0200)
committerHelge Deller <deller@gmx.de>
Thu, 15 May 2014 19:12:15 +0000 (21:12 +0200)
Ratelimit printing of userspace segfaults and make it runtime
configurable via the /proc/sys/debug/exception-trace variable. This
should resolve syslog from growing way too fast and thus prevents
possible system service attacks.

Signed-off-by: Helge Deller <deller@gmx.de>
Cc: stable@vger.kernel.org # 3.13+
arch/parisc/Kconfig
arch/parisc/kernel/traps.c
arch/parisc/mm/fault.c

index bb2a8ec440e76ac80fcb2d0afa69284291054099..a8f4a70c07427f70e0bb8a0ffdfe3212382ecc46 100644 (file)
@@ -22,6 +22,7 @@ config PARISC
        select GENERIC_SMP_IDLE_THREAD
        select GENERIC_STRNCPY_FROM_USER
        select SYSCTL_ARCH_UNALIGN_ALLOW
+       select SYSCTL_EXCEPTION_TRACE
        select HAVE_MOD_ARCH_SPECIFIC
        select VIRT_TO_BUS
        select MODULES_USE_ELF_RELA
index 1cd1d0c83b6d7bd7a21d0a22c57e18f2ac27f65a..47ee620d15d27850ab8ebac1f739dfd3215dae9b 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/interrupt.h>
 #include <linux/console.h>
 #include <linux/bug.h>
+#include <linux/ratelimit.h>
 
 #include <asm/assembly.h>
 #include <asm/uaccess.h>
@@ -42,9 +43,6 @@
 
 #include "../math-emu/math-emu.h"      /* for handle_fpe() */
 
-#define PRINT_USER_FAULTS /* (turn this on if you want user faults to be */
-                         /*  dumped to the console via printk)          */
-
 #if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)
 DEFINE_SPINLOCK(pa_dbit_lock);
 #endif
@@ -160,6 +158,17 @@ void show_regs(struct pt_regs *regs)
        }
 }
 
+static DEFINE_RATELIMIT_STATE(_hppa_rs,
+       DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);
+
+#define parisc_printk_ratelimited(critical, regs, fmt, ...)    {             \
+       if ((critical || show_unhandled_signals) && __ratelimit(&_hppa_rs)) { \
+               printk(fmt, ##__VA_ARGS__);                                   \
+               show_regs(regs);                                              \
+       }                                                                     \
+}
+
+
 static void do_show_stack(struct unwind_frame_info *info)
 {
        int i = 1;
@@ -229,12 +238,10 @@ void die_if_kernel(char *str, struct pt_regs *regs, long err)
                if (err == 0)
                        return; /* STFU */
 
-               printk(KERN_CRIT "%s (pid %d): %s (code %ld) at " RFMT "\n",
+               parisc_printk_ratelimited(1, regs,
+                       KERN_CRIT "%s (pid %d): %s (code %ld) at " RFMT "\n",
                        current->comm, task_pid_nr(current), str, err, regs->iaoq[0]);
-#ifdef PRINT_USER_FAULTS
-               /* XXX for debugging only */
-               show_regs(regs);
-#endif
+
                return;
        }
 
@@ -321,14 +328,11 @@ static void handle_break(struct pt_regs *regs)
                        (tt == BUG_TRAP_TYPE_NONE) ? 9 : 0);
        }
 
-#ifdef PRINT_USER_FAULTS
-       if (unlikely(iir != GDB_BREAK_INSN)) {
-               printk(KERN_DEBUG "break %d,%d: pid=%d command='%s'\n",
+       if (unlikely(iir != GDB_BREAK_INSN))
+               parisc_printk_ratelimited(0, regs,
+                       KERN_DEBUG "break %d,%d: pid=%d command='%s'\n",
                        iir & 31, (iir>>13) & ((1<<13)-1),
                        task_pid_nr(current), current->comm);
-               show_regs(regs);
-       }
-#endif
 
        /* send standard GDB signal */
        handle_gdb_break(regs, TRAP_BRKPT);
@@ -758,11 +762,9 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
 
        default:
                if (user_mode(regs)) {
-#ifdef PRINT_USER_FAULTS
-                       printk(KERN_DEBUG "\nhandle_interruption() pid=%d command='%s'\n",
-                           task_pid_nr(current), current->comm);
-                       show_regs(regs);
-#endif
+                       parisc_printk_ratelimited(0, regs, KERN_DEBUG
+                               "handle_interruption() pid=%d command='%s'\n",
+                               task_pid_nr(current), current->comm);
                        /* SIGBUS, for lack of a better one. */
                        si.si_signo = SIGBUS;
                        si.si_code = BUS_OBJERR;
@@ -779,16 +781,10 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
 
        if (user_mode(regs)) {
            if ((fault_space >> SPACEID_SHIFT) != (regs->sr[7] >> SPACEID_SHIFT)) {
-#ifdef PRINT_USER_FAULTS
-               if (fault_space == 0)
-                       printk(KERN_DEBUG "User Fault on Kernel Space ");
-               else
-                       printk(KERN_DEBUG "User Fault (long pointer) (fault %d) ",
-                              code);
-               printk(KERN_CONT "pid=%d command='%s'\n",
-                      task_pid_nr(current), current->comm);
-               show_regs(regs);
-#endif
+               parisc_printk_ratelimited(0, regs, KERN_DEBUG
+                               "User fault %d on space 0x%08lx, pid=%d command='%s'\n",
+                               code, fault_space,
+                               task_pid_nr(current), current->comm);
                si.si_signo = SIGSEGV;
                si.si_errno = 0;
                si.si_code = SEGV_MAPERR;
index 9d08c71a967ed2e1e86189272369f4b794b8453c..d72197f0ddb863999f1bfb62a2a81c6d60038445 100644 (file)
 #include <asm/uaccess.h>
 #include <asm/traps.h>
 
-#define PRINT_USER_FAULTS /* (turn this on if you want user faults to be */
-                        /*  dumped to the console via printk)          */
-
-
 /* Various important other fields */
 #define bit22set(x)            (x & 0x00000200)
 #define bits23_25set(x)                (x & 0x000001c0)
@@ -34,6 +30,8 @@
 
 DEFINE_PER_CPU(struct exception_data, exception_data);
 
+int show_unhandled_signals = 1;
+
 /*
  * parisc_acctyp(unsigned int inst) --
  *    Given a PA-RISC memory access instruction, determine if the
@@ -173,6 +171,32 @@ int fixup_exception(struct pt_regs *regs)
        return 0;
 }
 
+/*
+ * Print out info about fatal segfaults, if the show_unhandled_signals
+ * sysctl is set:
+ */
+static inline void
+show_signal_msg(struct pt_regs *regs, unsigned long code,
+               unsigned long address, struct task_struct *tsk,
+               struct vm_area_struct *vma)
+{
+       if (!unhandled_signal(tsk, SIGSEGV))
+               return;
+
+       if (!printk_ratelimit())
+               return;
+
+       pr_warn("\n");
+       pr_warn("do_page_fault() command='%s' type=%lu address=0x%08lx",
+           tsk->comm, code, address);
+       print_vma_addr(KERN_CONT " in ", regs->iaoq[0]);
+       if (vma)
+               pr_warn(" vm_start = 0x%08lx, vm_end = 0x%08lx\n",
+                               vma->vm_start, vma->vm_end);
+
+       show_regs(regs);
+}
+
 void do_page_fault(struct pt_regs *regs, unsigned long code,
                              unsigned long address)
 {
@@ -270,16 +294,8 @@ bad_area:
        if (user_mode(regs)) {
                struct siginfo si;
 
-#ifdef PRINT_USER_FAULTS
-               printk(KERN_DEBUG "\n");
-               printk(KERN_DEBUG "do_page_fault() pid=%d command='%s' type=%lu address=0x%08lx\n",
-                   task_pid_nr(tsk), tsk->comm, code, address);
-               if (vma) {
-                       printk(KERN_DEBUG "vm_start = 0x%08lx, vm_end = 0x%08lx\n",
-                                       vma->vm_start, vma->vm_end);
-               }
-               show_regs(regs);
-#endif
+               show_signal_msg(regs, code, address, tsk, vma);
+
                switch (code) {
                case 15:        /* Data TLB miss fault/Data page fault */
                        /* send SIGSEGV when outside of vma */