Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/64: Copy as much as possible in __copy_tofrom_user

In __copy_tofrom_user, if we encounter an exception on a store, we
stop copying and return the number of bytes not copied. However,
if the store is wider than one byte and is to an unaligned address,
it is possible that the store operand overlaps a page boundary
and the exception occurred on the latter part of the store operand,
meaning that it would be possible to copy a few more bytes. Since
copy_to_user is generally expected to copy as much as possible,
it would be better to copy those extra few bytes. This adds code
to do that. Since this edge case is not performance-critical,
the code has been written to be compact rather than as fast as
possible.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

authored by

Paul Mackerras and committed by
Michael Ellerman
f8db2007 2679f63f

+23 -6
+23 -6
arch/powerpc/lib/copyuser_64.S
··· 379 379 blr 380 380 381 381 /* 382 - * exception handlers for stores: we just need to work 383 - * out how many bytes weren't copied 382 + * exception handlers for stores: we need to work out how many bytes 383 + * weren't copied, and we may need to copy some more. 384 384 * Note that the number of bytes of instructions for adjusting r3 needs 385 385 * to equal the amount of the adjustment, due to the trick of using 386 386 * .Lst_exc - r3_offset as the handler address. ··· 400 400 /* adjust by 4 */ 401 401 addi r3,r3,4 402 402 .Lst_exc: 403 - ld r6,-24(r1) 404 - ld r5,-8(r1) 405 - add r6,r6,r5 406 - subf r3,r3,r6 /* #bytes not copied in r3 */ 403 + ld r6,-24(r1) /* original destination pointer */ 404 + ld r4,-16(r1) /* original source pointer */ 405 + ld r5,-8(r1) /* original number of bytes */ 406 + add r7,r6,r5 407 + /* 408 + * If the destination pointer isn't 8-byte aligned, 409 + * we may have got the exception as a result of a 410 + * store that overlapped a page boundary, so we may be 411 + * able to copy a few more bytes. 412 + */ 413 + 17: andi. r0,r3,7 414 + beq 19f 415 + subf r8,r6,r3 /* #bytes copied */ 416 + 100: EX_TABLE(100b,19f) 417 + lbzx r0,r8,r4 418 + 100: EX_TABLE(100b,19f) 419 + stb r0,0(r3) 420 + addi r3,r3,1 421 + cmpld r3,r7 422 + blt 17b 423 + 19: subf r3,r3,r7 /* #bytes not copied in r3 */ 407 424 blr 408 425 409 426 /*