diff options
author | David S. Miller <davem@davemloft.net> | 2016-05-29 00:21:31 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-05-29 14:24:05 -0400 |
commit | d11c2a0de2824395656cf8ed15811580c9dd38aa (patch) | |
tree | 1a2024f157752d35d4ac42807c6ca1da7b52b58c | |
parent | 9ea46abe22550e3366ff7cee2f8391b35b12f730 (diff) |
sparc: Harden signal return frame checks.
All signal frames must be at least 16-byte aligned, because that is
the alignment we explicitly create when we build signal return stack
frames.
All stack pointers must be at least 8-byte aligned.
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | arch/sparc/kernel/signal32.c | 46 | ||||
-rw-r--r-- | arch/sparc/kernel/signal_32.c | 41 | ||||
-rw-r--r-- | arch/sparc/kernel/signal_64.c | 31 | ||||
-rw-r--r-- | arch/sparc/kernel/sigutil_32.c | 9 | ||||
-rw-r--r-- | arch/sparc/kernel/sigutil_64.c | 10 |
5 files changed, 92 insertions, 45 deletions
diff --git a/arch/sparc/kernel/signal32.c b/arch/sparc/kernel/signal32.c index 3c25241fa5cb..91cc2f4ae4d9 100644 --- a/arch/sparc/kernel/signal32.c +++ b/arch/sparc/kernel/signal32.c | |||
@@ -138,12 +138,24 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from) | |||
138 | return 0; | 138 | return 0; |
139 | } | 139 | } |
140 | 140 | ||
141 | /* Checks if the fp is valid. We always build signal frames which are | ||
142 | * 16-byte aligned, therefore we can always enforce that the restore | ||
143 | * frame has that property as well. | ||
144 | */ | ||
145 | static bool invalid_frame_pointer(void __user *fp, int fplen) | ||
146 | { | ||
147 | if ((((unsigned long) fp) & 15) || | ||
148 | ((unsigned long)fp) > 0x100000000ULL - fplen) | ||
149 | return true; | ||
150 | return false; | ||
151 | } | ||
152 | |||
141 | void do_sigreturn32(struct pt_regs *regs) | 153 | void do_sigreturn32(struct pt_regs *regs) |
142 | { | 154 | { |
143 | struct signal_frame32 __user *sf; | 155 | struct signal_frame32 __user *sf; |
144 | compat_uptr_t fpu_save; | 156 | compat_uptr_t fpu_save; |
145 | compat_uptr_t rwin_save; | 157 | compat_uptr_t rwin_save; |
146 | unsigned int psr; | 158 | unsigned int psr, ufp; |
147 | unsigned int pc, npc; | 159 | unsigned int pc, npc; |
148 | sigset_t set; | 160 | sigset_t set; |
149 | compat_sigset_t seta; | 161 | compat_sigset_t seta; |
@@ -158,11 +170,16 @@ void do_sigreturn32(struct pt_regs *regs) | |||
158 | sf = (struct signal_frame32 __user *) regs->u_regs[UREG_FP]; | 170 | sf = (struct signal_frame32 __user *) regs->u_regs[UREG_FP]; |
159 | 171 | ||
160 | /* 1. Make sure we are not getting garbage from the user */ | 172 | /* 1. Make sure we are not getting garbage from the user */ |
161 | if (!access_ok(VERIFY_READ, sf, sizeof(*sf)) || | 173 | if (invalid_frame_pointer(sf, sizeof(*sf))) |
162 | (((unsigned long) sf) & 3)) | 174 | goto segv; |
175 | |||
176 | if (get_user(ufp, &sf->info.si_regs.u_regs[UREG_FP])) | ||
177 | goto segv; | ||
178 | |||
179 | if (ufp & 0x7) | ||
163 | goto segv; | 180 | goto segv; |
164 | 181 | ||
165 | if (get_user(pc, &sf->info.si_regs.pc) || | 182 | if (__get_user(pc, &sf->info.si_regs.pc) || |
166 | __get_user(npc, &sf->info.si_regs.npc)) | 183 | __get_user(npc, &sf->info.si_regs.npc)) |
167 | goto segv; | 184 | goto segv; |
168 | 185 | ||
@@ -227,7 +244,7 @@ segv: | |||
227 | asmlinkage void do_rt_sigreturn32(struct pt_regs *regs) | 244 | asmlinkage void do_rt_sigreturn32(struct pt_regs *regs) |
228 | { | 245 | { |
229 | struct rt_signal_frame32 __user *sf; | 246 | struct rt_signal_frame32 __user *sf; |
230 | unsigned int psr, pc, npc; | 247 | unsigned int psr, pc, npc, ufp; |
231 | compat_uptr_t fpu_save; | 248 | compat_uptr_t fpu_save; |
232 | compat_uptr_t rwin_save; | 249 | compat_uptr_t rwin_save; |
233 | sigset_t set; | 250 | sigset_t set; |
@@ -242,11 +259,16 @@ asmlinkage void do_rt_sigreturn32(struct pt_regs *regs) | |||
242 | sf = (struct rt_signal_frame32 __user *) regs->u_regs[UREG_FP]; | 259 | sf = (struct rt_signal_frame32 __user *) regs->u_regs[UREG_FP]; |
243 | 260 | ||
244 | /* 1. Make sure we are not getting garbage from the user */ | 261 | /* 1. Make sure we are not getting garbage from the user */ |
245 | if (!access_ok(VERIFY_READ, sf, sizeof(*sf)) || | 262 | if (invalid_frame_pointer(sf, sizeof(*sf))) |
246 | (((unsigned long) sf) & 3)) | ||
247 | goto segv; | 263 | goto segv; |
248 | 264 | ||
249 | if (get_user(pc, &sf->regs.pc) || | 265 | if (get_user(ufp, &sf->regs.u_regs[UREG_FP])) |
266 | goto segv; | ||
267 | |||
268 | if (ufp & 0x7) | ||
269 | goto segv; | ||
270 | |||
271 | if (__get_user(pc, &sf->regs.pc) || | ||
250 | __get_user(npc, &sf->regs.npc)) | 272 | __get_user(npc, &sf->regs.npc)) |
251 | goto segv; | 273 | goto segv; |
252 | 274 | ||
@@ -307,14 +329,6 @@ segv: | |||
307 | force_sig(SIGSEGV, current); | 329 | force_sig(SIGSEGV, current); |
308 | } | 330 | } |
309 | 331 | ||
310 | /* Checks if the fp is valid */ | ||
311 | static int invalid_frame_pointer(void __user *fp, int fplen) | ||
312 | { | ||
313 | if ((((unsigned long) fp) & 7) || ((unsigned long)fp) > 0x100000000ULL - fplen) | ||
314 | return 1; | ||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | static void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, unsigned long framesize) | 332 | static void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, unsigned long framesize) |
319 | { | 333 | { |
320 | unsigned long sp; | 334 | unsigned long sp; |
diff --git a/arch/sparc/kernel/signal_32.c b/arch/sparc/kernel/signal_32.c index 52aa5e4ce5e7..c3c12efe0bc0 100644 --- a/arch/sparc/kernel/signal_32.c +++ b/arch/sparc/kernel/signal_32.c | |||
@@ -60,10 +60,22 @@ struct rt_signal_frame { | |||
60 | #define SF_ALIGNEDSZ (((sizeof(struct signal_frame) + 7) & (~7))) | 60 | #define SF_ALIGNEDSZ (((sizeof(struct signal_frame) + 7) & (~7))) |
61 | #define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame) + 7) & (~7))) | 61 | #define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame) + 7) & (~7))) |
62 | 62 | ||
63 | /* Checks if the fp is valid. We always build signal frames which are | ||
64 | * 16-byte aligned, therefore we can always enforce that the restore | ||
65 | * frame has that property as well. | ||
66 | */ | ||
67 | static inline bool invalid_frame_pointer(void __user *fp, int fplen) | ||
68 | { | ||
69 | if ((((unsigned long) fp) & 15) || !__access_ok((unsigned long)fp, fplen)) | ||
70 | return true; | ||
71 | |||
72 | return false; | ||
73 | } | ||
74 | |||
63 | asmlinkage void do_sigreturn(struct pt_regs *regs) | 75 | asmlinkage void do_sigreturn(struct pt_regs *regs) |
64 | { | 76 | { |
77 | unsigned long up_psr, pc, npc, ufp; | ||
65 | struct signal_frame __user *sf; | 78 | struct signal_frame __user *sf; |
66 | unsigned long up_psr, pc, npc; | ||
67 | sigset_t set; | 79 | sigset_t set; |
68 | __siginfo_fpu_t __user *fpu_save; | 80 | __siginfo_fpu_t __user *fpu_save; |
69 | __siginfo_rwin_t __user *rwin_save; | 81 | __siginfo_rwin_t __user *rwin_save; |
@@ -77,10 +89,13 @@ asmlinkage void do_sigreturn(struct pt_regs *regs) | |||
77 | sf = (struct signal_frame __user *) regs->u_regs[UREG_FP]; | 89 | sf = (struct signal_frame __user *) regs->u_regs[UREG_FP]; |
78 | 90 | ||
79 | /* 1. Make sure we are not getting garbage from the user */ | 91 | /* 1. Make sure we are not getting garbage from the user */ |
80 | if (!access_ok(VERIFY_READ, sf, sizeof(*sf))) | 92 | if (!invalid_frame_pointer(sf, sizeof(*sf))) |
93 | goto segv_and_exit; | ||
94 | |||
95 | if (get_user(ufp, &sf->info.si_regs.u_regs[UREG_FP])) | ||
81 | goto segv_and_exit; | 96 | goto segv_and_exit; |
82 | 97 | ||
83 | if (((unsigned long) sf) & 3) | 98 | if (ufp & 0x7) |
84 | goto segv_and_exit; | 99 | goto segv_and_exit; |
85 | 100 | ||
86 | err = __get_user(pc, &sf->info.si_regs.pc); | 101 | err = __get_user(pc, &sf->info.si_regs.pc); |
@@ -127,7 +142,7 @@ segv_and_exit: | |||
127 | asmlinkage void do_rt_sigreturn(struct pt_regs *regs) | 142 | asmlinkage void do_rt_sigreturn(struct pt_regs *regs) |
128 | { | 143 | { |
129 | struct rt_signal_frame __user *sf; | 144 | struct rt_signal_frame __user *sf; |
130 | unsigned int psr, pc, npc; | 145 | unsigned int psr, pc, npc, ufp; |
131 | __siginfo_fpu_t __user *fpu_save; | 146 | __siginfo_fpu_t __user *fpu_save; |
132 | __siginfo_rwin_t __user *rwin_save; | 147 | __siginfo_rwin_t __user *rwin_save; |
133 | sigset_t set; | 148 | sigset_t set; |
@@ -135,8 +150,13 @@ asmlinkage void do_rt_sigreturn(struct pt_regs *regs) | |||
135 | 150 | ||
136 | synchronize_user_stack(); | 151 | synchronize_user_stack(); |
137 | sf = (struct rt_signal_frame __user *) regs->u_regs[UREG_FP]; | 152 | sf = (struct rt_signal_frame __user *) regs->u_regs[UREG_FP]; |
138 | if (!access_ok(VERIFY_READ, sf, sizeof(*sf)) || | 153 | if (!invalid_frame_pointer(sf, sizeof(*sf))) |
139 | (((unsigned long) sf) & 0x03)) | 154 | goto segv; |
155 | |||
156 | if (get_user(ufp, &sf->regs.u_regs[UREG_FP])) | ||
157 | goto segv; | ||
158 | |||
159 | if (ufp & 0x7) | ||
140 | goto segv; | 160 | goto segv; |
141 | 161 | ||
142 | err = __get_user(pc, &sf->regs.pc); | 162 | err = __get_user(pc, &sf->regs.pc); |
@@ -178,15 +198,6 @@ segv: | |||
178 | force_sig(SIGSEGV, current); | 198 | force_sig(SIGSEGV, current); |
179 | } | 199 | } |
180 | 200 | ||
181 | /* Checks if the fp is valid */ | ||
182 | static inline int invalid_frame_pointer(void __user *fp, int fplen) | ||
183 | { | ||
184 | if ((((unsigned long) fp) & 7) || !__access_ok((unsigned long)fp, fplen)) | ||
185 | return 1; | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static inline void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, unsigned long framesize) | 201 | static inline void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, unsigned long framesize) |
191 | { | 202 | { |
192 | unsigned long sp = regs->u_regs[UREG_FP]; | 203 | unsigned long sp = regs->u_regs[UREG_FP]; |
diff --git a/arch/sparc/kernel/signal_64.c b/arch/sparc/kernel/signal_64.c index 39aaec173f66..5ee930c48f4c 100644 --- a/arch/sparc/kernel/signal_64.c +++ b/arch/sparc/kernel/signal_64.c | |||
@@ -234,6 +234,17 @@ do_sigsegv: | |||
234 | goto out; | 234 | goto out; |
235 | } | 235 | } |
236 | 236 | ||
237 | /* Checks if the fp is valid. We always build rt signal frames which | ||
238 | * are 16-byte aligned, therefore we can always enforce that the | ||
239 | * restore frame has that property as well. | ||
240 | */ | ||
241 | static bool invalid_frame_pointer(void __user *fp) | ||
242 | { | ||
243 | if (((unsigned long) fp) & 15) | ||
244 | return true; | ||
245 | return false; | ||
246 | } | ||
247 | |||
237 | struct rt_signal_frame { | 248 | struct rt_signal_frame { |
238 | struct sparc_stackf ss; | 249 | struct sparc_stackf ss; |
239 | siginfo_t info; | 250 | siginfo_t info; |
@@ -246,8 +257,8 @@ struct rt_signal_frame { | |||
246 | 257 | ||
247 | void do_rt_sigreturn(struct pt_regs *regs) | 258 | void do_rt_sigreturn(struct pt_regs *regs) |
248 | { | 259 | { |
260 | unsigned long tpc, tnpc, tstate, ufp; | ||
249 | struct rt_signal_frame __user *sf; | 261 | struct rt_signal_frame __user *sf; |
250 | unsigned long tpc, tnpc, tstate; | ||
251 | __siginfo_fpu_t __user *fpu_save; | 262 | __siginfo_fpu_t __user *fpu_save; |
252 | __siginfo_rwin_t __user *rwin_save; | 263 | __siginfo_rwin_t __user *rwin_save; |
253 | sigset_t set; | 264 | sigset_t set; |
@@ -261,10 +272,16 @@ void do_rt_sigreturn(struct pt_regs *regs) | |||
261 | (regs->u_regs [UREG_FP] + STACK_BIAS); | 272 | (regs->u_regs [UREG_FP] + STACK_BIAS); |
262 | 273 | ||
263 | /* 1. Make sure we are not getting garbage from the user */ | 274 | /* 1. Make sure we are not getting garbage from the user */ |
264 | if (((unsigned long) sf) & 3) | 275 | if (invalid_frame_pointer(sf)) |
276 | goto segv; | ||
277 | |||
278 | if (get_user(ufp, &sf->regs.u_regs[UREG_FP])) | ||
265 | goto segv; | 279 | goto segv; |
266 | 280 | ||
267 | err = get_user(tpc, &sf->regs.tpc); | 281 | if ((ufp + STACK_BIAS) & 0x7) |
282 | goto segv; | ||
283 | |||
284 | err = __get_user(tpc, &sf->regs.tpc); | ||
268 | err |= __get_user(tnpc, &sf->regs.tnpc); | 285 | err |= __get_user(tnpc, &sf->regs.tnpc); |
269 | if (test_thread_flag(TIF_32BIT)) { | 286 | if (test_thread_flag(TIF_32BIT)) { |
270 | tpc &= 0xffffffff; | 287 | tpc &= 0xffffffff; |
@@ -308,14 +325,6 @@ segv: | |||
308 | force_sig(SIGSEGV, current); | 325 | force_sig(SIGSEGV, current); |
309 | } | 326 | } |
310 | 327 | ||
311 | /* Checks if the fp is valid */ | ||
312 | static int invalid_frame_pointer(void __user *fp) | ||
313 | { | ||
314 | if (((unsigned long) fp) & 15) | ||
315 | return 1; | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | static inline void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, unsigned long framesize) | 328 | static inline void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, unsigned long framesize) |
320 | { | 329 | { |
321 | unsigned long sp = regs->u_regs[UREG_FP] + STACK_BIAS; | 330 | unsigned long sp = regs->u_regs[UREG_FP] + STACK_BIAS; |
diff --git a/arch/sparc/kernel/sigutil_32.c b/arch/sparc/kernel/sigutil_32.c index 0f6eebe71e6c..e5fe8cef9a69 100644 --- a/arch/sparc/kernel/sigutil_32.c +++ b/arch/sparc/kernel/sigutil_32.c | |||
@@ -48,6 +48,10 @@ int save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) | |||
48 | int restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) | 48 | int restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) |
49 | { | 49 | { |
50 | int err; | 50 | int err; |
51 | |||
52 | if (((unsigned long) fpu) & 3) | ||
53 | return -EFAULT; | ||
54 | |||
51 | #ifdef CONFIG_SMP | 55 | #ifdef CONFIG_SMP |
52 | if (test_tsk_thread_flag(current, TIF_USEDFPU)) | 56 | if (test_tsk_thread_flag(current, TIF_USEDFPU)) |
53 | regs->psr &= ~PSR_EF; | 57 | regs->psr &= ~PSR_EF; |
@@ -97,7 +101,10 @@ int restore_rwin_state(__siginfo_rwin_t __user *rp) | |||
97 | struct thread_info *t = current_thread_info(); | 101 | struct thread_info *t = current_thread_info(); |
98 | int i, wsaved, err; | 102 | int i, wsaved, err; |
99 | 103 | ||
100 | __get_user(wsaved, &rp->wsaved); | 104 | if (((unsigned long) rp) & 3) |
105 | return -EFAULT; | ||
106 | |||
107 | get_user(wsaved, &rp->wsaved); | ||
101 | if (wsaved > NSWINS) | 108 | if (wsaved > NSWINS) |
102 | return -EFAULT; | 109 | return -EFAULT; |
103 | 110 | ||
diff --git a/arch/sparc/kernel/sigutil_64.c b/arch/sparc/kernel/sigutil_64.c index 387834a9c56a..36aadcbeac69 100644 --- a/arch/sparc/kernel/sigutil_64.c +++ b/arch/sparc/kernel/sigutil_64.c | |||
@@ -37,7 +37,10 @@ int restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) | |||
37 | unsigned long fprs; | 37 | unsigned long fprs; |
38 | int err; | 38 | int err; |
39 | 39 | ||
40 | err = __get_user(fprs, &fpu->si_fprs); | 40 | if (((unsigned long) fpu) & 7) |
41 | return -EFAULT; | ||
42 | |||
43 | err = get_user(fprs, &fpu->si_fprs); | ||
41 | fprs_write(0); | 44 | fprs_write(0); |
42 | regs->tstate &= ~TSTATE_PEF; | 45 | regs->tstate &= ~TSTATE_PEF; |
43 | if (fprs & FPRS_DL) | 46 | if (fprs & FPRS_DL) |
@@ -72,7 +75,10 @@ int restore_rwin_state(__siginfo_rwin_t __user *rp) | |||
72 | struct thread_info *t = current_thread_info(); | 75 | struct thread_info *t = current_thread_info(); |
73 | int i, wsaved, err; | 76 | int i, wsaved, err; |
74 | 77 | ||
75 | __get_user(wsaved, &rp->wsaved); | 78 | if (((unsigned long) rp) & 7) |
79 | return -EFAULT; | ||
80 | |||
81 | get_user(wsaved, &rp->wsaved); | ||
76 | if (wsaved > NSWINS) | 82 | if (wsaved > NSWINS) |
77 | return -EFAULT; | 83 | return -EFAULT; |
78 | 84 | ||