diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-03-03 15:58:43 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-03-03 15:58:43 -0500 |
commit | 530ede14cf63ef4a207b0003e14af2f750484180 (patch) | |
tree | 2849be2c8fd6d2d6a8a71447aebe5d7400317c8d /arch/s390 | |
parent | 6977c6fc77b864dc1851e100c8528c2f3ed5bd9b (diff) | |
parent | 72a6b43e4bbb818ac1875a3a572cb2aa8c9af820 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
Pull second set of s390 patches from Martin Schwidefsky:
"The main part of this merge are Heikos uaccess patches. Together with
commit 09884964335e ("mm: do not grow the stack vma just because of an
overrun on preceding vma") the user string access is hopefully fixed
for good.
In addition some bug fixes and two cleanup patches."
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux:
s390/module: fix compile warning
qdio: remove unused parameters
s390/uaccess: fix kernel ds access for page table walk
s390/uaccess: fix strncpy_from_user string length check
input: disable i8042 PC Keyboard controller for s390
s390/dis: Fix invalid array size
s390/uaccess: remove pointless access_ok() checks
s390/uaccess: fix strncpy_from_user/strnlen_user zero maxlen case
s390/uaccess: shorten strncpy_from_user/strnlen_user
s390/dasd: fix unresponsive device after all channel paths were lost
s390/mm: ignore change bit for vmemmap
s390/page table dumper: add support for change-recording override bit
Diffstat (limited to 'arch/s390')
-rw-r--r-- | arch/s390/include/asm/futex.h | 6 | ||||
-rw-r--r-- | arch/s390/include/asm/pgtable.h | 2 | ||||
-rw-r--r-- | arch/s390/include/asm/uaccess.h | 23 | ||||
-rw-r--r-- | arch/s390/kernel/compat_signal.c | 14 | ||||
-rw-r--r-- | arch/s390/kernel/dis.c | 4 | ||||
-rw-r--r-- | arch/s390/kernel/module.c | 2 | ||||
-rw-r--r-- | arch/s390/kernel/signal.c | 8 | ||||
-rw-r--r-- | arch/s390/lib/uaccess_mvcos.c | 26 | ||||
-rw-r--r-- | arch/s390/lib/uaccess_pt.c | 129 | ||||
-rw-r--r-- | arch/s390/lib/uaccess_std.c | 48 | ||||
-rw-r--r-- | arch/s390/mm/dump_pagetables.c | 25 | ||||
-rw-r--r-- | arch/s390/mm/vmem.c | 3 |
12 files changed, 156 insertions, 134 deletions
diff --git a/arch/s390/include/asm/futex.h b/arch/s390/include/asm/futex.h index 96bc83ea5c90..51bcaa0fdeef 100644 --- a/arch/s390/include/asm/futex.h +++ b/arch/s390/include/asm/futex.h | |||
@@ -16,9 +16,6 @@ static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) | |||
16 | if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) | 16 | if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) |
17 | oparg = 1 << oparg; | 17 | oparg = 1 << oparg; |
18 | 18 | ||
19 | if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32))) | ||
20 | return -EFAULT; | ||
21 | |||
22 | pagefault_disable(); | 19 | pagefault_disable(); |
23 | ret = uaccess.futex_atomic_op(op, uaddr, oparg, &oldval); | 20 | ret = uaccess.futex_atomic_op(op, uaddr, oparg, &oldval); |
24 | pagefault_enable(); | 21 | pagefault_enable(); |
@@ -40,9 +37,6 @@ static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) | |||
40 | static inline int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, | 37 | static inline int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, |
41 | u32 oldval, u32 newval) | 38 | u32 oldval, u32 newval) |
42 | { | 39 | { |
43 | if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32))) | ||
44 | return -EFAULT; | ||
45 | |||
46 | return uaccess.futex_atomic_cmpxchg(uval, uaddr, oldval, newval); | 40 | return uaccess.futex_atomic_cmpxchg(uval, uaddr, oldval, newval); |
47 | } | 41 | } |
48 | 42 | ||
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 97de1200c849..4a2930844d43 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h | |||
@@ -340,6 +340,8 @@ extern unsigned long MODULES_END; | |||
340 | #define _REGION3_ENTRY_EMPTY (_REGION_ENTRY_TYPE_R3 | _REGION_ENTRY_INV) | 340 | #define _REGION3_ENTRY_EMPTY (_REGION_ENTRY_TYPE_R3 | _REGION_ENTRY_INV) |
341 | 341 | ||
342 | #define _REGION3_ENTRY_LARGE 0x400 /* RTTE-format control, large page */ | 342 | #define _REGION3_ENTRY_LARGE 0x400 /* RTTE-format control, large page */ |
343 | #define _REGION3_ENTRY_RO 0x200 /* page protection bit */ | ||
344 | #define _REGION3_ENTRY_CO 0x100 /* change-recording override */ | ||
343 | 345 | ||
344 | /* Bits in the segment table entry */ | 346 | /* Bits in the segment table entry */ |
345 | #define _SEGMENT_ENTRY_ORIGIN ~0x7ffUL/* segment table origin */ | 347 | #define _SEGMENT_ENTRY_ORIGIN ~0x7ffUL/* segment table origin */ |
diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h index 34268df959a3..9c33ed4e666f 100644 --- a/arch/s390/include/asm/uaccess.h +++ b/arch/s390/include/asm/uaccess.h | |||
@@ -252,9 +252,7 @@ static inline unsigned long __must_check | |||
252 | copy_to_user(void __user *to, const void *from, unsigned long n) | 252 | copy_to_user(void __user *to, const void *from, unsigned long n) |
253 | { | 253 | { |
254 | might_fault(); | 254 | might_fault(); |
255 | if (access_ok(VERIFY_WRITE, to, n)) | 255 | return __copy_to_user(to, from, n); |
256 | n = __copy_to_user(to, from, n); | ||
257 | return n; | ||
258 | } | 256 | } |
259 | 257 | ||
260 | /** | 258 | /** |
@@ -315,11 +313,7 @@ copy_from_user(void *to, const void __user *from, unsigned long n) | |||
315 | copy_from_user_overflow(); | 313 | copy_from_user_overflow(); |
316 | return n; | 314 | return n; |
317 | } | 315 | } |
318 | if (access_ok(VERIFY_READ, from, n)) | 316 | return __copy_from_user(to, from, n); |
319 | n = __copy_from_user(to, from, n); | ||
320 | else | ||
321 | memset(to, 0, n); | ||
322 | return n; | ||
323 | } | 317 | } |
324 | 318 | ||
325 | static inline unsigned long __must_check | 319 | static inline unsigned long __must_check |
@@ -332,9 +326,7 @@ static inline unsigned long __must_check | |||
332 | copy_in_user(void __user *to, const void __user *from, unsigned long n) | 326 | copy_in_user(void __user *to, const void __user *from, unsigned long n) |
333 | { | 327 | { |
334 | might_fault(); | 328 | might_fault(); |
335 | if (__access_ok(from,n) && __access_ok(to,n)) | 329 | return __copy_in_user(to, from, n); |
336 | n = __copy_in_user(to, from, n); | ||
337 | return n; | ||
338 | } | 330 | } |
339 | 331 | ||
340 | /* | 332 | /* |
@@ -343,11 +335,8 @@ copy_in_user(void __user *to, const void __user *from, unsigned long n) | |||
343 | static inline long __must_check | 335 | static inline long __must_check |
344 | strncpy_from_user(char *dst, const char __user *src, long count) | 336 | strncpy_from_user(char *dst, const char __user *src, long count) |
345 | { | 337 | { |
346 | long res = -EFAULT; | ||
347 | might_fault(); | 338 | might_fault(); |
348 | if (access_ok(VERIFY_READ, src, 1)) | 339 | return uaccess.strncpy_from_user(count, src, dst); |
349 | res = uaccess.strncpy_from_user(count, src, dst); | ||
350 | return res; | ||
351 | } | 340 | } |
352 | 341 | ||
353 | static inline unsigned long | 342 | static inline unsigned long |
@@ -387,9 +376,7 @@ static inline unsigned long __must_check | |||
387 | clear_user(void __user *to, unsigned long n) | 376 | clear_user(void __user *to, unsigned long n) |
388 | { | 377 | { |
389 | might_fault(); | 378 | might_fault(); |
390 | if (access_ok(VERIFY_WRITE, to, n)) | 379 | return uaccess.clear_user(n, to); |
391 | n = uaccess.clear_user(n, to); | ||
392 | return n; | ||
393 | } | 380 | } |
394 | 381 | ||
395 | extern int copy_to_user_real(void __user *dest, void *src, size_t count); | 382 | extern int copy_to_user_real(void __user *dest, void *src, size_t count); |
diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c index 3e71194c1902..6de049fbe62d 100644 --- a/arch/s390/kernel/compat_signal.c +++ b/arch/s390/kernel/compat_signal.c | |||
@@ -53,9 +53,6 @@ int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from) | |||
53 | { | 53 | { |
54 | int err; | 54 | int err; |
55 | 55 | ||
56 | if (!access_ok (VERIFY_WRITE, to, sizeof(compat_siginfo_t))) | ||
57 | return -EFAULT; | ||
58 | |||
59 | /* If you change siginfo_t structure, please be sure | 56 | /* If you change siginfo_t structure, please be sure |
60 | this code is fixed accordingly. | 57 | this code is fixed accordingly. |
61 | It should never copy any pad contained in the structure | 58 | It should never copy any pad contained in the structure |
@@ -110,9 +107,6 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from) | |||
110 | int err; | 107 | int err; |
111 | u32 tmp; | 108 | u32 tmp; |
112 | 109 | ||
113 | if (!access_ok (VERIFY_READ, from, sizeof(compat_siginfo_t))) | ||
114 | return -EFAULT; | ||
115 | |||
116 | err = __get_user(to->si_signo, &from->si_signo); | 110 | err = __get_user(to->si_signo, &from->si_signo); |
117 | err |= __get_user(to->si_errno, &from->si_errno); | 111 | err |= __get_user(to->si_errno, &from->si_errno); |
118 | err |= __get_user(to->si_code, &from->si_code); | 112 | err |= __get_user(to->si_code, &from->si_code); |
@@ -244,8 +238,6 @@ asmlinkage long sys32_sigreturn(void) | |||
244 | sigframe32 __user *frame = (sigframe32 __user *)regs->gprs[15]; | 238 | sigframe32 __user *frame = (sigframe32 __user *)regs->gprs[15]; |
245 | sigset_t set; | 239 | sigset_t set; |
246 | 240 | ||
247 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
248 | goto badframe; | ||
249 | if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE32)) | 241 | if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE32)) |
250 | goto badframe; | 242 | goto badframe; |
251 | set_current_blocked(&set); | 243 | set_current_blocked(&set); |
@@ -265,8 +257,6 @@ asmlinkage long sys32_rt_sigreturn(void) | |||
265 | rt_sigframe32 __user *frame = (rt_sigframe32 __user *)regs->gprs[15]; | 257 | rt_sigframe32 __user *frame = (rt_sigframe32 __user *)regs->gprs[15]; |
266 | sigset_t set; | 258 | sigset_t set; |
267 | 259 | ||
268 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
269 | goto badframe; | ||
270 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | 260 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) |
271 | goto badframe; | 261 | goto badframe; |
272 | set_current_blocked(&set); | 262 | set_current_blocked(&set); |
@@ -325,8 +315,6 @@ static int setup_frame32(int sig, struct k_sigaction *ka, | |||
325 | sigset_t *set, struct pt_regs * regs) | 315 | sigset_t *set, struct pt_regs * regs) |
326 | { | 316 | { |
327 | sigframe32 __user *frame = get_sigframe(ka, regs, sizeof(sigframe32)); | 317 | sigframe32 __user *frame = get_sigframe(ka, regs, sizeof(sigframe32)); |
328 | if (!access_ok(VERIFY_WRITE, frame, sizeof(sigframe32))) | ||
329 | goto give_sigsegv; | ||
330 | 318 | ||
331 | if (frame == (void __user *) -1UL) | 319 | if (frame == (void __user *) -1UL) |
332 | goto give_sigsegv; | 320 | goto give_sigsegv; |
@@ -391,8 +379,6 @@ static int setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
391 | { | 379 | { |
392 | int err = 0; | 380 | int err = 0; |
393 | rt_sigframe32 __user *frame = get_sigframe(ka, regs, sizeof(rt_sigframe32)); | 381 | rt_sigframe32 __user *frame = get_sigframe(ka, regs, sizeof(rt_sigframe32)); |
394 | if (!access_ok(VERIFY_WRITE, frame, sizeof(rt_sigframe32))) | ||
395 | goto give_sigsegv; | ||
396 | 382 | ||
397 | if (frame == (void __user *) -1UL) | 383 | if (frame == (void __user *) -1UL) |
398 | goto give_sigsegv; | 384 | goto give_sigsegv; |
diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c index c50665fe9435..3ad5e9540160 100644 --- a/arch/s390/kernel/dis.c +++ b/arch/s390/kernel/dis.c | |||
@@ -1711,10 +1711,10 @@ int insn_to_mnemonic(unsigned char *instruction, char buf[8]) | |||
1711 | if (!insn) | 1711 | if (!insn) |
1712 | return -ENOENT; | 1712 | return -ENOENT; |
1713 | if (insn->name[0] == '\0') | 1713 | if (insn->name[0] == '\0') |
1714 | snprintf(buf, sizeof(buf), "%s", | 1714 | snprintf(buf, 8, "%s", |
1715 | long_insn_name[(int) insn->name[1]]); | 1715 | long_insn_name[(int) insn->name[1]]); |
1716 | else | 1716 | else |
1717 | snprintf(buf, sizeof(buf), "%.5s", insn->name); | 1717 | snprintf(buf, 8, "%.5s", insn->name); |
1718 | return 0; | 1718 | return 0; |
1719 | } | 1719 | } |
1720 | EXPORT_SYMBOL_GPL(insn_to_mnemonic); | 1720 | EXPORT_SYMBOL_GPL(insn_to_mnemonic); |
diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c index f750bd7bd2c2..7845e15a17df 100644 --- a/arch/s390/kernel/module.c +++ b/arch/s390/kernel/module.c | |||
@@ -222,7 +222,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, | |||
222 | struct mod_arch_syminfo *info; | 222 | struct mod_arch_syminfo *info; |
223 | Elf_Addr loc, val; | 223 | Elf_Addr loc, val; |
224 | int r_type, r_sym; | 224 | int r_type, r_sym; |
225 | int rc; | 225 | int rc = -ENOEXEC; |
226 | 226 | ||
227 | /* This is where to make the change */ | 227 | /* This is where to make the change */ |
228 | loc = base + rela->r_offset; | 228 | loc = base + rela->r_offset; |
diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index 9c6e747a5e1e..c45becf82e01 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c | |||
@@ -116,8 +116,6 @@ SYSCALL_DEFINE0(sigreturn) | |||
116 | sigframe __user *frame = (sigframe __user *)regs->gprs[15]; | 116 | sigframe __user *frame = (sigframe __user *)regs->gprs[15]; |
117 | sigset_t set; | 117 | sigset_t set; |
118 | 118 | ||
119 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
120 | goto badframe; | ||
121 | if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE)) | 119 | if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE)) |
122 | goto badframe; | 120 | goto badframe; |
123 | set_current_blocked(&set); | 121 | set_current_blocked(&set); |
@@ -135,8 +133,6 @@ SYSCALL_DEFINE0(rt_sigreturn) | |||
135 | rt_sigframe __user *frame = (rt_sigframe __user *)regs->gprs[15]; | 133 | rt_sigframe __user *frame = (rt_sigframe __user *)regs->gprs[15]; |
136 | sigset_t set; | 134 | sigset_t set; |
137 | 135 | ||
138 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
139 | goto badframe; | ||
140 | if (__copy_from_user(&set.sig, &frame->uc.uc_sigmask, sizeof(set))) | 136 | if (__copy_from_user(&set.sig, &frame->uc.uc_sigmask, sizeof(set))) |
141 | goto badframe; | 137 | goto badframe; |
142 | set_current_blocked(&set); | 138 | set_current_blocked(&set); |
@@ -195,8 +191,6 @@ static int setup_frame(int sig, struct k_sigaction *ka, | |||
195 | sigframe __user *frame; | 191 | sigframe __user *frame; |
196 | 192 | ||
197 | frame = get_sigframe(ka, regs, sizeof(sigframe)); | 193 | frame = get_sigframe(ka, regs, sizeof(sigframe)); |
198 | if (!access_ok(VERIFY_WRITE, frame, sizeof(sigframe))) | ||
199 | goto give_sigsegv; | ||
200 | 194 | ||
201 | if (frame == (void __user *) -1UL) | 195 | if (frame == (void __user *) -1UL) |
202 | goto give_sigsegv; | 196 | goto give_sigsegv; |
@@ -264,8 +258,6 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
264 | rt_sigframe __user *frame; | 258 | rt_sigframe __user *frame; |
265 | 259 | ||
266 | frame = get_sigframe(ka, regs, sizeof(rt_sigframe)); | 260 | frame = get_sigframe(ka, regs, sizeof(rt_sigframe)); |
267 | if (!access_ok(VERIFY_WRITE, frame, sizeof(rt_sigframe))) | ||
268 | goto give_sigsegv; | ||
269 | 261 | ||
270 | if (frame == (void __user *) -1UL) | 262 | if (frame == (void __user *) -1UL) |
271 | goto give_sigsegv; | 263 | goto give_sigsegv; |
diff --git a/arch/s390/lib/uaccess_mvcos.c b/arch/s390/lib/uaccess_mvcos.c index 2443ae476e33..1829742bf479 100644 --- a/arch/s390/lib/uaccess_mvcos.c +++ b/arch/s390/lib/uaccess_mvcos.c | |||
@@ -162,19 +162,19 @@ static size_t clear_user_mvcos(size_t size, void __user *to) | |||
162 | 162 | ||
163 | static size_t strnlen_user_mvcos(size_t count, const char __user *src) | 163 | static size_t strnlen_user_mvcos(size_t count, const char __user *src) |
164 | { | 164 | { |
165 | size_t done, len, offset, len_str; | ||
165 | char buf[256]; | 166 | char buf[256]; |
166 | int rc; | ||
167 | size_t done, len, len_str; | ||
168 | 167 | ||
169 | done = 0; | 168 | done = 0; |
170 | do { | 169 | do { |
171 | len = min(count - done, (size_t) 256); | 170 | offset = (size_t)src & ~PAGE_MASK; |
172 | rc = uaccess.copy_from_user(len, src + done, buf); | 171 | len = min(256UL, PAGE_SIZE - offset); |
173 | if (unlikely(rc == len)) | 172 | len = min(count - done, len); |
173 | if (copy_from_user_mvcos(len, src, buf)) | ||
174 | return 0; | 174 | return 0; |
175 | len -= rc; | ||
176 | len_str = strnlen(buf, len); | 175 | len_str = strnlen(buf, len); |
177 | done += len_str; | 176 | done += len_str; |
177 | src += len_str; | ||
178 | } while ((len_str == len) && (done < count)); | 178 | } while ((len_str == len) && (done < count)); |
179 | return done + 1; | 179 | return done + 1; |
180 | } | 180 | } |
@@ -182,18 +182,20 @@ static size_t strnlen_user_mvcos(size_t count, const char __user *src) | |||
182 | static size_t strncpy_from_user_mvcos(size_t count, const char __user *src, | 182 | static size_t strncpy_from_user_mvcos(size_t count, const char __user *src, |
183 | char *dst) | 183 | char *dst) |
184 | { | 184 | { |
185 | int rc; | 185 | size_t done, len, offset, len_str; |
186 | size_t done, len, len_str; | ||
187 | 186 | ||
187 | if (unlikely(!count)) | ||
188 | return 0; | ||
188 | done = 0; | 189 | done = 0; |
189 | do { | 190 | do { |
190 | len = min(count - done, (size_t) 4096); | 191 | offset = (size_t)src & ~PAGE_MASK; |
191 | rc = uaccess.copy_from_user(len, src + done, dst); | 192 | len = min(count - done, PAGE_SIZE - offset); |
192 | if (unlikely(rc == len)) | 193 | if (copy_from_user_mvcos(len, src, dst)) |
193 | return -EFAULT; | 194 | return -EFAULT; |
194 | len -= rc; | ||
195 | len_str = strnlen(dst, len); | 195 | len_str = strnlen(dst, len); |
196 | done += len_str; | 196 | done += len_str; |
197 | src += len_str; | ||
198 | dst += len_str; | ||
197 | } while ((len_str == len) && (done < count)); | 199 | } while ((len_str == len) && (done < count)); |
198 | return done; | 200 | return done; |
199 | } | 201 | } |
diff --git a/arch/s390/lib/uaccess_pt.c b/arch/s390/lib/uaccess_pt.c index a70ee84c0241..dff631d34b45 100644 --- a/arch/s390/lib/uaccess_pt.c +++ b/arch/s390/lib/uaccess_pt.c | |||
@@ -14,6 +14,63 @@ | |||
14 | #include <asm/futex.h> | 14 | #include <asm/futex.h> |
15 | #include "uaccess.h" | 15 | #include "uaccess.h" |
16 | 16 | ||
17 | #ifndef CONFIG_64BIT | ||
18 | #define AHI "ahi" | ||
19 | #define SLR "slr" | ||
20 | #else | ||
21 | #define AHI "aghi" | ||
22 | #define SLR "slgr" | ||
23 | #endif | ||
24 | |||
25 | static size_t strnlen_kernel(size_t count, const char __user *src) | ||
26 | { | ||
27 | register unsigned long reg0 asm("0") = 0UL; | ||
28 | unsigned long tmp1, tmp2; | ||
29 | |||
30 | asm volatile( | ||
31 | " la %2,0(%1)\n" | ||
32 | " la %3,0(%0,%1)\n" | ||
33 | " "SLR" %0,%0\n" | ||
34 | "0: srst %3,%2\n" | ||
35 | " jo 0b\n" | ||
36 | " la %0,1(%3)\n" /* strnlen_kernel results includes \0 */ | ||
37 | " "SLR" %0,%1\n" | ||
38 | "1:\n" | ||
39 | EX_TABLE(0b,1b) | ||
40 | : "+a" (count), "+a" (src), "=a" (tmp1), "=a" (tmp2) | ||
41 | : "d" (reg0) : "cc", "memory"); | ||
42 | return count; | ||
43 | } | ||
44 | |||
45 | static size_t copy_in_kernel(size_t count, void __user *to, | ||
46 | const void __user *from) | ||
47 | { | ||
48 | unsigned long tmp1; | ||
49 | |||
50 | asm volatile( | ||
51 | " "AHI" %0,-1\n" | ||
52 | " jo 5f\n" | ||
53 | " bras %3,3f\n" | ||
54 | "0:"AHI" %0,257\n" | ||
55 | "1: mvc 0(1,%1),0(%2)\n" | ||
56 | " la %1,1(%1)\n" | ||
57 | " la %2,1(%2)\n" | ||
58 | " "AHI" %0,-1\n" | ||
59 | " jnz 1b\n" | ||
60 | " j 5f\n" | ||
61 | "2: mvc 0(256,%1),0(%2)\n" | ||
62 | " la %1,256(%1)\n" | ||
63 | " la %2,256(%2)\n" | ||
64 | "3:"AHI" %0,-256\n" | ||
65 | " jnm 2b\n" | ||
66 | "4: ex %0,1b-0b(%3)\n" | ||
67 | "5:"SLR" %0,%0\n" | ||
68 | "6:\n" | ||
69 | EX_TABLE(1b,6b) EX_TABLE(2b,0b) EX_TABLE(4b,0b) | ||
70 | : "+a" (count), "+a" (to), "+a" (from), "=a" (tmp1) | ||
71 | : : "cc", "memory"); | ||
72 | return count; | ||
73 | } | ||
17 | 74 | ||
18 | /* | 75 | /* |
19 | * Returns kernel address for user virtual address. If the returned address is | 76 | * Returns kernel address for user virtual address. If the returned address is |
@@ -123,10 +180,8 @@ size_t copy_from_user_pt(size_t n, const void __user *from, void *to) | |||
123 | { | 180 | { |
124 | size_t rc; | 181 | size_t rc; |
125 | 182 | ||
126 | if (segment_eq(get_fs(), KERNEL_DS)) { | 183 | if (segment_eq(get_fs(), KERNEL_DS)) |
127 | memcpy(to, (void __kernel __force *) from, n); | 184 | return copy_in_kernel(n, (void __user *) to, from); |
128 | return 0; | ||
129 | } | ||
130 | rc = __user_copy_pt((unsigned long) from, to, n, 0); | 185 | rc = __user_copy_pt((unsigned long) from, to, n, 0); |
131 | if (unlikely(rc)) | 186 | if (unlikely(rc)) |
132 | memset(to + n - rc, 0, rc); | 187 | memset(to + n - rc, 0, rc); |
@@ -135,30 +190,28 @@ size_t copy_from_user_pt(size_t n, const void __user *from, void *to) | |||
135 | 190 | ||
136 | size_t copy_to_user_pt(size_t n, void __user *to, const void *from) | 191 | size_t copy_to_user_pt(size_t n, void __user *to, const void *from) |
137 | { | 192 | { |
138 | if (segment_eq(get_fs(), KERNEL_DS)) { | 193 | if (segment_eq(get_fs(), KERNEL_DS)) |
139 | memcpy((void __kernel __force *) to, from, n); | 194 | return copy_in_kernel(n, to, (void __user *) from); |
140 | return 0; | ||
141 | } | ||
142 | return __user_copy_pt((unsigned long) to, (void *) from, n, 1); | 195 | return __user_copy_pt((unsigned long) to, (void *) from, n, 1); |
143 | } | 196 | } |
144 | 197 | ||
145 | static size_t clear_user_pt(size_t n, void __user *to) | 198 | static size_t clear_user_pt(size_t n, void __user *to) |
146 | { | 199 | { |
200 | void *zpage = &empty_zero_page; | ||
147 | long done, size, ret; | 201 | long done, size, ret; |
148 | 202 | ||
149 | if (segment_eq(get_fs(), KERNEL_DS)) { | ||
150 | memset((void __kernel __force *) to, 0, n); | ||
151 | return 0; | ||
152 | } | ||
153 | done = 0; | 203 | done = 0; |
154 | do { | 204 | do { |
155 | if (n - done > PAGE_SIZE) | 205 | if (n - done > PAGE_SIZE) |
156 | size = PAGE_SIZE; | 206 | size = PAGE_SIZE; |
157 | else | 207 | else |
158 | size = n - done; | 208 | size = n - done; |
159 | ret = __user_copy_pt((unsigned long) to + done, | 209 | if (segment_eq(get_fs(), KERNEL_DS)) |
160 | &empty_zero_page, size, 1); | 210 | ret = copy_in_kernel(n, to, (void __user *) zpage); |
211 | else | ||
212 | ret = __user_copy_pt((unsigned long) to, zpage, size, 1); | ||
161 | done += size; | 213 | done += size; |
214 | to += size; | ||
162 | if (ret) | 215 | if (ret) |
163 | return ret + n - done; | 216 | return ret + n - done; |
164 | } while (done < n); | 217 | } while (done < n); |
@@ -172,8 +225,10 @@ static size_t strnlen_user_pt(size_t count, const char __user *src) | |||
172 | unsigned long offset, done, len, kaddr; | 225 | unsigned long offset, done, len, kaddr; |
173 | size_t len_str; | 226 | size_t len_str; |
174 | 227 | ||
228 | if (unlikely(!count)) | ||
229 | return 0; | ||
175 | if (segment_eq(get_fs(), KERNEL_DS)) | 230 | if (segment_eq(get_fs(), KERNEL_DS)) |
176 | return strnlen((const char __kernel __force *) src, count) + 1; | 231 | return strnlen_kernel(count, src); |
177 | done = 0; | 232 | done = 0; |
178 | retry: | 233 | retry: |
179 | spin_lock(&mm->page_table_lock); | 234 | spin_lock(&mm->page_table_lock); |
@@ -200,25 +255,27 @@ fault: | |||
200 | static size_t strncpy_from_user_pt(size_t count, const char __user *src, | 255 | static size_t strncpy_from_user_pt(size_t count, const char __user *src, |
201 | char *dst) | 256 | char *dst) |
202 | { | 257 | { |
203 | size_t n = strnlen_user_pt(count, src); | 258 | size_t done, len, offset, len_str; |
204 | 259 | ||
205 | if (!n) | 260 | if (unlikely(!count)) |
206 | return -EFAULT; | 261 | return 0; |
207 | if (n > count) | 262 | done = 0; |
208 | n = count; | 263 | do { |
209 | if (segment_eq(get_fs(), KERNEL_DS)) { | 264 | offset = (size_t)src & ~PAGE_MASK; |
210 | memcpy(dst, (const char __kernel __force *) src, n); | 265 | len = min(count - done, PAGE_SIZE - offset); |
211 | if (dst[n-1] == '\0') | 266 | if (segment_eq(get_fs(), KERNEL_DS)) { |
212 | return n-1; | 267 | if (copy_in_kernel(len, (void __user *) dst, src)) |
213 | else | 268 | return -EFAULT; |
214 | return n; | 269 | } else { |
215 | } | 270 | if (__user_copy_pt((unsigned long) src, dst, len, 0)) |
216 | if (__user_copy_pt((unsigned long) src, dst, n, 0)) | 271 | return -EFAULT; |
217 | return -EFAULT; | 272 | } |
218 | if (dst[n-1] == '\0') | 273 | len_str = strnlen(dst, len); |
219 | return n-1; | 274 | done += len_str; |
220 | else | 275 | src += len_str; |
221 | return n; | 276 | dst += len_str; |
277 | } while ((len_str == len) && (done < count)); | ||
278 | return done; | ||
222 | } | 279 | } |
223 | 280 | ||
224 | static size_t copy_in_user_pt(size_t n, void __user *to, | 281 | static size_t copy_in_user_pt(size_t n, void __user *to, |
@@ -231,10 +288,8 @@ static size_t copy_in_user_pt(size_t n, void __user *to, | |||
231 | unsigned long kaddr_to, kaddr_from; | 288 | unsigned long kaddr_to, kaddr_from; |
232 | int write_user; | 289 | int write_user; |
233 | 290 | ||
234 | if (segment_eq(get_fs(), KERNEL_DS)) { | 291 | if (segment_eq(get_fs(), KERNEL_DS)) |
235 | memcpy((void __force *) to, (void __force *) from, n); | 292 | return copy_in_kernel(n, to, from); |
236 | return 0; | ||
237 | } | ||
238 | done = 0; | 293 | done = 0; |
239 | retry: | 294 | retry: |
240 | spin_lock(&mm->page_table_lock); | 295 | spin_lock(&mm->page_table_lock); |
diff --git a/arch/s390/lib/uaccess_std.c b/arch/s390/lib/uaccess_std.c index 6fbd06338270..4a75d475b06a 100644 --- a/arch/s390/lib/uaccess_std.c +++ b/arch/s390/lib/uaccess_std.c | |||
@@ -188,6 +188,8 @@ size_t strnlen_user_std(size_t size, const char __user *src) | |||
188 | register unsigned long reg0 asm("0") = 0UL; | 188 | register unsigned long reg0 asm("0") = 0UL; |
189 | unsigned long tmp1, tmp2; | 189 | unsigned long tmp1, tmp2; |
190 | 190 | ||
191 | if (unlikely(!size)) | ||
192 | return 0; | ||
191 | asm volatile( | 193 | asm volatile( |
192 | " la %2,0(%1)\n" | 194 | " la %2,0(%1)\n" |
193 | " la %3,0(%0,%1)\n" | 195 | " la %3,0(%0,%1)\n" |
@@ -204,38 +206,24 @@ size_t strnlen_user_std(size_t size, const char __user *src) | |||
204 | return size; | 206 | return size; |
205 | } | 207 | } |
206 | 208 | ||
207 | size_t strncpy_from_user_std(size_t size, const char __user *src, char *dst) | 209 | size_t strncpy_from_user_std(size_t count, const char __user *src, char *dst) |
208 | { | 210 | { |
209 | register unsigned long reg0 asm("0") = 0UL; | 211 | size_t done, len, offset, len_str; |
210 | unsigned long tmp1, tmp2; | ||
211 | 212 | ||
212 | asm volatile( | 213 | if (unlikely(!count)) |
213 | " la %3,0(%1)\n" | 214 | return 0; |
214 | " la %4,0(%0,%1)\n" | 215 | done = 0; |
215 | " sacf 256\n" | 216 | do { |
216 | "0: srst %4,%3\n" | 217 | offset = (size_t)src & ~PAGE_MASK; |
217 | " jo 0b\n" | 218 | len = min(count - done, PAGE_SIZE - offset); |
218 | " sacf 0\n" | 219 | if (copy_from_user_std(len, src, dst)) |
219 | " la %0,0(%4)\n" | 220 | return -EFAULT; |
220 | " jh 1f\n" /* found \0 in string ? */ | 221 | len_str = strnlen(dst, len); |
221 | " "AHI" %4,1\n" /* include \0 in copy */ | 222 | done += len_str; |
222 | "1:"SLR" %0,%1\n" /* %0 = return length (without \0) */ | 223 | src += len_str; |
223 | " "SLR" %4,%1\n" /* %4 = copy length (including \0) */ | 224 | dst += len_str; |
224 | "2: mvcp 0(%4,%2),0(%1),%5\n" | 225 | } while ((len_str == len) && (done < count)); |
225 | " jz 9f\n" | 226 | return done; |
226 | "3:"AHI" %4,-256\n" | ||
227 | " la %1,256(%1)\n" | ||
228 | " la %2,256(%2)\n" | ||
229 | "4: mvcp 0(%4,%2),0(%1),%5\n" | ||
230 | " jnz 3b\n" | ||
231 | " j 9f\n" | ||
232 | "7: sacf 0\n" | ||
233 | "8:"LHI" %0,%6\n" | ||
234 | "9:\n" | ||
235 | EX_TABLE(0b,7b) EX_TABLE(2b,8b) EX_TABLE(4b,8b) | ||
236 | : "+a" (size), "+a" (src), "+d" (dst), "=a" (tmp1), "=a" (tmp2) | ||
237 | : "d" (reg0), "K" (-EFAULT) : "cc", "memory"); | ||
238 | return size; | ||
239 | } | 227 | } |
240 | 228 | ||
241 | #define __futex_atomic_op(insn, ret, oldval, newval, uaddr, oparg) \ | 229 | #define __futex_atomic_op(insn, ret, oldval, newval, uaddr, oparg) \ |
diff --git a/arch/s390/mm/dump_pagetables.c b/arch/s390/mm/dump_pagetables.c index 04e4892247d2..3ad65b04ac15 100644 --- a/arch/s390/mm/dump_pagetables.c +++ b/arch/s390/mm/dump_pagetables.c | |||
@@ -49,10 +49,13 @@ static void print_prot(struct seq_file *m, unsigned int pr, int level) | |||
49 | { "ASCE", "PGD", "PUD", "PMD", "PTE" }; | 49 | { "ASCE", "PGD", "PUD", "PMD", "PTE" }; |
50 | 50 | ||
51 | seq_printf(m, "%s ", level_name[level]); | 51 | seq_printf(m, "%s ", level_name[level]); |
52 | if (pr & _PAGE_INVALID) | 52 | if (pr & _PAGE_INVALID) { |
53 | seq_printf(m, "I\n"); | 53 | seq_printf(m, "I\n"); |
54 | else | 54 | return; |
55 | seq_printf(m, "%s\n", pr & _PAGE_RO ? "RO" : "RW"); | 55 | } |
56 | seq_printf(m, "%s", pr & _PAGE_RO ? "RO " : "RW "); | ||
57 | seq_printf(m, "%s", pr & _PAGE_CO ? "CO " : " "); | ||
58 | seq_putc(m, '\n'); | ||
56 | } | 59 | } |
57 | 60 | ||
58 | static void note_page(struct seq_file *m, struct pg_state *st, | 61 | static void note_page(struct seq_file *m, struct pg_state *st, |
@@ -125,6 +128,12 @@ static void walk_pte_level(struct seq_file *m, struct pg_state *st, | |||
125 | } | 128 | } |
126 | } | 129 | } |
127 | 130 | ||
131 | #ifdef CONFIG_64BIT | ||
132 | #define _PMD_PROT_MASK (_SEGMENT_ENTRY_RO | _SEGMENT_ENTRY_CO) | ||
133 | #else | ||
134 | #define _PMD_PROT_MASK 0 | ||
135 | #endif | ||
136 | |||
128 | static void walk_pmd_level(struct seq_file *m, struct pg_state *st, | 137 | static void walk_pmd_level(struct seq_file *m, struct pg_state *st, |
129 | pud_t *pud, unsigned long addr) | 138 | pud_t *pud, unsigned long addr) |
130 | { | 139 | { |
@@ -137,7 +146,7 @@ static void walk_pmd_level(struct seq_file *m, struct pg_state *st, | |||
137 | pmd = pmd_offset(pud, addr); | 146 | pmd = pmd_offset(pud, addr); |
138 | if (!pmd_none(*pmd)) { | 147 | if (!pmd_none(*pmd)) { |
139 | if (pmd_large(*pmd)) { | 148 | if (pmd_large(*pmd)) { |
140 | prot = pmd_val(*pmd) & _SEGMENT_ENTRY_RO; | 149 | prot = pmd_val(*pmd) & _PMD_PROT_MASK; |
141 | note_page(m, st, prot, 3); | 150 | note_page(m, st, prot, 3); |
142 | } else | 151 | } else |
143 | walk_pte_level(m, st, pmd, addr); | 152 | walk_pte_level(m, st, pmd, addr); |
@@ -147,6 +156,12 @@ static void walk_pmd_level(struct seq_file *m, struct pg_state *st, | |||
147 | } | 156 | } |
148 | } | 157 | } |
149 | 158 | ||
159 | #ifdef CONFIG_64BIT | ||
160 | #define _PUD_PROT_MASK (_REGION3_ENTRY_RO | _REGION3_ENTRY_CO) | ||
161 | #else | ||
162 | #define _PUD_PROT_MASK 0 | ||
163 | #endif | ||
164 | |||
150 | static void walk_pud_level(struct seq_file *m, struct pg_state *st, | 165 | static void walk_pud_level(struct seq_file *m, struct pg_state *st, |
151 | pgd_t *pgd, unsigned long addr) | 166 | pgd_t *pgd, unsigned long addr) |
152 | { | 167 | { |
@@ -159,7 +174,7 @@ static void walk_pud_level(struct seq_file *m, struct pg_state *st, | |||
159 | pud = pud_offset(pgd, addr); | 174 | pud = pud_offset(pgd, addr); |
160 | if (!pud_none(*pud)) | 175 | if (!pud_none(*pud)) |
161 | if (pud_large(*pud)) { | 176 | if (pud_large(*pud)) { |
162 | prot = pud_val(*pud) & _PAGE_RO; | 177 | prot = pud_val(*pud) & _PUD_PROT_MASK; |
163 | note_page(m, st, prot, 2); | 178 | note_page(m, st, prot, 2); |
164 | } else | 179 | } else |
165 | walk_pmd_level(m, st, pud, addr); | 180 | walk_pmd_level(m, st, pud, addr); |
diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index e21aaf4f5cb6..ffab84db6907 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c | |||
@@ -236,7 +236,8 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node) | |||
236 | if (!new_page) | 236 | if (!new_page) |
237 | goto out; | 237 | goto out; |
238 | pmd_val(*pm_dir) = __pa(new_page) | | 238 | pmd_val(*pm_dir) = __pa(new_page) | |
239 | _SEGMENT_ENTRY | _SEGMENT_ENTRY_LARGE; | 239 | _SEGMENT_ENTRY | _SEGMENT_ENTRY_LARGE | |
240 | _SEGMENT_ENTRY_CO; | ||
240 | address = (address + PMD_SIZE) & PMD_MASK; | 241 | address = (address + PMD_SIZE) & PMD_MASK; |
241 | continue; | 242 | continue; |
242 | } | 243 | } |