| From: Matt Redfearn <matt.redfearn@mips.com> |
| Date: Tue, 17 Apr 2018 15:52:21 +0100 |
| Subject: MIPS: memset.S: Fix return of __clear_user from Lpartial_fixup |
| |
| commit daf70d89f80c6e1772233da9e020114b1254e7e0 upstream. |
| |
| The __clear_user function is defined to return the number of bytes that |
| could not be cleared. From the underlying memset / bzero implementation |
| this means setting register a2 to that number on return. Currently if a |
| page fault is triggered within the memset_partial block, the value |
| loaded into a2 on return is meaningless. |
| |
| The label .Lpartial_fixup\@ is jumped to on page fault. In order to work |
| out how many bytes failed to copy, the exception handler should find how |
| many bytes left in the partial block (andi a2, STORMASK), add that to |
| the partial block end address (a2), and subtract the faulting address to |
| get the remainder. Currently it incorrectly subtracts the partial block |
| start address (t1), which has additionally been clobbered to generate a |
| jump target in memset_partial. Fix this by adding the block end address |
| instead. |
| |
| This issue was found with the following test code: |
| int j, k; |
| for (j = 0; j < 512; j++) { |
| if ((k = clear_user(NULL, j)) != j) { |
| pr_err("clear_user (NULL %d) returned %d\n", j, k); |
| } |
| } |
| Which now passes on Creator Ci40 (MIPS32) and Cavium Octeon II (MIPS64). |
| |
| Suggested-by: James Hogan <jhogan@kernel.org> |
| Signed-off-by: Matt Redfearn <matt.redfearn@mips.com> |
| Cc: Ralf Baechle <ralf@linux-mips.org> |
| Cc: linux-mips@linux-mips.org |
| Patchwork: https://patchwork.linux-mips.org/patch/19108/ |
| Signed-off-by: James Hogan <jhogan@kernel.org> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| arch/mips/lib/memset.S | 2 +- |
| 1 file changed, 1 insertion(+), 1 deletion(-) |
| |
| --- a/arch/mips/lib/memset.S |
| +++ b/arch/mips/lib/memset.S |
| @@ -204,7 +204,7 @@ |
| PTR_L t0, TI_TASK($28) |
| andi a2, STORMASK |
| LONG_L t0, THREAD_BUADDR(t0) |
| - LONG_ADDU a2, t1 |
| + LONG_ADDU a2, a0 |
| jr ra |
| LONG_SUBU a2, t0 |
| |