Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+49 −46
Original line number Diff line number Diff line
@@ -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 (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.
		 * 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;
		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 (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.
		 * 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;
		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;
}