diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2005-09-29 00:06:47 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2005-09-29 00:06:47 -0400 |
commit | efdc1e2083e04cc70721d55803889b346c1a3de2 (patch) | |
tree | 9f24fab33f795a69bb2dc43a8f3613392762ff02 | |
parent | 5fd29752f09cabff582f65c0ce35518db4c64937 (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.c | 63 | ||||
-rw-r--r-- | arch/sparc64/mm/fault.c | 10 | ||||
-rw-r--r-- | include/asm-sparc64/uaccess.h | 6 |
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 | ||
18 | unsigned long copy_from_user_fixup(void *to, const void __user *from, unsigned long size) | 19 | static 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) | 33 | unsigned 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 | ||
37 | unsigned long copy_to_user_fixup(void __user *to, const void *from, unsigned long size) | 44 | unsigned 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 | ||
53 | unsigned long copy_in_user_fixup(void __user *to, void __user *from, unsigned long size) | 51 | unsigned 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 | ||
470 | handle_kernel_fault: | 470 | handle_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 | |||
505 | fault_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 | } |