Commit efdc1e20 authored by David S. Miller's avatar David S. Miller

[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: default avatarDavid S. Miller <davem@davemloft.net>
parent 5fd29752
...@@ -11,61 +11,56 @@ ...@@ -11,61 +11,56 @@
/* Calculating the exact fault address when using /* Calculating the exact fault address when using
* block loads and stores can be very complicated. * block loads and stores can be very complicated.
*
* Instead of trying to be clever and handling all * Instead of trying to be clever and handling all
* of the cases, just fix things up simply here. * of the cases, just fix things up simply here.
*/ */
unsigned long copy_from_user_fixup(void *to, const void __user *from, unsigned long size) static unsigned long compute_size(unsigned long start, unsigned long size, unsigned long *offset)
{ {
char *dst = to; unsigned long fault_addr = current_thread_info()->fault_address;
const char __user *src = from; unsigned long end = start + size;
while (size) { if (fault_addr < start || fault_addr >= end) {
if (__get_user(*dst, src)) *offset = 0;
break; } else {
dst++; *offset = start - fault_addr;
src++; size = end - fault_addr;
size--;
} }
return size;
}
if (size) unsigned long copy_from_user_fixup(void *to, const void __user *from, unsigned long size)
memset(dst, 0, size); {
unsigned long offset;
size = compute_size((unsigned long) from, size, &offset);
if (likely(size))
memset(to + offset, 0, size);
return size; return size;
} }
unsigned long copy_to_user_fixup(void __user *to, const void *from, unsigned long size) unsigned long copy_to_user_fixup(void __user *to, const void *from, unsigned long size)
{ {
char __user *dst = to; unsigned long offset;
const char *src = from;
while (size) {
if (__put_user(*src, dst))
break;
dst++;
src++;
size--;
}
return size; return compute_size((unsigned long) to, size, &offset);
} }
unsigned long copy_in_user_fixup(void __user *to, void __user *from, unsigned long size) unsigned long copy_in_user_fixup(void __user *to, void __user *from, unsigned long size)
{ {
char __user *dst = to; unsigned long fault_addr = current_thread_info()->fault_address;
char __user *src = from; unsigned long start = (unsigned long) to;
unsigned long end = start + size;
while (size) { if (fault_addr >= start && fault_addr < end)
char tmp; return end - fault_addr;
if (__get_user(tmp, src)) start = (unsigned long) from;
break; end = start + size;
if (__put_user(tmp, dst)) if (fault_addr >= start && fault_addr < end)
break; return end - fault_addr;
dst++;
src++;
size--;
}
return size; return size;
} }
...@@ -457,7 +457,7 @@ good_area: ...@@ -457,7 +457,7 @@ good_area:
} }
up_read(&mm->mmap_sem); up_read(&mm->mmap_sem);
goto fault_done; return;
/* /*
* Something tried to access memory that isn't in our memory map.. * Something tried to access memory that isn't in our memory map..
...@@ -469,8 +469,7 @@ bad_area: ...@@ -469,8 +469,7 @@ bad_area:
handle_kernel_fault: handle_kernel_fault:
do_kernel_fault(regs, si_code, fault_code, insn, address); do_kernel_fault(regs, si_code, fault_code, insn, address);
return;
goto fault_done;
/* /*
* We ran out of memory, or some other thing happened to us that made * We ran out of memory, or some other thing happened to us that made
...@@ -501,9 +500,4 @@ do_sigbus: ...@@ -501,9 +500,4 @@ do_sigbus:
/* Kernel mode? Handle exceptions or die */ /* Kernel mode? Handle exceptions or die */
if (regs->tstate & TSTATE_PRIV) if (regs->tstate & TSTATE_PRIV)
goto handle_kernel_fault; goto handle_kernel_fault;
fault_done:
/* These values are no longer needed, clear them. */
set_thread_fault_code(0);
current_thread_info()->fault_address = 0;
} }
...@@ -251,7 +251,7 @@ copy_from_user(void *to, const void __user *from, unsigned long size) ...@@ -251,7 +251,7 @@ copy_from_user(void *to, const void __user *from, unsigned long size)
{ {
unsigned long ret = ___copy_from_user(to, from, size); unsigned long ret = ___copy_from_user(to, from, size);
if (ret) if (unlikely(ret))
ret = copy_from_user_fixup(to, from, size); ret = copy_from_user_fixup(to, from, size);
return ret; return ret;
} }
...@@ -267,7 +267,7 @@ copy_to_user(void __user *to, const void *from, unsigned long size) ...@@ -267,7 +267,7 @@ copy_to_user(void __user *to, const void *from, unsigned long size)
{ {
unsigned long ret = ___copy_to_user(to, from, size); unsigned long ret = ___copy_to_user(to, from, size);
if (ret) if (unlikely(ret))
ret = copy_to_user_fixup(to, from, size); ret = copy_to_user_fixup(to, from, size);
return ret; return ret;
} }
...@@ -283,7 +283,7 @@ copy_in_user(void __user *to, void __user *from, unsigned long size) ...@@ -283,7 +283,7 @@ copy_in_user(void __user *to, void __user *from, unsigned long size)
{ {
unsigned long ret = ___copy_in_user(to, from, size); unsigned long ret = ___copy_in_user(to, from, size);
if (ret) if (unlikely(ret))
ret = copy_in_user_fixup(to, from, size); ret = copy_in_user_fixup(to, from, size);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment