diff options
-rw-r--r-- | arch/x86/kernel/kprobes.c | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c index 7b5169d2b000..aa15f3e1f64b 100644 --- a/arch/x86/kernel/kprobes.c +++ b/arch/x86/kernel/kprobes.c | |||
@@ -48,12 +48,14 @@ | |||
48 | #include <linux/preempt.h> | 48 | #include <linux/preempt.h> |
49 | #include <linux/module.h> | 49 | #include <linux/module.h> |
50 | #include <linux/kdebug.h> | 50 | #include <linux/kdebug.h> |
51 | #include <linux/kallsyms.h> | ||
51 | 52 | ||
52 | #include <asm/cacheflush.h> | 53 | #include <asm/cacheflush.h> |
53 | #include <asm/desc.h> | 54 | #include <asm/desc.h> |
54 | #include <asm/pgtable.h> | 55 | #include <asm/pgtable.h> |
55 | #include <asm/uaccess.h> | 56 | #include <asm/uaccess.h> |
56 | #include <asm/alternative.h> | 57 | #include <asm/alternative.h> |
58 | #include <asm/insn.h> | ||
57 | 59 | ||
58 | void jprobe_return_end(void); | 60 | void jprobe_return_end(void); |
59 | 61 | ||
@@ -244,6 +246,75 @@ retry: | |||
244 | } | 246 | } |
245 | } | 247 | } |
246 | 248 | ||
249 | /* Recover the probed instruction at addr for further analysis. */ | ||
250 | static int recover_probed_instruction(kprobe_opcode_t *buf, unsigned long addr) | ||
251 | { | ||
252 | struct kprobe *kp; | ||
253 | kp = get_kprobe((void *)addr); | ||
254 | if (!kp) | ||
255 | return -EINVAL; | ||
256 | |||
257 | /* | ||
258 | * Basically, kp->ainsn.insn has an original instruction. | ||
259 | * However, RIP-relative instruction can not do single-stepping | ||
260 | * at different place, fix_riprel() tweaks the displacement of | ||
261 | * that instruction. In that case, we can't recover the instruction | ||
262 | * from the kp->ainsn.insn. | ||
263 | * | ||
264 | * On the other hand, kp->opcode has a copy of the first byte of | ||
265 | * the probed instruction, which is overwritten by int3. And | ||
266 | * the instruction at kp->addr is not modified by kprobes except | ||
267 | * for the first byte, we can recover the original instruction | ||
268 | * from it and kp->opcode. | ||
269 | */ | ||
270 | memcpy(buf, kp->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); | ||
271 | buf[0] = kp->opcode; | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | /* Dummy buffers for kallsyms_lookup */ | ||
276 | static char __dummy_buf[KSYM_NAME_LEN]; | ||
277 | |||
278 | /* Check if paddr is at an instruction boundary */ | ||
279 | static int __kprobes can_probe(unsigned long paddr) | ||
280 | { | ||
281 | int ret; | ||
282 | unsigned long addr, offset = 0; | ||
283 | struct insn insn; | ||
284 | kprobe_opcode_t buf[MAX_INSN_SIZE]; | ||
285 | |||
286 | if (!kallsyms_lookup(paddr, NULL, &offset, NULL, __dummy_buf)) | ||
287 | return 0; | ||
288 | |||
289 | /* Decode instructions */ | ||
290 | addr = paddr - offset; | ||
291 | while (addr < paddr) { | ||
292 | kernel_insn_init(&insn, (void *)addr); | ||
293 | insn_get_opcode(&insn); | ||
294 | |||
295 | /* | ||
296 | * Check if the instruction has been modified by another | ||
297 | * kprobe, in which case we replace the breakpoint by the | ||
298 | * original instruction in our buffer. | ||
299 | */ | ||
300 | if (insn.opcode.bytes[0] == BREAKPOINT_INSTRUCTION) { | ||
301 | ret = recover_probed_instruction(buf, addr); | ||
302 | if (ret) | ||
303 | /* | ||
304 | * Another debugging subsystem might insert | ||
305 | * this breakpoint. In that case, we can't | ||
306 | * recover it. | ||
307 | */ | ||
308 | return 0; | ||
309 | kernel_insn_init(&insn, buf); | ||
310 | } | ||
311 | insn_get_length(&insn); | ||
312 | addr += insn.length; | ||
313 | } | ||
314 | |||
315 | return (addr == paddr); | ||
316 | } | ||
317 | |||
247 | /* | 318 | /* |
248 | * Returns non-zero if opcode modifies the interrupt flag. | 319 | * Returns non-zero if opcode modifies the interrupt flag. |
249 | */ | 320 | */ |
@@ -359,6 +430,8 @@ static void __kprobes arch_copy_kprobe(struct kprobe *p) | |||
359 | 430 | ||
360 | int __kprobes arch_prepare_kprobe(struct kprobe *p) | 431 | int __kprobes arch_prepare_kprobe(struct kprobe *p) |
361 | { | 432 | { |
433 | if (!can_probe((unsigned long)p->addr)) | ||
434 | return -EILSEQ; | ||
362 | /* insn: must be on special executable page on x86. */ | 435 | /* insn: must be on special executable page on x86. */ |
363 | p->ainsn.insn = get_insn_slot(); | 436 | p->ainsn.insn = get_insn_slot(); |
364 | if (!p->ainsn.insn) | 437 | if (!p->ainsn.insn) |