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