diff options
author | Paul Mundt <lethal@linux-sh.org> | 2006-09-27 04:07:07 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2006-09-27 04:07:07 -0400 |
commit | 2991be725260d6fec11691a6138b9d71de949956 (patch) | |
tree | 407241b126eed902bacba4e02043930c6ce087b4 | |
parent | 0f08f338083cc1d68788ccbccc44bd0502fc57ae (diff) |
sh: Fixup __strnlen_user() behaviour.
Drop TIF_USERSPACE and add addr_limit to the thread_info struct.
Subsequently, use that for address checking in strnlen_user() to
ward off bogus -EFAULTs.
Make __strnlen_user() return 0 on exception, rather than -EFAULT.
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
-rw-r--r-- | arch/sh/kernel/process.c | 3 | ||||
-rw-r--r-- | include/asm-sh/processor.h | 4 | ||||
-rw-r--r-- | include/asm-sh/thread_info.h | 4 | ||||
-rw-r--r-- | include/asm-sh/uaccess.h | 65 |
4 files changed, 22 insertions, 54 deletions
diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index f2031314cb2b..4a32550fd7c6 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c | |||
@@ -263,6 +263,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, | |||
263 | unsigned long unused, | 263 | unsigned long unused, |
264 | struct task_struct *p, struct pt_regs *regs) | 264 | struct task_struct *p, struct pt_regs *regs) |
265 | { | 265 | { |
266 | struct thread_info *ti = task_thread_info(p); | ||
266 | struct pt_regs *childregs; | 267 | struct pt_regs *childregs; |
267 | #if defined(CONFIG_SH_FPU) | 268 | #if defined(CONFIG_SH_FPU) |
268 | struct task_struct *tsk = current; | 269 | struct task_struct *tsk = current; |
@@ -277,8 +278,10 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, | |||
277 | 278 | ||
278 | if (user_mode(regs)) { | 279 | if (user_mode(regs)) { |
279 | childregs->regs[15] = usp; | 280 | childregs->regs[15] = usp; |
281 | ti->addr_limit = USER_DS; | ||
280 | } else { | 282 | } else { |
281 | childregs->regs[15] = (unsigned long)task_stack_page(p) + THREAD_SIZE; | 283 | childregs->regs[15] = (unsigned long)task_stack_page(p) + THREAD_SIZE; |
284 | ti->addr_limit = KERNEL_DS; | ||
282 | } | 285 | } |
283 | if (clone_flags & CLONE_SETTLS) { | 286 | if (clone_flags & CLONE_SETTLS) { |
284 | childregs->gbr = childregs->regs[0]; | 287 | childregs->gbr = childregs->regs[0]; |
diff --git a/include/asm-sh/processor.h b/include/asm-sh/processor.h index e99aff706cf7..a22732007dd4 100644 --- a/include/asm-sh/processor.h +++ b/include/asm-sh/processor.h | |||
@@ -149,6 +149,10 @@ struct thread_struct { | |||
149 | union sh_fpu_union fpu; | 149 | union sh_fpu_union fpu; |
150 | }; | 150 | }; |
151 | 151 | ||
152 | typedef struct { | ||
153 | unsigned long seg; | ||
154 | } mm_segment_t; | ||
155 | |||
152 | /* Count of active tasks with UBC settings */ | 156 | /* Count of active tasks with UBC settings */ |
153 | extern int ubc_usercnt; | 157 | extern int ubc_usercnt; |
154 | 158 | ||
diff --git a/include/asm-sh/thread_info.h b/include/asm-sh/thread_info.h index f64dd803a014..b986a1914472 100644 --- a/include/asm-sh/thread_info.h +++ b/include/asm-sh/thread_info.h | |||
@@ -21,6 +21,7 @@ struct thread_info { | |||
21 | unsigned long flags; /* low level flags */ | 21 | unsigned long flags; /* low level flags */ |
22 | __u32 cpu; | 22 | __u32 cpu; |
23 | int preempt_count; /* 0 => preemptable, <0 => BUG */ | 23 | int preempt_count; /* 0 => preemptable, <0 => BUG */ |
24 | mm_segment_t addr_limit; /* thread address space */ | ||
24 | struct restart_block restart_block; | 25 | struct restart_block restart_block; |
25 | __u8 supervisor_stack[0]; | 26 | __u8 supervisor_stack[0]; |
26 | }; | 27 | }; |
@@ -40,6 +41,7 @@ struct thread_info { | |||
40 | .flags = 0, \ | 41 | .flags = 0, \ |
41 | .cpu = 0, \ | 42 | .cpu = 0, \ |
42 | .preempt_count = 1, \ | 43 | .preempt_count = 1, \ |
44 | .addr_limit = KERNEL_DS, \ | ||
43 | .restart_block = { \ | 45 | .restart_block = { \ |
44 | .fn = do_no_restart_syscall, \ | 46 | .fn = do_no_restart_syscall, \ |
45 | }, \ | 47 | }, \ |
@@ -95,7 +97,6 @@ static inline struct thread_info *current_thread_info(void) | |||
95 | #define TIF_USEDFPU 16 /* FPU was used by this task this quantum (SMP) */ | 97 | #define TIF_USEDFPU 16 /* FPU was used by this task this quantum (SMP) */ |
96 | #define TIF_POLLING_NRFLAG 17 /* true if poll_idle() is polling TIF_NEED_RESCHED */ | 98 | #define TIF_POLLING_NRFLAG 17 /* true if poll_idle() is polling TIF_NEED_RESCHED */ |
97 | #define TIF_MEMDIE 18 | 99 | #define TIF_MEMDIE 18 |
98 | #define TIF_USERSPACE 31 /* true if FS sets userspace */ | ||
99 | 100 | ||
100 | #define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE) | 101 | #define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE) |
101 | #define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME) | 102 | #define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME) |
@@ -103,7 +104,6 @@ static inline struct thread_info *current_thread_info(void) | |||
103 | #define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED) | 104 | #define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED) |
104 | #define _TIF_USEDFPU (1<<TIF_USEDFPU) | 105 | #define _TIF_USEDFPU (1<<TIF_USEDFPU) |
105 | #define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG) | 106 | #define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG) |
106 | #define _TIF_USERSPACE (1<<TIF_USERSPACE) | ||
107 | 107 | ||
108 | #define _TIF_WORK_MASK 0x000000FE /* work to do on interrupt/exception return */ | 108 | #define _TIF_WORK_MASK 0x000000FE /* work to do on interrupt/exception return */ |
109 | #define _TIF_ALLWORK_MASK 0x000000FF /* work to do on any return to u-space */ | 109 | #define _TIF_ALLWORK_MASK 0x000000FF /* work to do on any return to u-space */ |
diff --git a/include/asm-sh/uaccess.h b/include/asm-sh/uaccess.h index 2cb01861e7c5..6c0014dd2ef7 100644 --- a/include/asm-sh/uaccess.h +++ b/include/asm-sh/uaccess.h | |||
@@ -16,21 +16,9 @@ | |||
16 | #include <linux/errno.h> | 16 | #include <linux/errno.h> |
17 | #include <linux/sched.h> | 17 | #include <linux/sched.h> |
18 | 18 | ||
19 | /* | ||
20 | * NOTE: Macro/functions in this file depends on threads_info.h implementation. | ||
21 | * Assumes: | ||
22 | * TI_FLAGS == 8 | ||
23 | * TIF_USERSPACE == 31 | ||
24 | * USER_ADDR_LIMIT == 0x80000000 | ||
25 | */ | ||
26 | |||
27 | #define VERIFY_READ 0 | 19 | #define VERIFY_READ 0 |
28 | #define VERIFY_WRITE 1 | 20 | #define VERIFY_WRITE 1 |
29 | 21 | ||
30 | typedef struct { | ||
31 | unsigned int is_user_space; | ||
32 | } mm_segment_t; | ||
33 | |||
34 | /* | 22 | /* |
35 | * The fs value determines whether argument validity checking should be | 23 | * The fs value determines whether argument validity checking should be |
36 | * performed or not. If get_fs() == USER_DS, checking is performed, with | 24 | * performed or not. If get_fs() == USER_DS, checking is performed, with |
@@ -40,12 +28,14 @@ typedef struct { | |||
40 | */ | 28 | */ |
41 | 29 | ||
42 | #define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) | 30 | #define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) |
43 | #define segment_eq(a,b) ((a).is_user_space == (b).is_user_space) | ||
44 | 31 | ||
45 | #define USER_ADDR_LIMIT 0x80000000 | 32 | #define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFFUL) |
33 | #define USER_DS MAKE_MM_SEG(PAGE_OFFSET) | ||
34 | |||
35 | #define segment_eq(a,b) ((a).seg == (b).seg) | ||
46 | 36 | ||
47 | #define KERNEL_DS MAKE_MM_SEG(0) | 37 | #define __addr_ok(addr) \ |
48 | #define USER_DS MAKE_MM_SEG(1) | 38 | ((unsigned long)(addr) < (current_thread_info()->addr_limit.seg)) |
49 | 39 | ||
50 | #define get_ds() (KERNEL_DS) | 40 | #define get_ds() (KERNEL_DS) |
51 | 41 | ||
@@ -76,31 +66,8 @@ static inline int __access_ok(unsigned long addr, unsigned long size) | |||
76 | return ((addr >= memory_start) && ((addr + size) < memory_end)); | 66 | return ((addr >= memory_start) && ((addr + size) < memory_end)); |
77 | } | 67 | } |
78 | #else /* CONFIG_MMU */ | 68 | #else /* CONFIG_MMU */ |
79 | static inline mm_segment_t get_fs(void) | 69 | #define get_fs() (current_thread_info()->addr_limit) |
80 | { | 70 | #define set_fs(x) (current_thread_info()->addr_limit = (x)) |
81 | return MAKE_MM_SEG(test_thread_flag(TIF_USERSPACE)); | ||
82 | } | ||
83 | |||
84 | static inline void set_fs(mm_segment_t s) | ||
85 | { | ||
86 | unsigned long ti, flag; | ||
87 | __asm__ __volatile__( | ||
88 | "stc r7_bank, %0\n\t" | ||
89 | "mov.l @(8,%0), %1\n\t" | ||
90 | "shal %1\n\t" | ||
91 | "cmp/pl %2\n\t" | ||
92 | "rotcr %1\n\t" | ||
93 | "mov.l %1, @(8,%0)" | ||
94 | : "=&r" (ti), "=&r" (flag) | ||
95 | : "r" (s.is_user_space) | ||
96 | : "t"); | ||
97 | /**** | ||
98 | if (s.is_user_space) | ||
99 | set_thread_flag(TIF_USERSPACE); | ||
100 | else | ||
101 | clear_thread_flag(TIF_USERSPACE); | ||
102 | ****/ | ||
103 | } | ||
104 | 71 | ||
105 | /* | 72 | /* |
106 | * __access_ok: Check if address with size is OK or not. | 73 | * __access_ok: Check if address with size is OK or not. |
@@ -108,7 +75,7 @@ static inline void set_fs(mm_segment_t s) | |||
108 | * We do three checks: | 75 | * We do three checks: |
109 | * (1) is it user space? | 76 | * (1) is it user space? |
110 | * (2) addr + size --> carry? | 77 | * (2) addr + size --> carry? |
111 | * (3) addr + size >= 0x80000000 (USER_ADDR_LIMIT) | 78 | * (3) addr + size >= 0x80000000 (PAGE_OFFSET) |
112 | * | 79 | * |
113 | * (1) (2) (3) | RESULT | 80 | * (1) (2) (3) | RESULT |
114 | * 0 0 0 | ok | 81 | * 0 0 0 | ok |
@@ -541,7 +508,7 @@ static __inline__ long __strnlen_user(const char __user *__s, long __n) | |||
541 | "3:\n\t" | 508 | "3:\n\t" |
542 | "mov.l 4f, %1\n\t" | 509 | "mov.l 4f, %1\n\t" |
543 | "jmp @%1\n\t" | 510 | "jmp @%1\n\t" |
544 | " mov %5, %0\n" | 511 | " mov #0, %0\n" |
545 | ".balign 4\n" | 512 | ".balign 4\n" |
546 | "4: .long 2b\n" | 513 | "4: .long 2b\n" |
547 | ".previous\n" | 514 | ".previous\n" |
@@ -550,26 +517,20 @@ static __inline__ long __strnlen_user(const char __user *__s, long __n) | |||
550 | " .long 1b,3b\n" | 517 | " .long 1b,3b\n" |
551 | ".previous" | 518 | ".previous" |
552 | : "=z" (res), "=&r" (__dummy) | 519 | : "=z" (res), "=&r" (__dummy) |
553 | : "0" (0), "r" (__s), "r" (__n), "i" (-EFAULT) | 520 | : "0" (0), "r" (__s), "r" (__n) |
554 | : "t"); | 521 | : "t"); |
555 | return res; | 522 | return res; |
556 | } | 523 | } |
557 | 524 | ||
558 | static __inline__ long strnlen_user(const char __user *s, long n) | 525 | static __inline__ long strnlen_user(const char __user *s, long n) |
559 | { | 526 | { |
560 | if (!access_ok(VERIFY_READ, s, n)) | 527 | if (!__addr_ok(s)) |
561 | return 0; | 528 | return 0; |
562 | else | 529 | else |
563 | return __strnlen_user(s, n); | 530 | return __strnlen_user(s, n); |
564 | } | 531 | } |
565 | 532 | ||
566 | static __inline__ long strlen_user(const char __user *s) | 533 | #define strlen_user(str) strnlen_user(str, ~0UL >> 1) |
567 | { | ||
568 | if (!access_ok(VERIFY_READ, s, 0)) | ||
569 | return 0; | ||
570 | else | ||
571 | return __strnlen_user(s, ~0UL >> 1); | ||
572 | } | ||
573 | 534 | ||
574 | /* | 535 | /* |
575 | * The exception table consists of pairs of addresses: the first is the | 536 | * The exception table consists of pairs of addresses: the first is the |