aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390
diff options
context:
space:
mode:
authorHeiko Carstens <heiko.carstens@de.ibm.com>2015-03-13 07:55:56 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2015-03-25 06:49:43 -0400
commit8a5d8473dd7e2b0bc2864e34bd6836b520589fa1 (patch)
tree36997ac10fc75d5a0ed5fe5632e58684f90edf24 /arch/s390
parentb2527d2009903a06b0076bb89166c72a0f17823b (diff)
s390/maccess: remove potentially broken probe_kernel_write()
Remove the s390 architecture implementation of probe_kernel_write() and instead use a new function s390_kernel_write() to modify kernel text and data everywhere. The s390 implementation of probe_kernel_write() was potentially broken since it modified memory in a read-modify-write fashion, which read four bytes, modified the requested bytes within those four bytes and wrote the result back. If two cpus would modify the same four byte area at different locations within that area, this could lead to corruption. Right now the only places which called probe_kernel_write() did run within stop_machine_run. Therefore the scenario can't happen right now, however that might change at any time. To fix this rename probe_kernel_write() to s390_kernel_write() which can have special semantics, like only call it while running within stop_machine(). Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390')
-rw-r--r--arch/s390/include/asm/uaccess.h1
-rw-r--r--arch/s390/kernel/ftrace.c12
-rw-r--r--arch/s390/kernel/jump_label.c2
-rw-r--r--arch/s390/kernel/kprobes.c2
-rw-r--r--arch/s390/mm/maccess.c29
5 files changed, 28 insertions, 18 deletions
diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h
index cd4c68e0398d..d64a7a62164f 100644
--- a/arch/s390/include/asm/uaccess.h
+++ b/arch/s390/include/asm/uaccess.h
@@ -372,5 +372,6 @@ static inline unsigned long __must_check clear_user(void __user *to, unsigned lo
372} 372}
373 373
374int copy_to_user_real(void __user *dest, void *src, unsigned long count); 374int copy_to_user_real(void __user *dest, void *src, unsigned long count);
375void s390_kernel_write(void *dst, const void *src, size_t size);
375 376
376#endif /* __S390_UACCESS_H */ 377#endif /* __S390_UACCESS_H */
diff --git a/arch/s390/kernel/ftrace.c b/arch/s390/kernel/ftrace.c
index 6c79f1b44fe7..e0eaf11134b4 100644
--- a/arch/s390/kernel/ftrace.c
+++ b/arch/s390/kernel/ftrace.c
@@ -130,8 +130,7 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
130 /* Verify that the to be replaced code matches what we expect. */ 130 /* Verify that the to be replaced code matches what we expect. */
131 if (memcmp(&orig, &old, sizeof(old))) 131 if (memcmp(&orig, &old, sizeof(old)))
132 return -EINVAL; 132 return -EINVAL;
133 if (probe_kernel_write((void *) rec->ip, &new, sizeof(new))) 133 s390_kernel_write((void *) rec->ip, &new, sizeof(new));
134 return -EPERM;
135 return 0; 134 return 0;
136} 135}
137 136
@@ -159,8 +158,7 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
159 /* Verify that the to be replaced code matches what we expect. */ 158 /* Verify that the to be replaced code matches what we expect. */
160 if (memcmp(&orig, &old, sizeof(old))) 159 if (memcmp(&orig, &old, sizeof(old)))
161 return -EINVAL; 160 return -EINVAL;
162 if (probe_kernel_write((void *) rec->ip, &new, sizeof(new))) 161 s390_kernel_write((void *) rec->ip, &new, sizeof(new));
163 return -EPERM;
164 return 0; 162 return 0;
165} 163}
166 164
@@ -231,14 +229,16 @@ int ftrace_enable_ftrace_graph_caller(void)
231{ 229{
232 u8 op = 0x04; /* set mask field to zero */ 230 u8 op = 0x04; /* set mask field to zero */
233 231
234 return probe_kernel_write(__va(ftrace_graph_caller)+1, &op, sizeof(op)); 232 s390_kernel_write(__va(ftrace_graph_caller)+1, &op, sizeof(op));
233 return 0;
235} 234}
236 235
237int ftrace_disable_ftrace_graph_caller(void) 236int ftrace_disable_ftrace_graph_caller(void)
238{ 237{
239 u8 op = 0xf4; /* set mask field to all ones */ 238 u8 op = 0xf4; /* set mask field to all ones */
240 239
241 return probe_kernel_write(__va(ftrace_graph_caller)+1, &op, sizeof(op)); 240 s390_kernel_write(__va(ftrace_graph_caller)+1, &op, sizeof(op));
241 return 0;
242} 242}
243 243
244#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ 244#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
diff --git a/arch/s390/kernel/jump_label.c b/arch/s390/kernel/jump_label.c
index 830066f936c8..a90299600483 100644
--- a/arch/s390/kernel/jump_label.c
+++ b/arch/s390/kernel/jump_label.c
@@ -78,7 +78,7 @@ static void __jump_label_transform(struct jump_entry *entry,
78 if (memcmp((void *)entry->code, &old, sizeof(old))) 78 if (memcmp((void *)entry->code, &old, sizeof(old)))
79 jump_label_bug(entry, &old, &new); 79 jump_label_bug(entry, &old, &new);
80 } 80 }
81 probe_kernel_write((void *)entry->code, &new, sizeof(new)); 81 s390_kernel_write((void *)entry->code, &new, sizeof(new));
82} 82}
83 83
84static int __sm_arch_jump_label_transform(void *data) 84static int __sm_arch_jump_label_transform(void *data)
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c
index f516edc1fbe3..389db56a2208 100644
--- a/arch/s390/kernel/kprobes.c
+++ b/arch/s390/kernel/kprobes.c
@@ -178,7 +178,7 @@ static int swap_instruction(void *data)
178 } 178 }
179skip_ftrace: 179skip_ftrace:
180 kcb->kprobe_status = KPROBE_SWAP_INST; 180 kcb->kprobe_status = KPROBE_SWAP_INST;
181 probe_kernel_write(p->addr, &new_insn, len); 181 s390_kernel_write(p->addr, &new_insn, len);
182 kcb->kprobe_status = status; 182 kcb->kprobe_status = status;
183 return 0; 183 return 0;
184} 184}
diff --git a/arch/s390/mm/maccess.c b/arch/s390/mm/maccess.c
index 2eb34bdfc613..fb737e9e0683 100644
--- a/arch/s390/mm/maccess.c
+++ b/arch/s390/mm/maccess.c
@@ -16,13 +16,7 @@
16#include <asm/ctl_reg.h> 16#include <asm/ctl_reg.h>
17#include <asm/io.h> 17#include <asm/io.h>
18 18
19/* 19static notrace long s390_kernel_write_odd(void *dst, const void *src, size_t size)
20 * This function writes to kernel memory bypassing DAT and possible
21 * write protection. It copies one to four bytes from src to dst
22 * using the stura instruction.
23 * Returns the number of bytes copied or -EFAULT.
24 */
25static long probe_kernel_write_odd(void *dst, const void *src, size_t size)
26{ 20{
27 unsigned long count, aligned; 21 unsigned long count, aligned;
28 int offset, mask; 22 int offset, mask;
@@ -48,19 +42,34 @@ static long probe_kernel_write_odd(void *dst, const void *src, size_t size)
48 return rc ? rc : count; 42 return rc ? rc : count;
49} 43}
50 44
51long probe_kernel_write(void *dst, const void *src, size_t size) 45/*
46 * s390_kernel_write - write to kernel memory bypassing DAT
47 * @dst: destination address
48 * @src: source address
49 * @size: number of bytes to copy
50 *
51 * This function writes to kernel memory bypassing DAT and possible page table
52 * write protection. It writes to the destination using the sturg instruction.
53 * Therefore we have a read-modify-write sequence: the function reads four
54 * bytes from destination at a four byte boundary, modifies the bytes
55 * requested and writes the result back in a loop.
56 *
57 * Note: this means that this function may not be called concurrently on
58 * several cpus with overlapping words, since this may potentially
59 * cause data corruption.
60 */
61void notrace s390_kernel_write(void *dst, const void *src, size_t size)
52{ 62{
53 long copied = 0; 63 long copied = 0;
54 64
55 while (size) { 65 while (size) {
56 copied = probe_kernel_write_odd(dst, src, size); 66 copied = s390_kernel_write_odd(dst, src, size);
57 if (copied < 0) 67 if (copied < 0)
58 break; 68 break;
59 dst += copied; 69 dst += copied;
60 src += copied; 70 src += copied;
61 size -= copied; 71 size -= copied;
62 } 72 }
63 return copied < 0 ? -EFAULT : 0;
64} 73}
65 74
66static int __memcpy_real(void *dest, void *src, size_t count) 75static int __memcpy_real(void *dest, void *src, size_t count)