s390/ftrace: add code replacement sanity checks
authorHeiko Carstens <heiko.carstens@de.ibm.com>
Tue, 9 Dec 2014 09:18:49 +0000 (10:18 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Thu, 8 Jan 2015 09:02:46 +0000 (10:02 +0100)
Always verify that the to be replaced code matches what we expect to see.

Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/kernel/ftrace.c

index b86bb8823f15192030f227d2c9cc189d66804d50..3dabcae40e0421457d4b6fee1aa05773920bec0c 100644 (file)
@@ -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,
                    unsigned long addr)
 {
-       struct ftrace_insn insn;
-       unsigned short op;
-       void *from, *to;
-       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)))
+       struct ftrace_insn orig, new, old;
+
+       if (probe_kernel_read(&old, (void *) rec->ip, sizeof(old)))
                return -EFAULT;
-       /*
-        * If we find a breakpoint instruction, a kprobe has been placed
-        * at the beginning of the function. We write the constant
-        * KPROBE_ON_FTRACE_NOP into the remaining four bytes of the original
-        * instruction so that the kprobes handler can execute a nop, if it
-        * reaches this breakpoint.
-        */
-       if (op == BREAKPOINT_INSTRUCTION) {
-               size -= 2;
-               from += 2;
-               to += 2;
-               insn.disp = KPROBE_ON_FTRACE_NOP;
+       if (addr == MCOUNT_ADDR) {
+               /* Initial code replacement; we expect to see stg r14,8(r15) */
+               orig.opc = 0xe3e0;
+               orig.disp = 0xf0080024;
+               ftrace_generate_nop_insn(&new);
+       } else 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 KPROBE_ON_FTRACE_NOP into the remaining four
+                * bytes of the original instruction so that the kprobes
+                * 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 0;
 }
 
 int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
 {
-       struct ftrace_insn insn;
-       unsigned short op;
-       void *from, *to;
-       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)))
+       struct ftrace_insn orig, new, old;
+
+       if (probe_kernel_read(&old, (void *) rec->ip, sizeof(old)))
                return -EFAULT;
-       /*
-        * If we find a breakpoint instruction, a kprobe has been placed
-        * at the beginning of the function. We write the constant
-        * KPROBE_ON_FTRACE_CALL into the remaining four 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;
-               from += 2;
-               to += 2;
-               insn.disp = KPROBE_ON_FTRACE_CALL;
+       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 KPROBE_ON_FTRACE_CALL into the remaining four
+                * bytes of the original instruction so that the kprobes
+                * handler can execute a brasl if it reaches this breakpoint.
+                */
+               new.opc = orig.opc = BREAKPOINT_INSTRUCTION;
+               orig.disp = KPROBE_ON_FTRACE_NOP;
+               new.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 0;
 }