From: Heiko Carstens <heiko.carstens@de.ibm.com>
Date: Tue, 9 Dec 2014 09:18:49 +0000 (+0100)
Subject: s390/ftrace: add code replacement sanity checks
X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=58498ee3e573dc03be651b6839dbdf865ae7ee38;p=GitHub%2Fexynos8895%2Fandroid_kernel_samsung_universal8895.git

s390/ftrace: add code replacement sanity checks

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>
---

diff --git a/arch/s390/kernel/ftrace.c b/arch/s390/kernel/ftrace.c
index b86bb8823f15..3dabcae40e04 100644
--- a/arch/s390/kernel/ftrace.c
+++ b/arch/s390/kernel/ftrace.c
@@ -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;
 }