diff options
author | Heiko Carstens <heiko.carstens@de.ibm.com> | 2014-12-09 04:18:49 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2015-01-08 04:02:46 -0500 |
commit | 58498ee3e573dc03be651b6839dbdf865ae7ee38 (patch) | |
tree | cea8b9834cc8ad9de2d962015502e47e91819341 /arch/s390/kernel | |
parent | fbc89c952f004fb9191c23605a1428df6dd39a90 (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.c | 95 |
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, | |||
59 | int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, | 59 | int 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 | ||
91 | int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | 95 | int 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 | } |