diff options
| author | Petr Mladek <pmladek@suse.cz> | 2015-02-20 09:07:30 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2015-02-21 04:33:31 -0500 |
| commit | 2a6730c8b6e075adf826a89a3e2caa705807afdb (patch) | |
| tree | 05620565e26b29ba355b8241b2b80c6c0189ef52 | |
| parent | 650b7b23cb1e32d77daeefbac1ceb1329abf3b23 (diff) | |
kprobes/x86: Check for invalid ftrace location in __recover_probed_insn()
__recover_probed_insn() should always be called from an address
where an instructions starts. The check for ftrace_location()
might help to discover a potential inconsistency.
This patch adds WARN_ON() when the inconsistency is detected.
Also it adds handling of the situation when the original code
can not get recovered.
Suggested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Petr Mladek <pmladek@suse.cz>
Cc: Ananth NMavinakayanahalli <ananth@in.ibm.com>
Cc: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>
Cc: Steven Rostedt <rostedt@goodmis.org>
Link: http://lkml.kernel.org/r/1424441250-27146-3-git-send-email-pmladek@suse.cz
Signed-off-by: Ingo Molnar <mingo@kernel.org>
| -rw-r--r-- | arch/x86/kernel/kprobes/core.c | 12 | ||||
| -rw-r--r-- | arch/x86/kernel/kprobes/opt.c | 2 |
2 files changed, 14 insertions, 0 deletions
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c index c3b4b46b4797..4e3d5a9621fe 100644 --- a/arch/x86/kernel/kprobes/core.c +++ b/arch/x86/kernel/kprobes/core.c | |||
| @@ -228,6 +228,13 @@ __recover_probed_insn(kprobe_opcode_t *buf, unsigned long addr) | |||
| 228 | kp = get_kprobe((void *)addr); | 228 | kp = get_kprobe((void *)addr); |
| 229 | faddr = ftrace_location(addr); | 229 | faddr = ftrace_location(addr); |
| 230 | /* | 230 | /* |
| 231 | * Addresses inside the ftrace location are refused by | ||
| 232 | * arch_check_ftrace_location(). Something went terribly wrong | ||
| 233 | * if such an address is checked here. | ||
| 234 | */ | ||
| 235 | if (WARN_ON(faddr && faddr != addr)) | ||
| 236 | return 0UL; | ||
| 237 | /* | ||
| 231 | * Use the current code if it is not modified by Kprobe | 238 | * Use the current code if it is not modified by Kprobe |
| 232 | * and it cannot be modified by ftrace. | 239 | * and it cannot be modified by ftrace. |
| 233 | */ | 240 | */ |
| @@ -265,6 +272,7 @@ __recover_probed_insn(kprobe_opcode_t *buf, unsigned long addr) | |||
| 265 | * Recover the probed instruction at addr for further analysis. | 272 | * Recover the probed instruction at addr for further analysis. |
| 266 | * Caller must lock kprobes by kprobe_mutex, or disable preemption | 273 | * Caller must lock kprobes by kprobe_mutex, or disable preemption |
| 267 | * for preventing to release referencing kprobes. | 274 | * for preventing to release referencing kprobes. |
| 275 | * Returns zero if the instruction can not get recovered. | ||
| 268 | */ | 276 | */ |
| 269 | unsigned long recover_probed_instruction(kprobe_opcode_t *buf, unsigned long addr) | 277 | unsigned long recover_probed_instruction(kprobe_opcode_t *buf, unsigned long addr) |
| 270 | { | 278 | { |
| @@ -299,6 +307,8 @@ static int can_probe(unsigned long paddr) | |||
| 299 | * normally used, we just go through if there is no kprobe. | 307 | * normally used, we just go through if there is no kprobe. |
| 300 | */ | 308 | */ |
| 301 | __addr = recover_probed_instruction(buf, addr); | 309 | __addr = recover_probed_instruction(buf, addr); |
| 310 | if (!__addr) | ||
| 311 | return 0; | ||
| 302 | kernel_insn_init(&insn, (void *)__addr, MAX_INSN_SIZE); | 312 | kernel_insn_init(&insn, (void *)__addr, MAX_INSN_SIZE); |
| 303 | insn_get_length(&insn); | 313 | insn_get_length(&insn); |
| 304 | 314 | ||
| @@ -347,6 +357,8 @@ int __copy_instruction(u8 *dest, u8 *src) | |||
| 347 | unsigned long recovered_insn = | 357 | unsigned long recovered_insn = |
| 348 | recover_probed_instruction(buf, (unsigned long)src); | 358 | recover_probed_instruction(buf, (unsigned long)src); |
| 349 | 359 | ||
| 360 | if (!recovered_insn) | ||
| 361 | return 0; | ||
| 350 | kernel_insn_init(&insn, (void *)recovered_insn, MAX_INSN_SIZE); | 362 | kernel_insn_init(&insn, (void *)recovered_insn, MAX_INSN_SIZE); |
| 351 | insn_get_length(&insn); | 363 | insn_get_length(&insn); |
| 352 | /* Another subsystem puts a breakpoint, failed to recover */ | 364 | /* Another subsystem puts a breakpoint, failed to recover */ |
diff --git a/arch/x86/kernel/kprobes/opt.c b/arch/x86/kernel/kprobes/opt.c index 7c523bbf3dc8..3aef248ec1ee 100644 --- a/arch/x86/kernel/kprobes/opt.c +++ b/arch/x86/kernel/kprobes/opt.c | |||
| @@ -259,6 +259,8 @@ static int can_optimize(unsigned long paddr) | |||
| 259 | */ | 259 | */ |
| 260 | return 0; | 260 | return 0; |
| 261 | recovered_insn = recover_probed_instruction(buf, addr); | 261 | recovered_insn = recover_probed_instruction(buf, addr); |
| 262 | if (!recovered_insn) | ||
| 263 | return 0; | ||
| 262 | kernel_insn_init(&insn, (void *)recovered_insn, MAX_INSN_SIZE); | 264 | kernel_insn_init(&insn, (void *)recovered_insn, MAX_INSN_SIZE); |
| 263 | insn_get_length(&insn); | 265 | insn_get_length(&insn); |
| 264 | /* Another subsystem puts a breakpoint */ | 266 | /* Another subsystem puts a breakpoint */ |
