Commit 58498ee3 authored by Heiko Carstens's avatar Heiko Carstens Committed by Martin Schwidefsky

s390/ftrace: add code replacement sanity checks

Always verify that the to be replaced code matches what we expect to see.
Signed-off-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent fbc89c95
...@@ -59,62 +59,65 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, ...@@ -59,62 +59,65 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
unsigned long addr) unsigned long addr)
{ {
struct ftrace_insn insn; struct ftrace_insn orig, new, old;
unsigned short op;
void *from, *to; if (probe_kernel_read(&old, (void *) rec->ip, sizeof(old)))
size_t size;
ftrace_generate_nop_insn(&insn);
size = sizeof(insn);
from = &insn;
to = (void *) rec->ip;
if (probe_kernel_read(&op, (void *) rec->ip, sizeof(op)))
return -EFAULT; return -EFAULT;
/* if (addr == MCOUNT_ADDR) {
* If we find a breakpoint instruction, a kprobe has been placed /* Initial code replacement; we expect to see stg r14,8(r15) */
* at the beginning of the function. We write the constant orig.opc = 0xe3e0;
* KPROBE_ON_FTRACE_NOP into the remaining four bytes of the original orig.disp = 0xf0080024;
* instruction so that the kprobes handler can execute a nop, if it ftrace_generate_nop_insn(&new);
* reaches this breakpoint. } else if (old.opc == BREAKPOINT_INSTRUCTION) {
*/ /*
if (op == BREAKPOINT_INSTRUCTION) { * If we find a breakpoint instruction, a kprobe has been
size -= 2; * placed at the beginning of the function. We write the
from += 2; * constant KPROBE_ON_FTRACE_NOP into the remaining four
to += 2; * bytes of the original instruction so that the kprobes
insn.disp = KPROBE_ON_FTRACE_NOP; * handler can execute a nop, if it reaches this breakpoint.
*/
new.opc = orig.opc = BREAKPOINT_INSTRUCTION;
orig.disp = KPROBE_ON_FTRACE_CALL;
new.disp = KPROBE_ON_FTRACE_NOP;
} else {
/* Replace ftrace call with a nop. */
ftrace_generate_call_insn(&orig, rec->ip);
ftrace_generate_nop_insn(&new);
} }
if (probe_kernel_write(to, from, size)) /* Verify that the to be replaced code matches what we expect. */
if (memcmp(&orig, &old, sizeof(old)))
return -EINVAL;
if (probe_kernel_write((void *) rec->ip, &new, sizeof(new)))
return -EPERM; return -EPERM;
return 0; return 0;
} }
int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
{ {
struct ftrace_insn insn; struct ftrace_insn orig, new, old;
unsigned short op;
void *from, *to; if (probe_kernel_read(&old, (void *) rec->ip, sizeof(old)))
size_t size;
ftrace_generate_call_insn(&insn, rec->ip);
size = sizeof(insn);
from = &insn;
to = (void *) rec->ip;
if (probe_kernel_read(&op, (void *) rec->ip, sizeof(op)))
return -EFAULT; return -EFAULT;
/* if (old.opc == BREAKPOINT_INSTRUCTION) {
* If we find a breakpoint instruction, a kprobe has been placed /*
* at the beginning of the function. We write the constant * If we find a breakpoint instruction, a kprobe has been
* KPROBE_ON_FTRACE_CALL into the remaining four bytes of the original * placed at the beginning of the function. We write the
* instruction so that the kprobes handler can execute a brasl if it * constant KPROBE_ON_FTRACE_CALL into the remaining four
* reaches this breakpoint. * bytes of the original instruction so that the kprobes
*/ * handler can execute a brasl if it reaches this breakpoint.
if (op == BREAKPOINT_INSTRUCTION) { */
size -= 2; new.opc = orig.opc = BREAKPOINT_INSTRUCTION;
from += 2; orig.disp = KPROBE_ON_FTRACE_NOP;
to += 2; new.disp = KPROBE_ON_FTRACE_CALL;
insn.disp = KPROBE_ON_FTRACE_CALL; } else {
/* Replace nop with an ftrace call. */
ftrace_generate_nop_insn(&orig);
ftrace_generate_call_insn(&new, rec->ip);
} }
if (probe_kernel_write(to, from, size)) /* Verify that the to be replaced code matches what we expect. */
if (memcmp(&orig, &old, sizeof(old)))
return -EINVAL;
if (probe_kernel_write((void *) rec->ip, &new, sizeof(new)))
return -EPERM; return -EPERM;
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment