| From fb8ea062a8f2e85256e13f55696c5c5f0dfdcc8b Mon Sep 17 00:00:00 2001 |
| From: James Hogan <james.hogan@imgtec.com> |
| Date: Fri, 31 Mar 2017 13:35:01 +0100 |
| Subject: metag/usercopy: Add early abort to copy_to_user |
| |
| From: James Hogan <james.hogan@imgtec.com> |
| |
| commit fb8ea062a8f2e85256e13f55696c5c5f0dfdcc8b upstream. |
| |
| When copying to userland on Meta, if any faults are encountered |
| immediately abort the copy instead of continuing on and repeatedly |
| faulting, and worse potentially copying further bytes successfully to |
| subsequent valid pages. |
| |
| Fixes: 373cd784d0fc ("metag: Memory handling") |
| Reported-by: Al Viro <viro@zeniv.linux.org.uk> |
| Signed-off-by: James Hogan <james.hogan@imgtec.com> |
| Cc: linux-metag@vger.kernel.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/metag/lib/usercopy.c | 20 ++++++++++++++++++++ |
| 1 file changed, 20 insertions(+) |
| |
| --- a/arch/metag/lib/usercopy.c |
| +++ b/arch/metag/lib/usercopy.c |
| @@ -538,23 +538,31 @@ unsigned long __copy_user(void __user *p |
| if ((unsigned long) src & 1) { |
| __asm_copy_to_user_1(dst, src, retn); |
| n--; |
| + if (retn) |
| + return retn + n; |
| } |
| if ((unsigned long) dst & 1) { |
| /* Worst case - byte copy */ |
| while (n > 0) { |
| __asm_copy_to_user_1(dst, src, retn); |
| n--; |
| + if (retn) |
| + return retn + n; |
| } |
| } |
| if (((unsigned long) src & 2) && n >= 2) { |
| __asm_copy_to_user_2(dst, src, retn); |
| n -= 2; |
| + if (retn) |
| + return retn + n; |
| } |
| if ((unsigned long) dst & 2) { |
| /* Second worst case - word copy */ |
| while (n >= 2) { |
| __asm_copy_to_user_2(dst, src, retn); |
| n -= 2; |
| + if (retn) |
| + return retn + n; |
| } |
| } |
| |
| @@ -569,6 +577,8 @@ unsigned long __copy_user(void __user *p |
| while (n >= 8) { |
| __asm_copy_to_user_8x64(dst, src, retn); |
| n -= 8; |
| + if (retn) |
| + return retn + n; |
| } |
| } |
| if (n >= RAPF_MIN_BUF_SIZE) { |
| @@ -581,6 +591,8 @@ unsigned long __copy_user(void __user *p |
| while (n >= 8) { |
| __asm_copy_to_user_8x64(dst, src, retn); |
| n -= 8; |
| + if (retn) |
| + return retn + n; |
| } |
| } |
| #endif |
| @@ -588,11 +600,15 @@ unsigned long __copy_user(void __user *p |
| while (n >= 16) { |
| __asm_copy_to_user_16(dst, src, retn); |
| n -= 16; |
| + if (retn) |
| + return retn + n; |
| } |
| |
| while (n >= 4) { |
| __asm_copy_to_user_4(dst, src, retn); |
| n -= 4; |
| + if (retn) |
| + return retn + n; |
| } |
| |
| switch (n) { |
| @@ -609,6 +625,10 @@ unsigned long __copy_user(void __user *p |
| break; |
| } |
| |
| + /* |
| + * If we get here, retn correctly reflects the number of failing |
| + * bytes. |
| + */ |
| return retn; |
| } |
| EXPORT_SYMBOL(__copy_user); |