diff options
| author | Eugene Shatokhin <eugene.shatokhin@rosalab.ru> | 2015-03-17 06:09:18 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2015-03-17 09:00:38 -0400 |
| commit | c80e5c0c23ce2282476fdc64c4b5e3d3a40723fd (patch) | |
| tree | 056afab9efce2ce76166abb5d6ba177d62e0ebb0 /arch/x86/kernel/kprobes | |
| parent | 107eb964d8c04417e8bce9e9ec2ed61d9261aec6 (diff) | |
kprobes/x86: Return correct length in __copy_instruction()
On x86-64, __copy_instruction() always returns 0 (error) if the
instruction uses %rip-relative addressing. This is because
kernel_insn_init() is called the second time for 'insn' instance
in such cases and sets all its fields to 0.
Because of this, trying to place a kprobe on such instruction
will fail, register_kprobe() will return -EINVAL.
This patch fixes the problem.
Signed-off-by: Eugene Shatokhin <eugene.shatokhin@rosalab.ru>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Link: http://lkml.kernel.org/r/20150317100918.28349.94654.stgit@localhost.localdomain
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch/x86/kernel/kprobes')
| -rw-r--r-- | arch/x86/kernel/kprobes/core.c | 9 |
1 files changed, 6 insertions, 3 deletions
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c index 4e3d5a9621fe..03189d86357d 100644 --- a/arch/x86/kernel/kprobes/core.c +++ b/arch/x86/kernel/kprobes/core.c | |||
| @@ -354,6 +354,7 @@ int __copy_instruction(u8 *dest, u8 *src) | |||
| 354 | { | 354 | { |
| 355 | struct insn insn; | 355 | struct insn insn; |
| 356 | kprobe_opcode_t buf[MAX_INSN_SIZE]; | 356 | kprobe_opcode_t buf[MAX_INSN_SIZE]; |
| 357 | int length; | ||
| 357 | unsigned long recovered_insn = | 358 | unsigned long recovered_insn = |
| 358 | recover_probed_instruction(buf, (unsigned long)src); | 359 | recover_probed_instruction(buf, (unsigned long)src); |
| 359 | 360 | ||
| @@ -361,16 +362,18 @@ int __copy_instruction(u8 *dest, u8 *src) | |||
| 361 | return 0; | 362 | return 0; |
| 362 | kernel_insn_init(&insn, (void *)recovered_insn, MAX_INSN_SIZE); | 363 | kernel_insn_init(&insn, (void *)recovered_insn, MAX_INSN_SIZE); |
| 363 | insn_get_length(&insn); | 364 | insn_get_length(&insn); |
| 365 | length = insn.length; | ||
| 366 | |||
| 364 | /* Another subsystem puts a breakpoint, failed to recover */ | 367 | /* Another subsystem puts a breakpoint, failed to recover */ |
| 365 | if (insn.opcode.bytes[0] == BREAKPOINT_INSTRUCTION) | 368 | if (insn.opcode.bytes[0] == BREAKPOINT_INSTRUCTION) |
| 366 | return 0; | 369 | return 0; |
| 367 | memcpy(dest, insn.kaddr, insn.length); | 370 | memcpy(dest, insn.kaddr, length); |
| 368 | 371 | ||
| 369 | #ifdef CONFIG_X86_64 | 372 | #ifdef CONFIG_X86_64 |
| 370 | if (insn_rip_relative(&insn)) { | 373 | if (insn_rip_relative(&insn)) { |
| 371 | s64 newdisp; | 374 | s64 newdisp; |
| 372 | u8 *disp; | 375 | u8 *disp; |
| 373 | kernel_insn_init(&insn, dest, insn.length); | 376 | kernel_insn_init(&insn, dest, length); |
| 374 | insn_get_displacement(&insn); | 377 | insn_get_displacement(&insn); |
| 375 | /* | 378 | /* |
| 376 | * The copied instruction uses the %rip-relative addressing | 379 | * The copied instruction uses the %rip-relative addressing |
| @@ -394,7 +397,7 @@ int __copy_instruction(u8 *dest, u8 *src) | |||
| 394 | *(s32 *) disp = (s32) newdisp; | 397 | *(s32 *) disp = (s32) newdisp; |
| 395 | } | 398 | } |
| 396 | #endif | 399 | #endif |
| 397 | return insn.length; | 400 | return length; |
| 398 | } | 401 | } |
| 399 | 402 | ||
| 400 | static int arch_copy_kprobe(struct kprobe *p) | 403 | static int arch_copy_kprobe(struct kprobe *p) |
