aboutsummaryrefslogtreecommitdiffstats
path: root/arch/parisc
diff options
context:
space:
mode:
authorHelge Deller <deller@gmx.de>2013-07-04 16:34:11 -0400
committerHelge Deller <deller@gmx.de>2013-07-09 16:09:23 -0400
commit5b879d78bc0818aa710f5d4d9abbfc2aca075cc3 (patch)
tree4920e728eef0174babd0c26f9989513fb25912eb /arch/parisc
parente8d8fc219f9a0e63e7fb927881e6f4db8e7d34df (diff)
parisc: Fix gcc miscompilation in pa_memcpy()
When running the LTP testsuite one may hit this kernel BUG() with the write06 testcase: kernel BUG at mm/filemap.c:2023! CPU: 1 PID: 8614 Comm: writev01 Not tainted 3.10.0-rc7-64bit-c3000+ #6 IASQ: 0000000000000000 0000000000000000 IAOQ: 00000000401e6e84 00000000401e6e88 IIR: 03ffe01f ISR: 0000000010340000 IOR: 000001fbe0380820 CPU: 1 CR30: 00000000bef80000 CR31: ffffffffffffffff ORIG_R28: 00000000bdc192c0 IAOQ[0]: iov_iter_advance+0x3c/0xc0 IAOQ[1]: iov_iter_advance+0x40/0xc0 RP(r2): generic_file_buffered_write+0x204/0x3f0 Backtrace: [<00000000401e764c>] generic_file_buffered_write+0x204/0x3f0 [<00000000401eab24>] __generic_file_aio_write+0x244/0x448 [<00000000401eadc0>] generic_file_aio_write+0x98/0x150 [<000000004024f460>] do_sync_readv_writev+0xc0/0x130 [<000000004025037c>] compat_do_readv_writev+0x12c/0x340 [<00000000402505f8>] compat_writev+0x68/0xa0 [<0000000040251d88>] compat_SyS_writev+0x98/0xf8 Reason for this crash is a gcc miscompilation in the fault handlers of pa_memcpy() which return the fault address instead of the copied bytes. Since this seems to be a generic problem with gcc-4.7.x (and below), it's better to simplify the fault handlers in pa_memcpy to avoid this problem. Here is a simple reproducer for the problem: int main(int argc, char **argv) { int fd, nbytes; struct iovec wr_iovec[] = { { "TEST STRING ",32}, { (char*)0x40005000,32} }; // random memory. fd = open(DATA_FILE, O_RDWR | O_CREAT, 0666); nbytes = writev(fd, wr_iovec, 2); printf("return value = %d, errno %d (%s)\n", nbytes, errno, strerror(errno)); return 0; } In addition, John David Anglin wrote: There is no gcc PR as pa_memcpy is not legitimate C code. There is an implicit assumption that certain variables will contain correct values when an exception occurs and the code randomly jumps to one of the exception blocks. There is no guarantee of this. If a PR was filed, it would likely be marked as invalid. Signed-off-by: Helge Deller <deller@gmx.de> Signed-off-by: John David Anglin <dave.anglin@bell.net> Cc: <stable@vger.kernel.org> # 3.8+ Signed-off-by: Helge Deller <deller@gmx.de>
Diffstat (limited to 'arch/parisc')
-rw-r--r--arch/parisc/lib/memcpy.c79
1 files changed, 50 insertions, 29 deletions
diff --git a/arch/parisc/lib/memcpy.c b/arch/parisc/lib/memcpy.c
index a49cc812df8a..ac4370b1ca40 100644
--- a/arch/parisc/lib/memcpy.c
+++ b/arch/parisc/lib/memcpy.c
@@ -2,6 +2,7 @@
2 * Optimized memory copy routines. 2 * Optimized memory copy routines.
3 * 3 *
4 * Copyright (C) 2004 Randolph Chung <tausq@debian.org> 4 * Copyright (C) 2004 Randolph Chung <tausq@debian.org>
5 * Copyright (C) 2013 Helge Deller <deller@gmx.de>
5 * 6 *
6 * This program is free software; you can redistribute it and/or modify 7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by 8 * it under the terms of the GNU General Public License as published by
@@ -153,17 +154,21 @@ static inline void prefetch_dst(const void *addr)
153#define prefetch_dst(addr) do { } while(0) 154#define prefetch_dst(addr) do { } while(0)
154#endif 155#endif
155 156
157#define PA_MEMCPY_OK 0
158#define PA_MEMCPY_LOAD_ERROR 1
159#define PA_MEMCPY_STORE_ERROR 2
160
156/* Copy from a not-aligned src to an aligned dst, using shifts. Handles 4 words 161/* Copy from a not-aligned src to an aligned dst, using shifts. Handles 4 words
157 * per loop. This code is derived from glibc. 162 * per loop. This code is derived from glibc.
158 */ 163 */
159static inline unsigned long copy_dstaligned(unsigned long dst, unsigned long src, unsigned long len, unsigned long o_dst, unsigned long o_src, unsigned long o_len) 164static inline unsigned long copy_dstaligned(unsigned long dst,
165 unsigned long src, unsigned long len)
160{ 166{
161 /* gcc complains that a2 and a3 may be uninitialized, but actually 167 /* gcc complains that a2 and a3 may be uninitialized, but actually
162 * they cannot be. Initialize a2/a3 to shut gcc up. 168 * they cannot be. Initialize a2/a3 to shut gcc up.
163 */ 169 */
164 register unsigned int a0, a1, a2 = 0, a3 = 0; 170 register unsigned int a0, a1, a2 = 0, a3 = 0;
165 int sh_1, sh_2; 171 int sh_1, sh_2;
166 struct exception_data *d;
167 172
168 /* prefetch_src((const void *)src); */ 173 /* prefetch_src((const void *)src); */
169 174
@@ -197,7 +202,7 @@ static inline unsigned long copy_dstaligned(unsigned long dst, unsigned long src
197 goto do2; 202 goto do2;
198 case 0: 203 case 0:
199 if (len == 0) 204 if (len == 0)
200 return 0; 205 return PA_MEMCPY_OK;
201 /* a3 = ((unsigned int *) src)[0]; 206 /* a3 = ((unsigned int *) src)[0];
202 a0 = ((unsigned int *) src)[1]; */ 207 a0 = ((unsigned int *) src)[1]; */
203 ldw(s_space, 0, src, a3, cda_ldw_exc); 208 ldw(s_space, 0, src, a3, cda_ldw_exc);
@@ -256,42 +261,35 @@ do0:
256 preserve_branch(handle_load_error); 261 preserve_branch(handle_load_error);
257 preserve_branch(handle_store_error); 262 preserve_branch(handle_store_error);
258 263
259 return 0; 264 return PA_MEMCPY_OK;
260 265
261handle_load_error: 266handle_load_error:
262 __asm__ __volatile__ ("cda_ldw_exc:\n"); 267 __asm__ __volatile__ ("cda_ldw_exc:\n");
263 d = &__get_cpu_var(exception_data); 268 return PA_MEMCPY_LOAD_ERROR;
264 DPRINTF("cda_ldw_exc: o_len=%lu fault_addr=%lu o_src=%lu ret=%lu\n",
265 o_len, d->fault_addr, o_src, o_len - d->fault_addr + o_src);
266 return o_len * 4 - d->fault_addr + o_src;
267 269
268handle_store_error: 270handle_store_error:
269 __asm__ __volatile__ ("cda_stw_exc:\n"); 271 __asm__ __volatile__ ("cda_stw_exc:\n");
270 d = &__get_cpu_var(exception_data); 272 return PA_MEMCPY_STORE_ERROR;
271 DPRINTF("cda_stw_exc: o_len=%lu fault_addr=%lu o_dst=%lu ret=%lu\n",
272 o_len, d->fault_addr, o_dst, o_len - d->fault_addr + o_dst);
273 return o_len * 4 - d->fault_addr + o_dst;
274} 273}
275 274
276 275
277/* Returns 0 for success, otherwise, returns number of bytes not transferred. */ 276/* Returns PA_MEMCPY_OK, PA_MEMCPY_LOAD_ERROR or PA_MEMCPY_STORE_ERROR.
278static unsigned long pa_memcpy(void *dstp, const void *srcp, unsigned long len) 277 * In case of an access fault the faulty address can be read from the per_cpu
278 * exception data struct. */
279static unsigned long pa_memcpy_internal(void *dstp, const void *srcp,
280 unsigned long len)
279{ 281{
280 register unsigned long src, dst, t1, t2, t3; 282 register unsigned long src, dst, t1, t2, t3;
281 register unsigned char *pcs, *pcd; 283 register unsigned char *pcs, *pcd;
282 register unsigned int *pws, *pwd; 284 register unsigned int *pws, *pwd;
283 register double *pds, *pdd; 285 register double *pds, *pdd;
284 unsigned long ret = 0; 286 unsigned long ret;
285 unsigned long o_dst, o_src, o_len;
286 struct exception_data *d;
287 287
288 src = (unsigned long)srcp; 288 src = (unsigned long)srcp;
289 dst = (unsigned long)dstp; 289 dst = (unsigned long)dstp;
290 pcs = (unsigned char *)srcp; 290 pcs = (unsigned char *)srcp;
291 pcd = (unsigned char *)dstp; 291 pcd = (unsigned char *)dstp;
292 292
293 o_dst = dst; o_src = src; o_len = len;
294
295 /* prefetch_src((const void *)srcp); */ 293 /* prefetch_src((const void *)srcp); */
296 294
297 if (len < THRESHOLD) 295 if (len < THRESHOLD)
@@ -401,7 +399,7 @@ byte_copy:
401 len--; 399 len--;
402 } 400 }
403 401
404 return 0; 402 return PA_MEMCPY_OK;
405 403
406unaligned_copy: 404unaligned_copy:
407 /* possibly we are aligned on a word, but not on a double... */ 405 /* possibly we are aligned on a word, but not on a double... */
@@ -438,8 +436,7 @@ unaligned_copy:
438 src = (unsigned long)pcs; 436 src = (unsigned long)pcs;
439 } 437 }
440 438
441 ret = copy_dstaligned(dst, src, len / sizeof(unsigned int), 439 ret = copy_dstaligned(dst, src, len / sizeof(unsigned int));
442 o_dst, o_src, o_len);
443 if (ret) 440 if (ret)
444 return ret; 441 return ret;
445 442
@@ -454,17 +451,41 @@ unaligned_copy:
454 451
455handle_load_error: 452handle_load_error:
456 __asm__ __volatile__ ("pmc_load_exc:\n"); 453 __asm__ __volatile__ ("pmc_load_exc:\n");
457 d = &__get_cpu_var(exception_data); 454 return PA_MEMCPY_LOAD_ERROR;
458 DPRINTF("pmc_load_exc: o_len=%lu fault_addr=%lu o_src=%lu ret=%lu\n",
459 o_len, d->fault_addr, o_src, o_len - d->fault_addr + o_src);
460 return o_len - d->fault_addr + o_src;
461 455
462handle_store_error: 456handle_store_error:
463 __asm__ __volatile__ ("pmc_store_exc:\n"); 457 __asm__ __volatile__ ("pmc_store_exc:\n");
458 return PA_MEMCPY_STORE_ERROR;
459}
460
461
462/* Returns 0 for success, otherwise, returns number of bytes not transferred. */
463static unsigned long pa_memcpy(void *dstp, const void *srcp, unsigned long len)
464{
465 unsigned long ret, fault_addr, reference;
466 struct exception_data *d;
467
468 ret = pa_memcpy_internal(dstp, srcp, len);
469 if (likely(ret == PA_MEMCPY_OK))
470 return 0;
471
472 /* if a load or store fault occured we can get the faulty addr */
464 d = &__get_cpu_var(exception_data); 473 d = &__get_cpu_var(exception_data);
465 DPRINTF("pmc_store_exc: o_len=%lu fault_addr=%lu o_dst=%lu ret=%lu\n", 474 fault_addr = d->fault_addr;
466 o_len, d->fault_addr, o_dst, o_len - d->fault_addr + o_dst); 475
467 return o_len - d->fault_addr + o_dst; 476 /* error in load or store? */
477 if (ret == PA_MEMCPY_LOAD_ERROR)
478 reference = (unsigned long) srcp;
479 else
480 reference = (unsigned long) dstp;
481
482 DPRINTF("pa_memcpy: fault type = %lu, len=%lu fault_addr=%lu ref=%lu\n",
483 ret, len, fault_addr, reference);
484
485 if (fault_addr >= reference)
486 return len - (fault_addr - reference);
487 else
488 return len;
468} 489}
469 490
470#ifdef __KERNEL__ 491#ifdef __KERNEL__