[SPARC64]: Simplify user fault fixup handling.

Instead of doing byte-at-a-time user accesses to figure
out where the fault occurred, read the saved fault_address
from the current thread structure.

For the sake of defensive programming, if the fault_address
does not fall into the user buffer range, simply assume the
whole area faulted. This will cause the fixup for
copy_from_user() to clear the entire kernel side buffer.

Signed-off-by: David S. Miller <davem@davemloft.net>

+35 -46
+30 -35
arch/sparc64/lib/user_fixup.c
··· 11 12 /* Calculating the exact fault address when using 13 * block loads and stores can be very complicated. 14 * Instead of trying to be clever and handling all 15 * of the cases, just fix things up simply here. 16 */ 17 18 unsigned long copy_from_user_fixup(void *to, const void __user *from, unsigned long size) 19 { 20 - char *dst = to; 21 - const char __user *src = from; 22 23 - while (size) { 24 - if (__get_user(*dst, src)) 25 - break; 26 - dst++; 27 - src++; 28 - size--; 29 - } 30 - 31 - if (size) 32 - memset(dst, 0, size); 33 34 return size; 35 } 36 37 unsigned long copy_to_user_fixup(void __user *to, const void *from, unsigned long size) 38 { 39 - char __user *dst = to; 40 - const char *src = from; 41 42 - while (size) { 43 - if (__put_user(*src, dst)) 44 - break; 45 - dst++; 46 - src++; 47 - size--; 48 - } 49 - 50 - return size; 51 } 52 53 unsigned long copy_in_user_fixup(void __user *to, void __user *from, unsigned long size) 54 { 55 - char __user *dst = to; 56 - char __user *src = from; 57 58 - while (size) { 59 - char tmp; 60 61 - if (__get_user(tmp, src)) 62 - break; 63 - if (__put_user(tmp, dst)) 64 - break; 65 - dst++; 66 - src++; 67 - size--; 68 - } 69 70 return size; 71 }
··· 11 12 /* Calculating the exact fault address when using 13 * block loads and stores can be very complicated. 14 + * 15 * Instead of trying to be clever and handling all 16 * of the cases, just fix things up simply here. 17 */ 18 19 + static unsigned long compute_size(unsigned long start, unsigned long size, unsigned long *offset) 20 + { 21 + unsigned long fault_addr = current_thread_info()->fault_address; 22 + unsigned long end = start + size; 23 + 24 + if (fault_addr < start || fault_addr >= end) { 25 + *offset = 0; 26 + } else { 27 + *offset = start - fault_addr; 28 + size = end - fault_addr; 29 + } 30 + return size; 31 + } 32 + 33 unsigned long copy_from_user_fixup(void *to, const void __user *from, unsigned long size) 34 { 35 + unsigned long offset; 36 37 + size = compute_size((unsigned long) from, size, &offset); 38 + if (likely(size)) 39 + memset(to + offset, 0, size); 40 41 return size; 42 } 43 44 unsigned long copy_to_user_fixup(void __user *to, const void *from, unsigned long size) 45 { 46 + unsigned long offset; 47 48 + return compute_size((unsigned long) to, size, &offset); 49 } 50 51 unsigned long copy_in_user_fixup(void __user *to, void __user *from, unsigned long size) 52 { 53 + unsigned long fault_addr = current_thread_info()->fault_address; 54 + unsigned long start = (unsigned long) to; 55 + unsigned long end = start + size; 56 57 + if (fault_addr >= start && fault_addr < end) 58 + return end - fault_addr; 59 60 + start = (unsigned long) from; 61 + end = start + size; 62 + if (fault_addr >= start && fault_addr < end) 63 + return end - fault_addr; 64 65 return size; 66 }
+2 -8
arch/sparc64/mm/fault.c
··· 457 } 458 459 up_read(&mm->mmap_sem); 460 - goto fault_done; 461 462 /* 463 * Something tried to access memory that isn't in our memory map.. ··· 469 470 handle_kernel_fault: 471 do_kernel_fault(regs, si_code, fault_code, insn, address); 472 - 473 - goto fault_done; 474 475 /* 476 * We ran out of memory, or some other thing happened to us that made ··· 500 /* Kernel mode? Handle exceptions or die */ 501 if (regs->tstate & TSTATE_PRIV) 502 goto handle_kernel_fault; 503 - 504 - fault_done: 505 - /* These values are no longer needed, clear them. */ 506 - set_thread_fault_code(0); 507 - current_thread_info()->fault_address = 0; 508 }
··· 457 } 458 459 up_read(&mm->mmap_sem); 460 + return; 461 462 /* 463 * Something tried to access memory that isn't in our memory map.. ··· 469 470 handle_kernel_fault: 471 do_kernel_fault(regs, si_code, fault_code, insn, address); 472 + return; 473 474 /* 475 * We ran out of memory, or some other thing happened to us that made ··· 501 /* Kernel mode? Handle exceptions or die */ 502 if (regs->tstate & TSTATE_PRIV) 503 goto handle_kernel_fault; 504 }
+3 -3
include/asm-sparc64/uaccess.h
··· 251 { 252 unsigned long ret = ___copy_from_user(to, from, size); 253 254 - if (ret) 255 ret = copy_from_user_fixup(to, from, size); 256 return ret; 257 } ··· 267 { 268 unsigned long ret = ___copy_to_user(to, from, size); 269 270 - if (ret) 271 ret = copy_to_user_fixup(to, from, size); 272 return ret; 273 } ··· 283 { 284 unsigned long ret = ___copy_in_user(to, from, size); 285 286 - if (ret) 287 ret = copy_in_user_fixup(to, from, size); 288 return ret; 289 }
··· 251 { 252 unsigned long ret = ___copy_from_user(to, from, size); 253 254 + if (unlikely(ret)) 255 ret = copy_from_user_fixup(to, from, size); 256 return ret; 257 } ··· 267 { 268 unsigned long ret = ___copy_to_user(to, from, size); 269 270 + if (unlikely(ret)) 271 ret = copy_to_user_fixup(to, from, size); 272 return ret; 273 } ··· 283 { 284 unsigned long ret = ___copy_in_user(to, from, size); 285 286 + if (unlikely(ret)) 287 ret = copy_in_user_fixup(to, from, size); 288 return ret; 289 }