aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/kernel
diff options
context:
space:
mode:
authorHeiko Carstens <heiko.carstens@de.ibm.com>2014-12-09 04:18:49 -0500
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2015-01-08 04:02:46 -0500
commit58498ee3e573dc03be651b6839dbdf865ae7ee38 (patch)
treecea8b9834cc8ad9de2d962015502e47e91819341 /arch/s390/kernel
parentfbc89c952f004fb9191c23605a1428df6dd39a90 (diff)
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>
Diffstat (limited to 'arch/s390/kernel')
-rw-r--r--arch/s390/kernel/ftrace.c95
1 files changed, 49 insertions, 46 deletions
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,
59int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, 59int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
60 unsigned long addr) 60 unsigned long addr)
61{ 61{
62 struct ftrace_insn insn; 62 struct ftrace_insn orig, new, old;
63 unsigned short op; 63
64 void *from, *to; 64 if (probe_kernel_read(&old, (void *) rec->ip, sizeof(old)))
65 size_t size;
66
67 ftrace_generate_nop_insn(&insn);
68 size = sizeof(insn);
69 from = &insn;
70 to = (void *) rec->ip;
71 if (probe_kernel_read(&op, (void *) rec->ip, sizeof(op)))
72 return -EFAULT; 65 return -EFAULT;
73 /* 66 if (addr == MCOUNT_ADDR) {
74 * If we find a breakpoint instruction, a kprobe has been placed 67 /* Initial code replacement; we expect to see stg r14,8(r15) */
75 * at the beginning of the function. We write the constant 68 orig.opc = 0xe3e0;
76 * KPROBE_ON_FTRACE_NOP into the remaining four bytes of the original 69 orig.disp = 0xf0080024;
77 * instruction so that the kprobes handler can execute a nop, if it 70 ftrace_generate_nop_insn(&new);
78 * reaches this breakpoint. 71 } else if (old.opc == BREAKPOINT_INSTRUCTION) {
79 */ 72 /*
80 if (op == BREAKPOINT_INSTRUCTION) { 73 * If we find a breakpoint instruction, a kprobe has been
81 size -= 2; 74 * placed at the beginning of the function. We write the
82 from += 2; 75 * constant KPROBE_ON_FTRACE_NOP into the remaining four
83 to += 2; 76 * bytes of the original instruction so that the kprobes
84 insn.disp = KPROBE_ON_FTRACE_NOP; 77 * handler can execute a nop, if it reaches this breakpoint.
78 */
79 new.opc = orig.opc = BREAKPOINT_INSTRUCTION;
80 orig.disp = KPROBE_ON_FTRACE_CALL;
81 new.disp = KPROBE_ON_FTRACE_NOP;
82 } else {
83 /* Replace ftrace call with a nop. */
84 ftrace_generate_call_insn(&orig, rec->ip);
85 ftrace_generate_nop_insn(&new);
85 } 86 }
86 if (probe_kernel_write(to, from, size)) 87 /* Verify that the to be replaced code matches what we expect. */
88 if (memcmp(&orig, &old, sizeof(old)))
89 return -EINVAL;
90 if (probe_kernel_write((void *) rec->ip, &new, sizeof(new)))
87 return -EPERM; 91 return -EPERM;
88 return 0; 92 return 0;
89} 93}
90 94
91int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) 95int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
92{ 96{
93 struct ftrace_insn insn; 97 struct ftrace_insn orig, new, old;
94 unsigned short op; 98
95 void *from, *to; 99 if (probe_kernel_read(&old, (void *) rec->ip, sizeof(old)))
96 size_t size;
97
98 ftrace_generate_call_insn(&insn, rec->ip);
99 size = sizeof(insn);
100 from = &insn;
101 to = (void *) rec->ip;
102 if (probe_kernel_read(&op, (void *) rec->ip, sizeof(op)))
103 return -EFAULT; 100 return -EFAULT;
104 /* 101 if (old.opc == BREAKPOINT_INSTRUCTION) {
105 * If we find a breakpoint instruction, a kprobe has been placed 102 /*
106 * at the beginning of the function. We write the constant 103 * If we find a breakpoint instruction, a kprobe has been
107 * KPROBE_ON_FTRACE_CALL into the remaining four bytes of the original 104 * placed at the beginning of the function. We write the
108 * instruction so that the kprobes handler can execute a brasl if it 105 * constant KPROBE_ON_FTRACE_CALL into the remaining four
109 * reaches this breakpoint. 106 * bytes of the original instruction so that the kprobes
110 */ 107 * handler can execute a brasl if it reaches this breakpoint.
111 if (op == BREAKPOINT_INSTRUCTION) { 108 */
112 size -= 2; 109 new.opc = orig.opc = BREAKPOINT_INSTRUCTION;
113 from += 2; 110 orig.disp = KPROBE_ON_FTRACE_NOP;
114 to += 2; 111 new.disp = KPROBE_ON_FTRACE_CALL;
115 insn.disp = KPROBE_ON_FTRACE_CALL; 112 } else {
113 /* Replace nop with an ftrace call. */
114 ftrace_generate_nop_insn(&orig);
115 ftrace_generate_call_insn(&new, rec->ip);
116 } 116 }
117 if (probe_kernel_write(to, from, size)) 117 /* Verify that the to be replaced code matches what we expect. */
118 if (memcmp(&orig, &old, sizeof(old)))
119 return -EINVAL;
120 if (probe_kernel_write((void *) rec->ip, &new, sizeof(new)))
118 return -EPERM; 121 return -EPERM;
119 return 0; 122 return 0;
120} 123}