[PATCH 4/5] stack overflow safe kdump (2.6.16-rc1-i386) - nmi handler and trap vector replacement

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



In the nmi path, we have the problem that both nmi_enter and nmi_exit in
do_nmi (see code below) make extensive use of "current" (which might be
invalid) indirectly (specially through the kernel preemption code).
Create a new nmi trap handler robust against stack overflows and use it
on the crash dump path.

Signed-off-by: Fernando Vazquez <[email protected]>
--- 

diff -urNp linux-2.6.16-rc1/arch/i386/kernel/crash.c linux-2.6.16-rc1-sov/arch/i386/kernel/crash.c
--- linux-2.6.16-rc1/arch/i386/kernel/crash.c	2006-01-25 14:42:43.000000000 +0900
+++ linux-2.6.16-rc1-sov/arch/i386/kernel/crash.c	2006-01-25 15:16:36.000000000 +0900
@@ -22,6 +22,7 @@
 #include <asm/nmi.h>
 #include <asm/hw_irq.h>
 #include <asm/apic.h>
+#include <mach_traps.h>
 #include <mach_ipi.h>
 
 
@@ -104,6 +105,7 @@ static int crash_nmi_callback(struct pt_
 	if (cpu == crashing_cpu)
 		return 1;
 	local_irq_disable();
+	disable_nmi();
 
 	if (!user_mode(regs)) {
 		crash_fixup_ss_esp(&fixed_regs, regs);
@@ -134,8 +136,8 @@ static void nmi_shootdown_cpus(void)
 	unsigned long msecs;
 
 	atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
-	/* Would it be better to replace the trap vector here? */
-	set_nmi_callback(crash_nmi_callback);
+	/* Set the nmi handler appropriately for the crash case */
+	set_crash_nmi_callback(crash_nmi_callback);
 	/* Ensure the new callback function is set before sending
 	 * out the NMI
 	 */
diff -urNp linux-2.6.16-rc1/arch/i386/kernel/entry.S linux-2.6.16-rc1-sov/arch/i386/kernel/entry.S
--- linux-2.6.16-rc1/arch/i386/kernel/entry.S	2006-01-25 14:28:30.000000000 +0900
+++ linux-2.6.16-rc1-sov/arch/i386/kernel/entry.S	2006-01-25 14:44:41.000000000 +0900
@@ -592,6 +592,41 @@ nmi_16bit_stack:
 	.long 1b,iret_exc
 .previous
 
+ENTRY(crash_nmi)
+	pushl %eax
+	movl %ss, %eax
+	cmpw $__ESPFIX_SS, %ax
+	popl %eax
+	je crash_nmi_16bit_stack
+	pushl %eax
+	SAVE_ALL
+	xorl %edx,%edx		# zero error code
+	movl %esp,%eax		# pt_regs pointer
+	call do_crash_nmi
+	jmp restore_all
+crash_nmi_16bit_stack:
+	/* create the pointer to lss back */
+	pushl %ss
+	pushl %esp
+	movzwl %sp, %esp
+	addw $4, (%esp)
+	/* copy the iret frame of 12 bytes */
+        .rept 3
+	pushl 16(%esp)
+	.endr
+	pushl %eax
+	SAVE_ALL
+	FIXUP_ESPFIX_STACK              # %eax == %esp
+	xorl %edx,%edx                  # zero error code
+	call do_crash_nmi
+	RESTORE_REGS
+	lss 12+4(%esp), %esp            # back to 16bit stack
+1:      iret
+.section __ex_table,"a"
+	.align 4
+	.long 1b,iret_exc
+.previous
+
 KPROBE_ENTRY(int3)
 	pushl $-1			# mark this as an int
 	SAVE_ALL
diff -urNp linux-2.6.16-rc1/arch/i386/kernel/traps.c linux-2.6.16-rc1-sov/arch/i386/kernel/traps.c
--- linux-2.6.16-rc1/arch/i386/kernel/traps.c	2006-01-25 14:28:31.000000000 +0900
+++ linux-2.6.16-rc1-sov/arch/i386/kernel/traps.c	2006-01-25 14:44:41.000000000 +0900
@@ -74,6 +74,7 @@ struct desc_struct idt_table[256] __attr
 asmlinkage void divide_error(void);
 asmlinkage void debug(void);
 asmlinkage void nmi(void);
+asmlinkage void crash_nmi(void);
 asmlinkage void int3(void);
 asmlinkage void overflow(void);
 asmlinkage void bounds(void);
@@ -672,23 +673,37 @@ static int dummy_nmi_callback(struct pt_
 }
  
 static nmi_callback_t nmi_callback = dummy_nmi_callback;
- 
-fastcall void do_nmi(struct pt_regs * regs, long error_code)
+
+static fastcall unsigned int __do_nmi(struct pt_regs * regs, long error_code)
 {
 	int cpu;
 
-	nmi_enter();
-
-	cpu = smp_processor_id();
+	cpu = safe_smp_processor_id();
 
 	++nmi_count(cpu);
 
 	if (!rcu_dereference(nmi_callback)(regs, cpu))
 		default_do_nmi(regs);
 
+	return 0;
+}
+
+#define _do_nmi(regs, error_code, nmih) nmih(regs, error_code);
+
+fastcall void do_nmi(struct pt_regs * regs, long error_code)
+{
+	nmi_enter();
+
+	_do_nmi(regs, error_code, __do_nmi);
+
 	nmi_exit();
 }
 
+fastcall void do_crash_nmi(struct pt_regs * regs, long error_code)
+{
+	_do_nmi(regs, error_code, __do_nmi);
+}
+
 void set_nmi_callback(nmi_callback_t callback)
 {
 	rcu_assign_pointer(nmi_callback, callback);
@@ -701,6 +716,17 @@ void unset_nmi_callback(void)
 }
 EXPORT_SYMBOL_GPL(unset_nmi_callback);
 
+void set_crash_nmi_callback(nmi_callback_t callback)
+{
+	/* XXX Do we need to do this atomically? */
+	disable_nmi();
+	unset_nmi_callback();
+	/* Replace the trap vector */
+	set_intr_gate(2,&crash_nmi);
+	rcu_assign_pointer(nmi_callback, callback);
+}
+EXPORT_SYMBOL_GPL(set_crash_nmi_callback);
+
 #ifdef CONFIG_KPROBES
 fastcall void __kprobes do_int3(struct pt_regs *regs, long error_code)
 {
diff -urNp linux-2.6.16-rc1/include/asm-i386/mach-default/mach_traps.h linux-2.6.16-rc1-sov/include/asm-i386/mach-default/mach_traps.h
--- linux-2.6.16-rc1/include/asm-i386/mach-default/mach_traps.h	2006-01-03 12:21:10.000000000 +0900
+++ linux-2.6.16-rc1-sov/include/asm-i386/mach-default/mach_traps.h	2006-01-25 14:44:41.000000000 +0900
@@ -20,6 +20,11 @@ static inline unsigned char get_nmi_reas
 	return inb(0x61);
 }
 
+static inline void disable_nmi(void)
+{
+	outb(0x8f, 0x70);
+}
+
 static inline void reassert_nmi(void)
 {
 	int old_reg = -1;
diff -urNp linux-2.6.16-rc1/include/asm-i386/nmi.h linux-2.6.16-rc1-sov/include/asm-i386/nmi.h
--- linux-2.6.16-rc1/include/asm-i386/nmi.h	2006-01-03 12:21:10.000000000 +0900
+++ linux-2.6.16-rc1-sov/include/asm-i386/nmi.h	2006-01-25 14:44:41.000000000 +0900
@@ -17,6 +17,7 @@ typedef int (*nmi_callback_t)(struct pt_
  * set. Return 1 if the NMI was handled.
  */
 void set_nmi_callback(nmi_callback_t callback);
+void set_crash_nmi_callback(nmi_callback_t callback);
  
 /** 
  * unset_nmi_callback


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux