Skip to content

Commit 5b879d7

Browse files
committed
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 <[email protected]> Signed-off-by: John David Anglin <[email protected]> Cc: <[email protected]> # 3.8+ Signed-off-by: Helge Deller <[email protected]>
1 parent e8d8fc2 commit 5b879d7

File tree

1 file changed

+50
-29
lines changed

1 file changed

+50
-29
lines changed

arch/parisc/lib/memcpy.c

Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* Optimized memory copy routines.
33
*
44
* Copyright (C) 2004 Randolph Chung <[email protected]>
5+
* Copyright (C) 2013 Helge Deller <[email protected]>
56
*
67
* This program is free software; you can redistribute it and/or modify
78
* 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)
153154
#define prefetch_dst(addr) do { } while(0)
154155
#endif
155156

157+
#define PA_MEMCPY_OK 0
158+
#define PA_MEMCPY_LOAD_ERROR 1
159+
#define PA_MEMCPY_STORE_ERROR 2
160+
156161
/* Copy from a not-aligned src to an aligned dst, using shifts. Handles 4 words
157162
* per loop. This code is derived from glibc.
158163
*/
159-
static 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)
164+
static inline unsigned long copy_dstaligned(unsigned long dst,
165+
unsigned long src, unsigned long len)
160166
{
161167
/* gcc complains that a2 and a3 may be uninitialized, but actually
162168
* they cannot be. Initialize a2/a3 to shut gcc up.
163169
*/
164170
register unsigned int a0, a1, a2 = 0, a3 = 0;
165171
int sh_1, sh_2;
166-
struct exception_data *d;
167172

168173
/* prefetch_src((const void *)src); */
169174

@@ -197,7 +202,7 @@ static inline unsigned long copy_dstaligned(unsigned long dst, unsigned long src
197202
goto do2;
198203
case 0:
199204
if (len == 0)
200-
return 0;
205+
return PA_MEMCPY_OK;
201206
/* a3 = ((unsigned int *) src)[0];
202207
a0 = ((unsigned int *) src)[1]; */
203208
ldw(s_space, 0, src, a3, cda_ldw_exc);
@@ -256,42 +261,35 @@ static inline unsigned long copy_dstaligned(unsigned long dst, unsigned long src
256261
preserve_branch(handle_load_error);
257262
preserve_branch(handle_store_error);
258263

259-
return 0;
264+
return PA_MEMCPY_OK;
260265

261266
handle_load_error:
262267
__asm__ __volatile__ ("cda_ldw_exc:\n");
263-
d = &__get_cpu_var(exception_data);
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;
268+
return PA_MEMCPY_LOAD_ERROR;
267269

268270
handle_store_error:
269271
__asm__ __volatile__ ("cda_stw_exc:\n");
270-
d = &__get_cpu_var(exception_data);
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;
272+
return PA_MEMCPY_STORE_ERROR;
274273
}
275274

276275

277-
/* Returns 0 for success, otherwise, returns number of bytes not transferred. */
278-
static unsigned long pa_memcpy(void *dstp, const void *srcp, unsigned long len)
276+
/* Returns PA_MEMCPY_OK, PA_MEMCPY_LOAD_ERROR or PA_MEMCPY_STORE_ERROR.
277+
* In case of an access fault the faulty address can be read from the per_cpu
278+
* exception data struct. */
279+
static unsigned long pa_memcpy_internal(void *dstp, const void *srcp,
280+
unsigned long len)
279281
{
280282
register unsigned long src, dst, t1, t2, t3;
281283
register unsigned char *pcs, *pcd;
282284
register unsigned int *pws, *pwd;
283285
register double *pds, *pdd;
284-
unsigned long ret = 0;
285-
unsigned long o_dst, o_src, o_len;
286-
struct exception_data *d;
286+
unsigned long ret;
287287

288288
src = (unsigned long)srcp;
289289
dst = (unsigned long)dstp;
290290
pcs = (unsigned char *)srcp;
291291
pcd = (unsigned char *)dstp;
292292

293-
o_dst = dst; o_src = src; o_len = len;
294-
295293
/* prefetch_src((const void *)srcp); */
296294

297295
if (len < THRESHOLD)
@@ -401,7 +399,7 @@ static unsigned long pa_memcpy(void *dstp, const void *srcp, unsigned long len)
401399
len--;
402400
}
403401

404-
return 0;
402+
return PA_MEMCPY_OK;
405403

406404
unaligned_copy:
407405
/* possibly we are aligned on a word, but not on a double... */
@@ -438,8 +436,7 @@ static unsigned long pa_memcpy(void *dstp, const void *srcp, unsigned long len)
438436
src = (unsigned long)pcs;
439437
}
440438

441-
ret = copy_dstaligned(dst, src, len / sizeof(unsigned int),
442-
o_dst, o_src, o_len);
439+
ret = copy_dstaligned(dst, src, len / sizeof(unsigned int));
443440
if (ret)
444441
return ret;
445442

@@ -454,17 +451,41 @@ static unsigned long pa_memcpy(void *dstp, const void *srcp, unsigned long len)
454451

455452
handle_load_error:
456453
__asm__ __volatile__ ("pmc_load_exc:\n");
457-
d = &__get_cpu_var(exception_data);
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;
454+
return PA_MEMCPY_LOAD_ERROR;
461455

462456
handle_store_error:
463457
__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. */
463+
static 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 */
464473
d = &__get_cpu_var(exception_data);
465-
DPRINTF("pmc_store_exc: o_len=%lu fault_addr=%lu o_dst=%lu ret=%lu\n",
466-
o_len, d->fault_addr, o_dst, o_len - d->fault_addr + o_dst);
467-
return o_len - d->fault_addr + o_dst;
474+
fault_addr = d->fault_addr;
475+
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;
468489
}
469490

470491
#ifdef __KERNEL__

0 commit comments

Comments
 (0)