(re-xmit): kprobes for i386
PATCH 1/1 (re-xmit): kprobes for i386
Name: Kprobes for i386
Author: Vamsi Krishna S
Status: Tested on 2.5.31 SMP
D: This patch allows trapping at almost any kernel address, useful for
D: various kernel-hacking tasks, and building on for more
D: infrastructure. This patch is x86 only, but other archs can add
D: support as required.
arch/i386/Config.help +6 -0
@@ -967,3 +967,9 @@ CONFIG_SOFTWARE_SUSPEND absence of features. For more information take a look at Documentation/swsusp.txt. + +CONFIG_KPROBES + Kprobes allows you to trap at almost any kernel address, using + register_kprobe(), and providing a callback function. This is useful + for kernel debugging, non-intrusive instrumentation and testing. If + in doubt, say "N".
arch/i386/config.in +1 -0
@@ -416,6 +416,7 @@ if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; th if [ "$CONFIG_HIGHMEM" = "y" ]; then bool ' Highmem debugging' CONFIG_DEBUG_HIGHMEM fi + bool ' Probes' CONFIG_KPROBES fi endmenu
arch/i386/kernel/Makefile +1 -0
@@ -26,6 +26,7 @@ obj-$(CONFIG_SMP) += smp.o smpboot.o tr obj-$(CONFIG_X86_LOCAL_APIC) += mpparse.o apic.o nmi.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend.o +obj-$(CONFIG_KPROBES) += kprobes.o ifdef CONFIG_VISWS obj-y += setup-visws.o obj-$(CONFIG_X86_VISWS_APIC) += visws_apic.o
arch/i386/kernel/entry.S +18 -4
@@ -430,9 +430,16 @@ device_not_available_emulate: jmp ret_from_exception ENTRY(debug) + pushl %eax + SAVE_ALL + movl %esp,%edx pushl $0 - pushl $do_debug - jmp error_code + pushl %edx + call do_debug + addl $8,%esp + testl %eax,%eax + jnz restore_all + jmp ret_from_exception ENTRY(nmi) pushl %eax
@@ -445,9 +452,16 @@ ENTRY(nmi) RESTORE_ALL ENTRY(int3) + pushl %eax + SAVE_ALL + movl %esp,%edx pushl $0 - pushl $do_int3 - jmp error_code + pushl %edx + call do_int3 + addl $8,%esp + cmpl $0,%eax + jnz restore_all + jmp ret_from_exception ENTRY(overflow) pushl $0
arch/i386/kernel/kprobes.c +158 -0
158 lines not on this page · raw message
arch/i386/kernel/traps.c +31 -7
@@ -24,6 +24,7 @@#include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/highmem.h> +#include <linux/kprobes.h> #ifdef CONFIG_EISA #include <linux/ioport.h>
@@ -326,6 +327,8 @@ static void inline do_trap(int trapnr, i panic("do_trap: can't hit this"); }#endif + if (kprobe_running() && kprobe_fault_handler(regs, trapnr)) +return ;if (!(regs->xcs &3 )) goto kernel_trap;
@@ -392,7 +395,6 @@ asmlinkage void do_##name(struct pt_regs } DO_VM86_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->eip) -DO_VM86_ERROR( 3, SIGTRAP, "int3", int3) DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow) DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds)DO_ERROR_INFO (6 ,SIGILL ,"invalid operand" , invalid_op,ILL_ILLOPN , regs->eip)
@@ -408,6 +410,9 @@ asmlinkage void do_general_protection(st {if (regs->eflags &VM_MASK ) goto gp_in_vm86; + +if (kprobe_running () &&kprobe_fault_handler (regs,13 )) +return ;if (!(regs->xcs &3 )) goto gp_in_kernel;
@@ -508,6 +513,17 @@ asmlinkage void do_nmi(struct pt_regs *inb (0x71 );/* dummy */ } +asmlinkage int do_int3(struct pt_regs *regs, long error_code) +{ +if (kprobe_handler (regs)) +return 1 ; +/* This is an interrupt gate, because kprobes wants interrupts +disabled. Normal trap handlers don't. */ +restore_interrupts (regs); +do_trap (3 ,SIGTRAP ,"int3" ,1 , regs, error_code,NULL ); +return 0 ; +} +/* * Our handling of the processor debug registers is non-trivial. * We do not clear them on entry and exit from the kernel. Therefore
@@ -530,7 +546,7 @@ asmlinkage void do_nmi(struct pt_regs ** find every occurrence of the TF bit that could be saved away even * by user code) */ -asmlinkage void do_debug(struct pt_regs * regs, long error_code) +asmlinkage int do_debug(struct pt_regs * regs, long error_code) {unsigned int condition;struct task_struct *tsk = current;
@@ -538,6 +554,12 @@ asmlinkage void do_debug(struct pt_regs __asm__ __volatile__("movl %%db6,%0" :"=r" (condition)); +if (post_kprobe_handler (regs)) +return 1 ; + +/* Interrupts not disabled for normal trap handling. */ +restore_interrupts (regs); +/* Mask out spurious debug traps due to lazy DR7 setting */ if (condition & (DR_TRAP0 |DR_TRAP1 |DR_TRAP2 |DR_TRAP3 )) {if (!tsk->thread.debugreg[7 ])
@@ -588,15 +610,15 @@ clear_dr7: __asm__("movl %0,%%db7" :/* no output */ :"r" (0 )); -return ; +return 0 ; debug_vm86:handle_vm86_trap ((struct kernel_vm86_regs *) regs, error_code,1 ); -return ; +return 0 ; clear_TF: regs->eflags &= ~TF_MASK ; -return ; +return 0 ; }/*
@@ -760,6 +782,8 @@ asmlinkage void math_state_restore(strucstruct task_struct *tsk = current;clts ();/* Allow maths ops (or we recurse) */ +if (kprobe_running () &&kprobe_fault_handler (®s,7 )) +return ;if (!tsk->used_math)init_fpu (tsk);restore_fpu (tsk);
@@ -943,9 +967,9 @@ void __init trap_init(void)#endif set_trap_gate (0 ,÷_error); -set_trap_gate (1 ,&debug); +_set_gate (idt_table+1 ,14 ,3 ,&debug);/* debug trap for kprobes */ set_intr_gate (2 ,&nmi); -set_system_gate (3 ,&int3);/* int3-5 can be called from all */ +_set_gate (idt_table+3 ,14 ,3 ,&int3);/* int3-5 can be called from all */ set_system_gate (4 ,&overflow);set_system_gate (5 ,&bounds);set_trap_gate (6 ,&invalid_op);
arch/i386/mm/fault.c +4 -0
@@ -19,6 +19,7 @@#include <linux/init.h> #include <linux/tty.h> #include <linux/vt_kern.h> /* For unblank_screen() */ +#include <linux/kprobes.h> #include <asm/system.h> #include <asm/uaccess.h>
@@ -155,6 +156,9 @@ asmlinkage void do_page_fault(struct pt_/* get the address */ __asm__("movl %%cr2,%0" :"=r" (address)); +if (kprobe_running () &&kprobe_fault_handler (regs,14 )) +return ; +/* It's safe to allow irq's after cr2 has been saved */ if (regs->eflags &X86_EFLAGS_IF )local_irq_enable ();
include/asm-i386/kprobes.h +34 -0
@@ -0,0 +1,34 @@ +#ifndef _ASM_KPROBES_H +#define _ASM_KPROBES_H +/* +* Dynamic Probes (kprobes) support +* Vamsi Krishna S <[email protected]>, July, 2002 +* Mailing list: [email protected] +*/ +#include <linux/types.h> +#include <linux/ptrace.h> + +struct pt_regs ; + +typedef u8 kprobe_opcode_t ; +#define BREAKPOINT_INSTRUCTION 0xcc + +/* trap3/1 are intr gates for kprobes. So, restore the status of IF, +* if necessary, before executing the original int3/1 (trap) handler. +*/ +static inline void restore_interrupts (struct pt_regs *regs) +{ +if (regs->eflags &IF_MASK ) + __asm__ __volatile__ ("sti" ); +} + +#ifdef CONFIG_KPROBES +extern int kprobe_fault_handler (struct pt_regs *regs,int trapnr); +extern int post_kprobe_handler (struct pt_regs *regs); +extern int kprobe_handler (struct pt_regs *regs); +#else /* !CONFIG_KPROBES */ +static inline int kprobe_fault_handler (struct pt_regs *regs,int trapnr) {return 0 ; } +static inline int post_kprobe_handler (struct pt_regs *regs) {return 0 ; } +static inline int kprobe_handler (struct pt_regs *regs) {return 0 ; } +#endif +#endif /* _ASM_KPROBES_H */
include/linux/kprobes.h +54 -0
54 lines not on this page · raw message
kernel/Makefile +2 -1
@@ -10,7 +10,7 @@ O_TARGET := kernel.o export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o \ - printk.o platform.o suspend.o dma.o + printk.o platform.o suspend.o dma.o kprobes.o obj-y = sched.o fork.o exec_domain.o panic.o printk.o \ module.o exit.o itimer.o time.o softirq.o resource.o \
@@ -24,6 +24,7 @@ obj-$(CONFIG_MODULES) += ksyms.o obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend.o +obj-$(CONFIG_KPROBES) += kprobes.o ifneq ($(CONFIG_IA64),y) # According to Alan Modra <[email protected]>, the -fno-omit-frame-pointer is
kernel/kprobes.c +94 -0
94 lines not on this page · raw message
--
Anyone who quotes me in their sig is an idiot. -- Rusty Russell.
Quoted 1 line · Rusty Russell
+ bool ' Probes'
CONFIG_KPROBES
A little more descriptive options sounds like a good idea.. ;)
In message <[email protected]> you write:
Quoted 4 lines · Rusty Russell
Quoted 1 line
+ bool ' Probes'
CONFIG_KPROBESA little more descriptive options sounds like a good idea.. ;)
I couldn't really think of a better one. Kernel Probes is kinda redundant. It's under kernel hacking, and it has a help option.
Rusty.
--
Anyone who quotes me in their sig is an idiot. -- Rusty Russell.
arch/i386/kernel/entry.S device_not_available_emulate: · line 442
Quoted 13 lines
ENTRY(debug)
arch/i386/kernel/entry.S +9
-2+ pushl %eax + SAVE_ALL + movl %esp,%edx pushl $0 - pushl $do_debug - jmp error_code + pushl %edx + call do_debug + addl $8,%esp + testl %eax,%eax + jnz restore_all + jmp ret_from_exception
How about checking %cs in assembly and branching off for the kernel-mode case?
Something like this:
ENTRY(debug)
testl $0x3, 4(%esp) jz handle_kernel_mode_debug
arch/i386/kernel/kprobes.c line 124
Quoted 8 lines
arch/i386/kernel/kprobes.c +8
-0+/* +* We singlestepped with interrupts disabled. So, the result on +* the stack would be incorrect for "pushfl" instruction. +*/ +if (current_kprobe->opcode ==0x9c ) {/* pushfl */ + regs->esp &= ~(TF_MASK |IF_MASK ); + regs->esp |= kprobe_old_eflags; + }
This masks the stack pointer. It should mask the value pointer at by the stack pointer.
arch/i386/kernel/traps.c static void inline do_trap(int trapnr, i · line 334
Quoted 4 lines
arch/i386/kernel/traps.c +2
-0+if (kprobe_running () &&kprobe_fault_handler (regs, trapnr)) +return ;if (!(regs->xcs &3 )) goto kernel_trap;
The kprobe check should be after the kernel_trap label.
arch/i386/kernel/traps.c asmlinkage void do_general_protection(st · line 418
Quoted 5 lines
arch/i386/kernel/traps.c +2
-0+if (kprobe_running () &&kprobe_fault_handler (regs,13 )) +return ;if (!(regs->xcs &3 )) goto gp_in_kernel;
Same here.
kernel. Therefore
Review
Quoted 2 lines
(quoted diff) +1
-1- return; + return 0;
Branching off in assembly would avoid having a return value in do_debug.
arch/i386/kernel/traps.c asmlinkage void math_state_restore(struc · line 786
Quoted 2 lines
arch/i386/kernel/traps.c +2
-0+if (kprobe_running () &&kprobe_fault_handler (®s,7 )) +return ;
kprobe_running should be inline.
arch/i386/mm/fault.c asmlinkage void do_page_fault(struct pt_ · line 161
Quoted 3 lines
arch/i386/mm/fault.c +3
-0+if (kprobe_running () &&kprobe_fault_handler (regs,14 )) +return ; +
Same here.
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 8 lines
Quoted 5 lines
Something like this:
ENTRY(debug)testl $0x3, 4(%esp) jz handle_kernel_mode_debugThis check is insufficient, it can go the wrong way if the interrupted taks is in vm 86 mode (and open a big security hole, I believe).
Right.
This test from ret_from_intr could be used instead:
push %es
push %ds
push %eax
movl 8(%esp), %eax
movb 4(%esp), %al
testl $(VM_MASK | 3), %eax
jz handle_kernel_mode
There is however a potential register stall for the eax read after the al write.
BTW, vm86 has the VM flag set so the comment in ret_from_intr is wrong. The code seems to be right though.
Quoted 7 lines · Luca Barbieri
How about checking %cs in assembly and branching off for the kernel-mode case?
Something like this:
ENTRY(debug)testl $0x3, 4(%esp) jz handle_kernel_mode_debug
That's not correct, you can have the low bits of CS clear even from user mode if the thing is in vm86 mode.
See the full test at the top of "ret_from_intr" (the "mix EFLAGS and CS" thing - it's a bit funky).
Linus
In message <1029844464.1745.49.camel@ldb> you write:
Quoted 10 lines
Review
Quoted 8 lines
(quoted diff) +8
-0+ /* + * We singlestepped with interrupts disabled. So, the result on + * the stack would be incorrect for "pushfl" instruction. + */ + if (current_kprobe->opcode == 0x9c) { /* pushfl */ + regs->esp &= ~(TF_MASK | IF_MASK); + regs->esp |= kprobe_old_eflags; + }This masks the stack pointer. It should mask the value pointer at by the stack pointer.
Good catch! I've changed it to:
if (current_kprobe->opcode == 0x9c) { /* pushfl */
unsigned long *stacktop = (unsigned long *)regs->esp;
*stacktop &= ~(TF_MASK | IF_MASK);
*stacktop |= kprobe_old_eflags;
}Quoted 5 lines
Review
Quoted 4 lines
(quoted diff) +2
-0+ if (kprobe_running() && kprobe_fault_handler(regs, trapnr)) + return; if (!(regs->xcs & 3)) goto kernel_trap;The kprobe check should be after the
kernel_traplabel.
No. The entire *point* of being able to register a kprobe fault handler is to be able to handle any kernel faults yourself if you want to.
Quoted 6 lines
Review
Quoted 5 lines
(quoted diff) +2
-0+ if (kprobe_running() && kprobe_fault_handler(regs, 13)) + return; if (!(regs->xcs & 3)) goto gp_in_kernel;Same here.
Still no.
Quoted 3 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ if (kprobe_running() && kprobe_fault_handler(®s, 7)) + return;
kprobe_runningshould be inline.
<shrug> It is, of course, for !CONFIG_KPROBES. I really didn't want to expose the internals of kprobe_running, since it's subtle.
Still, to quieten the pedants, here is the patch.
Thanks,
Rusty.
--
Anyone who quotes me in their sig is an idiot. -- Rusty Russell.
Name: Kprobes for i386
Author: Vamsi Krishna S
Status: Tested on 2.5.31 SMP
D: This patch allows trapping at almost any kernel address, useful for
D: various kernel-hacking tasks, and building on for more
D: infrastructure. This patch is x86 only, but other archs can add
D: support as required.
arch/i386/Config.help +6 -0
@@ -967,3 +967,9 @@ CONFIG_SOFTWARE_SUSPEND absence of features. For more information take a look at Documentation/swsusp.txt. + +CONFIG_KPROBES + Kprobes allows you to trap at almost any kernel address, using + register_kprobe(), and providing a callback function. This is useful + for kernel debugging, non-intrusive instrumentation and testing. If + in doubt, say "N".
arch/i386/config.in +1 -0
@@ -416,6 +416,7 @@ if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; th if [ "$CONFIG_HIGHMEM" = "y" ]; then bool ' Highmem debugging' CONFIG_DEBUG_HIGHMEM fi + bool ' Kprobes' CONFIG_KPROBES fi endmenu
arch/i386/kernel/Makefile +1 -0
@@ -26,6 +26,7 @@ obj-$(CONFIG_SMP) += smp.o smpboot.o tr obj-$(CONFIG_X86_LOCAL_APIC) += mpparse.o apic.o nmi.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend.o +obj-$(CONFIG_KPROBES) += kprobes.o ifdef CONFIG_VISWS obj-y += setup-visws.o obj-$(CONFIG_X86_VISWS_APIC) += visws_apic.o
arch/i386/kernel/entry.S +18 -4
@@ -430,9 +430,16 @@ device_not_available_emulate: jmp ret_from_exception ENTRY(debug) + pushl %eax + SAVE_ALL + movl %esp,%edx pushl $0 - pushl $do_debug - jmp error_code + pushl %edx + call do_debug + addl $8,%esp + testl %eax,%eax + jnz restore_all + jmp ret_from_exception ENTRY(nmi) pushl %eax
@@ -445,9 +452,16 @@ ENTRY(nmi) RESTORE_ALL ENTRY(int3) + pushl %eax + SAVE_ALL + movl %esp,%edx pushl $0 - pushl $do_int3 - jmp error_code + pushl %edx + call do_int3 + addl $8,%esp + cmpl $0,%eax + jnz restore_all + jmp ret_from_exception ENTRY(overflow) pushl $0
arch/i386/kernel/kprobes.c +159 -0
159 lines not on this page · raw message
arch/i386/kernel/traps.c +31 -7
@@ -24,6 +24,7 @@#include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/highmem.h> +#include <linux/kprobes.h> #ifdef CONFIG_EISA #include <linux/ioport.h>
@@ -326,6 +327,8 @@ static void inline do_trap(int trapnr, i panic("do_trap: can't hit this"); }#endif + if (kprobe_running() && kprobe_fault_handler(regs, trapnr)) +return ;if (!(regs->xcs &3 )) goto kernel_trap;
@@ -392,7 +395,6 @@ asmlinkage void do_##name(struct pt_regs } DO_VM86_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->eip) -DO_VM86_ERROR( 3, SIGTRAP, "int3", int3) DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow) DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds)DO_ERROR_INFO (6 ,SIGILL ,"invalid operand" , invalid_op,ILL_ILLOPN , regs->eip)
@@ -408,6 +410,9 @@ asmlinkage void do_general_protection(st {if (regs->eflags &VM_MASK ) goto gp_in_vm86; + +if (kprobe_running () &&kprobe_fault_handler (regs,13 )) +return ;if (!(regs->xcs &3 )) goto gp_in_kernel;
@@ -508,6 +513,17 @@ asmlinkage void do_nmi(struct pt_regs *inb (0x71 );/* dummy */ } +asmlinkage int do_int3(struct pt_regs *regs, long error_code) +{ +if (kprobe_handler (regs)) +return 1 ; +/* This is an interrupt gate, because kprobes wants interrupts +disabled. Normal trap handlers don't. */ +restore_interrupts (regs); +do_trap (3 ,SIGTRAP ,"int3" ,1 , regs, error_code,NULL ); +return 0 ; +} +/* * Our handling of the processor debug registers is non-trivial. * We do not clear them on entry and exit from the kernel. Therefore
@@ -530,7 +546,7 @@ asmlinkage void do_nmi(struct pt_regs ** find every occurrence of the TF bit that could be saved away even * by user code) */ -asmlinkage void do_debug(struct pt_regs * regs, long error_code) +asmlinkage int do_debug(struct pt_regs * regs, long error_code) {unsigned int condition;struct task_struct *tsk = current;
@@ -538,6 +554,12 @@ asmlinkage void do_debug(struct pt_regs __asm__ __volatile__("movl %%db6,%0" :"=r" (condition)); +if (post_kprobe_handler (regs)) +return 1 ; + +/* Interrupts not disabled for normal trap handling. */ +restore_interrupts (regs); +/* Mask out spurious debug traps due to lazy DR7 setting */ if (condition & (DR_TRAP0 |DR_TRAP1 |DR_TRAP2 |DR_TRAP3 )) {if (!tsk->thread.debugreg[7 ])
@@ -588,15 +610,15 @@ clear_dr7: __asm__("movl %0,%%db7" :/* no output */ :"r" (0 )); -return ; +return 0 ; debug_vm86:handle_vm86_trap ((struct kernel_vm86_regs *) regs, error_code,1 ); -return ; +return 0 ; clear_TF: regs->eflags &= ~TF_MASK ; -return ; +return 0 ; }/*
@@ -760,6 +782,8 @@ asmlinkage void math_state_restore(strucstruct task_struct *tsk = current;clts ();/* Allow maths ops (or we recurse) */ +if (kprobe_running () &&kprobe_fault_handler (®s,7 )) +return ;if (!tsk->used_math)init_fpu (tsk);restore_fpu (tsk);
@@ -943,9 +967,9 @@ void __init trap_init(void)#endif set_trap_gate (0 ,÷_error); -set_trap_gate (1 ,&debug); +_set_gate (idt_table+1 ,14 ,3 ,&debug);/* debug trap for kprobes */ set_intr_gate (2 ,&nmi); -set_system_gate (3 ,&int3);/* int3-5 can be called from all */ +_set_gate (idt_table+3 ,14 ,3 ,&int3);/* int3-5 can be called from all */ set_system_gate (4 ,&overflow);set_system_gate (5 ,&bounds);set_trap_gate (6 ,&invalid_op);
arch/i386/mm/fault.c +4 -0
@@ -19,6 +19,7 @@#include <linux/init.h> #include <linux/tty.h> #include <linux/vt_kern.h> /* For unblank_screen() */ +#include <linux/kprobes.h> #include <asm/system.h> #include <asm/uaccess.h>
@@ -155,6 +156,9 @@ asmlinkage void do_page_fault(struct pt_/* get the address */ __asm__("movl %%cr2,%0" :"=r" (address)); +if (kprobe_running () &&kprobe_fault_handler (regs,14 )) +return ; +/* It's safe to allow irq's after cr2 has been saved */ if (regs->eflags &X86_EFLAGS_IF )local_irq_enable ();
include/asm-i386/kprobes.h +34 -0
@@ -0,0 +1,34 @@ +#ifndef _ASM_KPROBES_H +#define _ASM_KPROBES_H +/* +* Dynamic Probes (kprobes) support +* Vamsi Krishna S <[email protected]>, July, 2002 +* Mailing list: [email protected] +*/ +#include <linux/types.h> +#include <linux/ptrace.h> + +struct pt_regs ; + +typedef u8 kprobe_opcode_t ; +#define BREAKPOINT_INSTRUCTION 0xcc + +/* trap3/1 are intr gates for kprobes. So, restore the status of IF, +* if necessary, before executing the original int3/1 (trap) handler. +*/ +static inline void restore_interrupts (struct pt_regs *regs) +{ +if (regs->eflags &IF_MASK ) + __asm__ __volatile__ ("sti" ); +} + +#ifdef CONFIG_KPROBES +extern int kprobe_fault_handler (struct pt_regs *regs,int trapnr); +extern int post_kprobe_handler (struct pt_regs *regs); +extern int kprobe_handler (struct pt_regs *regs); +#else /* !CONFIG_KPROBES */ +static inline int kprobe_fault_handler (struct pt_regs *regs,int trapnr) {return 0 ; } +static inline int post_kprobe_handler (struct pt_regs *regs) {return 0 ; } +static inline int kprobe_handler (struct pt_regs *regs) {return 0 ; } +#endif +#endif /* _ASM_KPROBES_H */
include/linux/kprobes.h +58 -0
58 lines not on this page · raw message
kernel/Makefile +2 -1
@@ -10,7 +10,7 @@ O_TARGET := kernel.o export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o \ - printk.o platform.o suspend.o dma.o + printk.o platform.o suspend.o dma.o kprobes.o obj-y = sched.o fork.o exec_domain.o panic.o printk.o \ module.o exit.o itimer.o time.o softirq.o resource.o \
@@ -24,6 +24,7 @@ obj-$(CONFIG_MODULES) += ksyms.o obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend.o +obj-$(CONFIG_KPROBES) += kprobes.o ifneq ($(CONFIG_IA64),y) # According to Alan Modra <[email protected]>, the -fno-omit-frame-pointer is
kernel/kprobes.c +89 -0
89 lines not on this page · raw message
Quoted 9 lines
Quoted 5 lines
arch/i386/kernel/traps.c static void inline do_trap(int trapnr, i · line 334
Quoted 4 lines
arch/i386/kernel/traps.c +2
-0+if (kprobe_running () &&kprobe_fault_handler (regs, trapnr)) +return ;if (!(regs->xcs &3 )) goto kernel_trap;The kprobe check should be after the
kernel_traplabel.No. The entire *point* of being able to register a kprobe fault handler is to be able to handle any kernel faults yourself if you want to.
It seems you have misunderstood my point.
My idea is that since kprobes are only used for kernel mode address, we should move the kprobe check in the code that executes after we check that the fault is happening in kernel mode.
Soemthing like this:
if (!(regs->xcs & 3))
goto kernel_trap;
[...]
kernel_trap:
if (kprobe_running() && kprobe_fault_handler(regs, trapnr)) return;
In message <1029893377.24300.162.camel@ldb> you write:
Quoted 11 lines
--=-UkzkmpwPZ3tahG697mpi
Content-Type: text/plain
Content-Transfer-Encoding: 7bitQuoted 6 lines
Quoted 5 lines
arch/i386/kernel/traps.c static void inline do_trap(int trapnr, i · line 334
Quoted 4 lines
arch/i386/kernel/traps.c +2
-0+if (kprobe_running () &&kprobe_fault_handler (regs, trapnr)) +return ;if (!(regs->xcs &3 )) goto kernel_trap;The kprobe check should be after the
kernel_traplabel.
Quoted 7 lines
Quoted 3 lines
No. The entire *point* of being able to register a kprobe fault handler is to be able to handle any kernel faults yourself if you want to.
It seems you have misunderstood my point.
My idea is that since kprobes are only used for kernel mode address, we should move the kprobe check in the code that executes after we check that the fault is happening in kernel mode.
Ah, I see. That's true at the moment, but there's an (future) extension that covers userspace traps as well, which is why it was done this way.
Hope that clarifies,
Rusty.
--
Anyone who quotes me in their sig is an idiot. -- Rusty Russell.
Quoted 8 lines · Rusty Russell
In message <1029844464.1745.49.camel@ldb> you write:
Quoted 6 lines
arch/i386/kernel/kprobes.c line 125
Quoted 4 lines
arch/i386/kernel/kprobes.c +4
-0+if (current_kprobe->opcode ==0x9c ) {/* pushfl */ + regs->esp &= ~(TF_MASK |IF_MASK ); + regs->esp |= kprobe_old_eflags; + }This masks the stack pointer. It should mask the value pointer at by the stack pointer.
Quoted 7 lines
Good catch! I've changed it to:
if (current_kprobe->opcode == 0x9c) { /* pushfl */ unsigned long *stacktop = (unsigned long *)regs->esp; *stacktop &= ~(TF_MASK | IF_MASK); *stacktop |= kprobe_old_eflags; }
No. Don't do this. When a trap occurs in kernel space, the processor does not save ss and esp on the stack. So, inside an exception handler, regs->esp is actually the top of the stack when the exception occurs in kernel space, not a pointer to it. This change was tested to work fine.
I reverted your change and added a comment to explain what's going on here. I have also made another fix: trap1 and trap3 now set error_code to -1 to mark that these are exceptions. This makes the Borland debugger Kylix work again on a kprobes kernel. Here is the incremental patch on top of your latest.
Thanks,
Vamsi.
Signature · 58 lines
Vamsi Krishna S. Linux Technology Center, IBM Software Lab, Bangalore. Ph: +91 80 5044959 Internet: [email protected] -- diff -u 31-dp/arch/i386/kernel/entry.S 31-dp/arch/i386/kernel/entry.S --- 31-dp/arch/i386/kernel/entry.S 2002-08-21 11:20:18.000000000 +0530 +++ 31-dp/arch/i386/kernel/entry.S 2002-08-21 12:10:30.000000000 +0530 @@ -430,7 +430,7 @@ jmp ret_from_exception ENTRY(debug) - pushl %eax + pushl $-1 # mark this as an int SAVE_ALL movl %esp,%edx pushl $0 @@ -452,7 +452,7 @@ RESTORE_ALL ENTRY(int3) - pushl %eax + pushl $-1 # mark this as an int SAVE_ALL movl %esp,%edx pushl $0 diff -u 31-dp/arch/i386/kernel/kprobes.c 31-dp/arch/i386/kernel/kprobes.c --- 31-dp/arch/i386/kernel/kprobes.c 2002-08-21 11:20:18.000000000 +0530 +++ 31-dp/arch/i386/kernel/kprobes.c 2002-08-21 12:09:50.000000000 +0530 @@ -117,11 +117,12 @@ /* * We singlestepped with interrupts disabled. So, the result on * the stack would be incorrect for "pushfl" instruction. + * Note that regs->esp is actually the top of the stack when the + * trap occurs in kernel space. */ if (current_kprobe->opcode == 0x9c) { /* pushfl */ - unsigned long *stacktop = (unsigned long *)regs->esp; - *stacktop &= ~(TF_MASK | IF_MASK); - *stacktop |= kprobe_old_eflags; + regs->esp &= ~(TF_MASK | IF_MASK); + regs->esp |= kprobe_old_eflags; } rearm_kprobe(current_kprobe, regs); diff -u 31-dp/include/linux/kprobes.h 31-dp/include/linux/kprobes.h --- 31-dp/include/linux/kprobes.h 2002-08-21 11:20:18.000000000 +0530 +++ 31-dp/include/linux/kprobes.h 2002-08-21 12:30:24.000000000 +0530 @@ -2,6 +2,8 @@ #define _LINUX_KPROBES_H #include <linux/config.h> #include <linux/list.h> +#include <linux/notifier.h> +#include <linux/smp.h> #include <asm/kprobes.h> struct kprobe;
In message <[email protected]> you write:
Quoted 1 line
No. Don't do this. When a trap occurs in kernel space, the
Sorry Vamsi: I'll have more faith in your code next time, I promise.
Linus please apply merged patch.
Rusty.
--
Anyone who quotes me in their sig is an idiot. -- Rusty Russell.
arch/i386/Config.help +6 -0
@@ -967,3 +967,9 @@ CONFIG_SOFTWARE_SUSPEND absence of features. For more information take a look at Documentation/swsusp.txt. + +CONFIG_KPROBES + Kprobes allows you to trap at almost any kernel address, using + register_kprobe(), and providing a callback function. This is useful + for kernel debugging, non-intrusive instrumentation and testing. If + in doubt, say "N".
arch/i386/config.in +1 -0
@@ -416,6 +416,7 @@ if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; th if [ "$CONFIG_HIGHMEM" = "y" ]; then bool ' Highmem debugging' CONFIG_DEBUG_HIGHMEM fi + bool ' Kprobes' CONFIG_KPROBES fi endmenu
arch/i386/kernel/Makefile +1 -0
@@ -26,6 +26,7 @@ obj-$(CONFIG_SMP) += smp.o smpboot.o tr obj-$(CONFIG_X86_LOCAL_APIC) += mpparse.o apic.o nmi.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend.o +obj-$(CONFIG_KPROBES) += kprobes.o ifdef CONFIG_VISWS obj-y += setup-visws.o obj-$(CONFIG_X86_VISWS_APIC) += visws_apic.o
arch/i386/kernel/entry.S +18 -4
@@ -430,9 +430,16 @@ device_not_available_emulate: jmp ret_from_exception ENTRY(debug) + pushl %eax + SAVE_ALL + movl %esp,%edx pushl $0 - pushl $do_debug - jmp error_code + pushl %edx + call do_debug + addl $8,%esp + testl %eax,%eax + jnz restore_all + jmp ret_from_exception ENTRY(nmi) pushl %eax
@@ -445,9 +452,16 @@ ENTRY(nmi) RESTORE_ALL ENTRY(int3) + pushl %eax + SAVE_ALL + movl %esp,%edx pushl $0 - pushl $do_int3 - jmp error_code + pushl %edx + call do_int3 + addl $8,%esp + cmpl $0,%eax + jnz restore_all + jmp ret_from_exception ENTRY(overflow) pushl $0
arch/i386/kernel/kprobes.c +159 -0
159 lines not on this page · raw message
arch/i386/kernel/traps.c +31 -7
@@ -24,6 +24,7 @@#include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/highmem.h> +#include <linux/kprobes.h> #ifdef CONFIG_EISA #include <linux/ioport.h>
@@ -326,6 +327,8 @@ static void inline do_trap(int trapnr, i panic("do_trap: can't hit this"); }#endif + if (kprobe_running() && kprobe_fault_handler(regs, trapnr)) +return ;if (!(regs->xcs &3 )) goto kernel_trap;
@@ -392,7 +395,6 @@ asmlinkage void do_##name(struct pt_regs } DO_VM86_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->eip) -DO_VM86_ERROR( 3, SIGTRAP, "int3", int3) DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow) DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds)DO_ERROR_INFO (6 ,SIGILL ,"invalid operand" , invalid_op,ILL_ILLOPN , regs->eip)
@@ -408,6 +410,9 @@ asmlinkage void do_general_protection(st {if (regs->eflags &VM_MASK ) goto gp_in_vm86; + +if (kprobe_running () &&kprobe_fault_handler (regs,13 )) +return ;if (!(regs->xcs &3 )) goto gp_in_kernel;
@@ -508,6 +513,17 @@ asmlinkage void do_nmi(struct pt_regs *inb (0x71 );/* dummy */ } +asmlinkage int do_int3(struct pt_regs *regs, long error_code) +{ +if (kprobe_handler (regs)) +return 1 ; +/* This is an interrupt gate, because kprobes wants interrupts +disabled. Normal trap handlers don't. */ +restore_interrupts (regs); +do_trap (3 ,SIGTRAP ,"int3" ,1 , regs, error_code,NULL ); +return 0 ; +} +/* * Our handling of the processor debug registers is non-trivial. * We do not clear them on entry and exit from the kernel. Therefore
@@ -530,7 +546,7 @@ asmlinkage void do_nmi(struct pt_regs ** find every occurrence of the TF bit that could be saved away even * by user code) */ -asmlinkage void do_debug(struct pt_regs * regs, long error_code) +asmlinkage int do_debug(struct pt_regs * regs, long error_code) {unsigned int condition;struct task_struct *tsk = current;
@@ -538,6 +554,12 @@ asmlinkage void do_debug(struct pt_regs __asm__ __volatile__("movl %%db6,%0" :"=r" (condition)); +if (post_kprobe_handler (regs)) +return 1 ; + +/* Interrupts not disabled for normal trap handling. */ +restore_interrupts (regs); +/* Mask out spurious debug traps due to lazy DR7 setting */ if (condition & (DR_TRAP0 |DR_TRAP1 |DR_TRAP2 |DR_TRAP3 )) {if (!tsk->thread.debugreg[7 ])
@@ -588,15 +610,15 @@ clear_dr7: __asm__("movl %0,%%db7" :/* no output */ :"r" (0 )); -return ; +return 0 ; debug_vm86:handle_vm86_trap ((struct kernel_vm86_regs *) regs, error_code,1 ); -return ; +return 0 ; clear_TF: regs->eflags &= ~TF_MASK ; -return ; +return 0 ; }/*
@@ -760,6 +782,8 @@ asmlinkage void math_state_restore(strucstruct task_struct *tsk = current;clts ();/* Allow maths ops (or we recurse) */ +if (kprobe_running () &&kprobe_fault_handler (®s,7 )) +return ;if (!tsk->used_math)init_fpu (tsk);restore_fpu (tsk);
@@ -943,9 +967,9 @@ void __init trap_init(void)#endif set_trap_gate (0 ,÷_error); -set_trap_gate (1 ,&debug); +_set_gate (idt_table+1 ,14 ,3 ,&debug);/* debug trap for kprobes */ set_intr_gate (2 ,&nmi); -set_system_gate (3 ,&int3);/* int3-5 can be called from all */ +_set_gate (idt_table+3 ,14 ,3 ,&int3);/* int3-5 can be called from all */ set_system_gate (4 ,&overflow);set_system_gate (5 ,&bounds);set_trap_gate (6 ,&invalid_op);
arch/i386/mm/fault.c +4 -0
@@ -19,6 +19,7 @@#include <linux/init.h> #include <linux/tty.h> #include <linux/vt_kern.h> /* For unblank_screen() */ +#include <linux/kprobes.h> #include <asm/system.h> #include <asm/uaccess.h>
@@ -155,6 +156,9 @@ asmlinkage void do_page_fault(struct pt_/* get the address */ __asm__("movl %%cr2,%0" :"=r" (address)); +if (kprobe_running () &&kprobe_fault_handler (regs,14 )) +return ; +/* It's safe to allow irq's after cr2 has been saved */ if (regs->eflags &X86_EFLAGS_IF )local_irq_enable ();
include/asm-i386/kprobes.h +34 -0
@@ -0,0 +1,34 @@ +#ifndef _ASM_KPROBES_H +#define _ASM_KPROBES_H +/* +* Dynamic Probes (kprobes) support +* Vamsi Krishna S <[email protected]>, July, 2002 +* Mailing list: [email protected] +*/ +#include <linux/types.h> +#include <linux/ptrace.h> + +struct pt_regs ; + +typedef u8 kprobe_opcode_t ; +#define BREAKPOINT_INSTRUCTION 0xcc + +/* trap3/1 are intr gates for kprobes. So, restore the status of IF, +* if necessary, before executing the original int3/1 (trap) handler. +*/ +static inline void restore_interrupts (struct pt_regs *regs) +{ +if (regs->eflags &IF_MASK ) + __asm__ __volatile__ ("sti" ); +} + +#ifdef CONFIG_KPROBES +extern int kprobe_fault_handler (struct pt_regs *regs,int trapnr); +extern int post_kprobe_handler (struct pt_regs *regs); +extern int kprobe_handler (struct pt_regs *regs); +#else /* !CONFIG_KPROBES */ +static inline int kprobe_fault_handler (struct pt_regs *regs,int trapnr) {return 0 ; } +static inline int post_kprobe_handler (struct pt_regs *regs) {return 0 ; } +static inline int kprobe_handler (struct pt_regs *regs) {return 0 ; } +#endif +#endif /* _ASM_KPROBES_H */
include/linux/kprobes.h +58 -0
58 lines not on this page · raw message
kernel/Makefile +2 -1
@@ -10,7 +10,7 @@ O_TARGET := kernel.o export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o \ - printk.o platform.o suspend.o dma.o + printk.o platform.o suspend.o dma.o kprobes.o obj-y = sched.o fork.o exec_domain.o panic.o printk.o \ module.o exit.o itimer.o time.o softirq.o resource.o \
@@ -24,6 +24,7 @@ obj-$(CONFIG_MODULES) += ksyms.o obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend.o +obj-$(CONFIG_KPROBES) += kprobes.o ifneq ($(CONFIG_IA64),y) # According to Alan Modra <[email protected]>, the -fno-omit-frame-pointer is
kernel/kprobes.c +89 -0
89 lines not on this page · raw message
Hi Rusty,
Quoted 5 lines · Rusty Russell
In message <[email protected]> you write:
Quoted 1 line
No. Don't do this. When a trap occurs in kernel space, the
Sorry Vamsi: I'll have more faith in your code next time, I promise.
Thanks :-). But, I think you forgot to apply my incremental patch. Here it is:
Thanks again,
Vamsi.
Signature · 58 lines
Vamsi Krishna S. Linux Technology Center, IBM Software Lab, Bangalore. Ph: +91 80 5044959 Internet: [email protected] -- diff -u 31-dp/arch/i386/kernel/entry.S 31-dp/arch/i386/kernel/entry.S --- 31-dp/arch/i386/kernel/entry.S 2002-08-21 11:20:18.000000000 +0530 +++ 31-dp/arch/i386/kernel/entry.S 2002-08-21 12:10:30.000000000 +0530 @@ -430,7 +430,7 @@ jmp ret_from_exception ENTRY(debug) - pushl %eax + pushl $-1 # mark this as an int SAVE_ALL movl %esp,%edx pushl $0 @@ -452,7 +452,7 @@ RESTORE_ALL ENTRY(int3) - pushl %eax + pushl $-1 # mark this as an int SAVE_ALL movl %esp,%edx pushl $0 diff -u 31-dp/arch/i386/kernel/kprobes.c 31-dp/arch/i386/kernel/kprobes.c --- 31-dp/arch/i386/kernel/kprobes.c 2002-08-21 11:20:18.000000000 +0530 +++ 31-dp/arch/i386/kernel/kprobes.c 2002-08-21 12:09:50.000000000 +0530 @@ -117,11 +117,12 @@ /* * We singlestepped with interrupts disabled. So, the result on * the stack would be incorrect for "pushfl" instruction. + * Note that regs->esp is actually the top of the stack when the + * trap occurs in kernel space. */ if (current_kprobe->opcode == 0x9c) { /* pushfl */ - unsigned long *stacktop = (unsigned long *)regs->esp; - *stacktop &= ~(TF_MASK | IF_MASK); - *stacktop |= kprobe_old_eflags; + regs->esp &= ~(TF_MASK | IF_MASK); + regs->esp |= kprobe_old_eflags; } rearm_kprobe(current_kprobe, regs); diff -u 31-dp/include/linux/kprobes.h 31-dp/include/linux/kprobes.h --- 31-dp/include/linux/kprobes.h 2002-08-21 11:20:18.000000000 +0530 +++ 31-dp/include/linux/kprobes.h 2002-08-21 12:30:24.000000000 +0530 @@ -2,6 +2,8 @@ #define _LINUX_KPROBES_H #include <linux/config.h> #include <linux/list.h> +#include <linux/notifier.h> +#include <linux/smp.h> #include <asm/kprobes.h> struct kprobe;