From f438d914b220051d4cbc65cbc5d98e163c85c93b Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Oct 2007 01:27:49 -0700 Subject: [PATCH] kprobes: support kretprobe blacklist Introduce architecture dependent kretprobe blacklists to prohibit users from inserting return probes on the function in which kprobes can be inserted but kretprobes can not. This patch also removes "__kprobes" mark from "__switch_to" on x86_64 and registers "__switch_to" to the blacklist on x86-64, because that mark is to prohibit user from inserting only kretprobe. Signed-off-by: Masami Hiramatsu Cc: Prasanna S Panchamukhi Acked-by: Ananth N Mavinakayanahalli Cc: Anil S Keshavamurthy Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/avr32/kernel/kprobes.c | 2 ++ arch/ia64/kernel/kprobes.c | 2 ++ arch/powerpc/kernel/kprobes.c | 2 ++ arch/s390/kernel/kprobes.c | 2 ++ arch/sparc64/kernel/kprobes.c | 2 ++ arch/x86/kernel/kprobes_32.c | 7 +++++++ arch/x86/kernel/kprobes_64.c | 7 +++++++ arch/x86/kernel/process_64.c | 2 +- include/asm-avr32/kprobes.h | 2 ++ include/asm-ia64/kprobes.h | 1 + include/asm-powerpc/kprobes.h | 1 + include/asm-s390/kprobes.h | 1 + include/asm-sparc64/kprobes.h | 2 ++ include/asm-x86/kprobes_32.h | 2 ++ include/asm-x86/kprobes_64.h | 1 + include/linux/kprobes.h | 6 ++++++ kernel/kprobes.c | 23 +++++++++++++++++++++++ 17 files changed, 64 insertions(+), 1 deletion(-) diff --git a/arch/avr32/kernel/kprobes.c b/arch/avr32/kernel/kprobes.c index 4942ee662e0..20b1c9d8f94 100644 --- a/arch/avr32/kernel/kprobes.c +++ b/arch/avr32/kernel/kprobes.c @@ -22,6 +22,8 @@ DEFINE_PER_CPU(struct kprobe *, current_kprobe); static unsigned long kprobe_status; static struct pt_regs jprobe_saved_regs; +struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}}; + int __kprobes arch_prepare_kprobe(struct kprobe *p) { int ret = 0; diff --git a/arch/ia64/kernel/kprobes.c b/arch/ia64/kernel/kprobes.c index 5dc98b5abcf..5fd65d8302c 100644 --- a/arch/ia64/kernel/kprobes.c +++ b/arch/ia64/kernel/kprobes.c @@ -40,6 +40,8 @@ extern void jprobe_inst_return(void); DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); +struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}}; + enum instruction_type {A, I, M, F, B, L, X, u}; static enum instruction_type bundle_encoding[32][3] = { { M, I, I }, /* 00 */ diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c index 440f5a87271..5338e485571 100644 --- a/arch/powerpc/kernel/kprobes.c +++ b/arch/powerpc/kernel/kprobes.c @@ -38,6 +38,8 @@ DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); +struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}}; + int __kprobes arch_prepare_kprobe(struct kprobe *p) { int ret = 0; diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index e40373d9fbc..c5549a20628 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c @@ -33,6 +33,8 @@ DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); +struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}}; + int __kprobes arch_prepare_kprobe(struct kprobe *p) { /* Make sure the probe isn't going on a difficult instruction */ diff --git a/arch/sparc64/kernel/kprobes.c b/arch/sparc64/kernel/kprobes.c index c93a15b785f..d94f901d321 100644 --- a/arch/sparc64/kernel/kprobes.c +++ b/arch/sparc64/kernel/kprobes.c @@ -42,6 +42,8 @@ DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); +struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}}; + int __kprobes arch_prepare_kprobe(struct kprobe *p) { p->ainsn.insn[0] = *p->addr; diff --git a/arch/x86/kernel/kprobes_32.c b/arch/x86/kernel/kprobes_32.c index 06b86e5617f..90f778c04b3 100644 --- a/arch/x86/kernel/kprobes_32.c +++ b/arch/x86/kernel/kprobes_32.c @@ -41,6 +41,13 @@ void jprobe_return_end(void); DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); +struct kretprobe_blackpoint kretprobe_blacklist[] = { + {"__switch_to", }, /* This function switches only current task, but + doesn't switch kernel stack.*/ + {NULL, NULL} /* Terminator */ +}; +const int kretprobe_blacklist_size = ARRAY_SIZE(kretprobe_blacklist); + /* insert a jmp code */ static __always_inline void set_jmp_op(void *from, void *to) { diff --git a/arch/x86/kernel/kprobes_64.c b/arch/x86/kernel/kprobes_64.c index 7c16506d681..681b801c5e2 100644 --- a/arch/x86/kernel/kprobes_64.c +++ b/arch/x86/kernel/kprobes_64.c @@ -48,6 +48,13 @@ static void __kprobes arch_copy_kprobe(struct kprobe *p); DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); +struct kretprobe_blackpoint kretprobe_blacklist[] = { + {"__switch_to", }, /* This function switches only current task, but + doesn't switch kernel stack.*/ + {NULL, NULL} /* Terminator */ +}; +const int kretprobe_blacklist_size = ARRAY_SIZE(kretprobe_blacklist); + /* * returns non-zero if opcode modifies the interrupt flag. */ diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 7352d4b377e..6309b275cb9 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -581,7 +581,7 @@ static inline void __switch_to_xtra(struct task_struct *prev_p, * * Kprobes not supported here. Set the probe on schedule instead. */ -__kprobes struct task_struct * +struct task_struct * __switch_to(struct task_struct *prev_p, struct task_struct *next_p) { struct thread_struct *prev = &prev_p->thread, diff --git a/include/asm-avr32/kprobes.h b/include/asm-avr32/kprobes.h index 0f3e636e6e4..996cb656474 100644 --- a/include/asm-avr32/kprobes.h +++ b/include/asm-avr32/kprobes.h @@ -17,6 +17,8 @@ typedef u16 kprobe_opcode_t; #define BREAKPOINT_INSTRUCTION 0xd673 /* breakpoint */ #define MAX_INSN_SIZE 2 +#define kretprobe_blacklist_size 0 + #define arch_remove_kprobe(p) do { } while (0) /* Architecture specific copy of original instruction */ diff --git a/include/asm-ia64/kprobes.h b/include/asm-ia64/kprobes.h index 6c79edf24d7..a93ce9ef07f 100644 --- a/include/asm-ia64/kprobes.h +++ b/include/asm-ia64/kprobes.h @@ -83,6 +83,7 @@ struct kprobe_ctlblk { }; #define ARCH_SUPPORTS_KRETPROBES +#define kretprobe_blacklist_size 0 #define SLOT0_OPCODE_SHIFT (37) #define SLOT1_p1_OPCODE_SHIFT (37 - (64-46)) diff --git a/include/asm-powerpc/kprobes.h b/include/asm-powerpc/kprobes.h index c16973d5de6..afabad230db 100644 --- a/include/asm-powerpc/kprobes.h +++ b/include/asm-powerpc/kprobes.h @@ -82,6 +82,7 @@ typedef unsigned int kprobe_opcode_t; #define ARCH_SUPPORTS_KRETPROBES #define flush_insn_slot(p) do { } while (0) +#define kretprobe_blacklist_size 0 void kretprobe_trampoline(void); extern void arch_remove_kprobe(struct kprobe *p); diff --git a/include/asm-s390/kprobes.h b/include/asm-s390/kprobes.h index 8bc67cc9ffd..948db3d0d05 100644 --- a/include/asm-s390/kprobes.h +++ b/include/asm-s390/kprobes.h @@ -47,6 +47,7 @@ typedef u16 kprobe_opcode_t; : (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR))) #define ARCH_SUPPORTS_KRETPROBES +#define kretprobe_blacklist_size 0 #define KPROBE_SWAP_INST 0x10 diff --git a/include/asm-sparc64/kprobes.h b/include/asm-sparc64/kprobes.h index a04145b77f9..5020eaf67c2 100644 --- a/include/asm-sparc64/kprobes.h +++ b/include/asm-sparc64/kprobes.h @@ -10,6 +10,8 @@ typedef u32 kprobe_opcode_t; #define BREAKPOINT_INSTRUCTION_2 0x91d02071 /* ta 0x71 */ #define MAX_INSN_SIZE 2 +#define kretprobe_blacklist_size 0 + #define arch_remove_kprobe(p) do {} while (0) #define flush_insn_slot(p) \ diff --git a/include/asm-x86/kprobes_32.h b/include/asm-x86/kprobes_32.h index f2489d07ce8..b772d5b3868 100644 --- a/include/asm-x86/kprobes_32.h +++ b/include/asm-x86/kprobes_32.h @@ -45,6 +45,8 @@ typedef u8 kprobe_opcode_t; #define ARCH_SUPPORTS_KRETPROBES #define flush_insn_slot(p) do { } while (0) +extern const int kretprobe_blacklist_size; + void arch_remove_kprobe(struct kprobe *p); void kretprobe_trampoline(void); diff --git a/include/asm-x86/kprobes_64.h b/include/asm-x86/kprobes_64.h index 3f495e5308b..53f4d850735 100644 --- a/include/asm-x86/kprobes_64.h +++ b/include/asm-x86/kprobes_64.h @@ -42,6 +42,7 @@ typedef u8 kprobe_opcode_t; : (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR))) #define ARCH_SUPPORTS_KRETPROBES +extern const int kretprobe_blacklist_size; void kretprobe_trampoline(void); extern void arch_remove_kprobe(struct kprobe *p); diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 51464d12a4e..81891581e89 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -166,6 +166,12 @@ struct kretprobe_instance { struct task_struct *task; }; +struct kretprobe_blackpoint { + const char *name; + void *addr; +}; +extern struct kretprobe_blackpoint kretprobe_blacklist[]; + static inline void kretprobe_assert(struct kretprobe_instance *ri, unsigned long orig_ret_address, unsigned long trampoline_address) { diff --git a/kernel/kprobes.c b/kernel/kprobes.c index f9798ff7899..e3a5d817ac9 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -716,6 +716,18 @@ int __kprobes register_kretprobe(struct kretprobe *rp) int ret = 0; struct kretprobe_instance *inst; int i; + void *addr = rp->kp.addr; + + if (kretprobe_blacklist_size) { + if (addr == NULL) + kprobe_lookup_name(rp->kp.symbol_name, addr); + addr += rp->kp.offset; + + for (i = 0; kretprobe_blacklist[i].name != NULL; i++) { + if (kretprobe_blacklist[i].addr == addr) + return -EINVAL; + } + } rp->kp.pre_handler = pre_handler_kretprobe; rp->kp.post_handler = NULL; @@ -794,6 +806,17 @@ static int __init init_kprobes(void) INIT_HLIST_HEAD(&kretprobe_inst_table[i]); } + if (kretprobe_blacklist_size) { + /* lookup the function address from its name */ + for (i = 0; kretprobe_blacklist[i].name != NULL; i++) { + kprobe_lookup_name(kretprobe_blacklist[i].name, + kretprobe_blacklist[i].addr); + if (!kretprobe_blacklist[i].addr) + printk("kretprobe: lookup failed: %s\n", + kretprobe_blacklist[i].name); + } + } + /* By default, kprobes are enabled */ kprobe_enabled = true; -- 2.41.1