aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorMichael Neuling <mikey@neuling.org>2008-10-22 20:42:36 -0400
committerPaul Mackerras <paulus@samba.org>2008-10-31 01:12:00 -0400
commit16c29d180becc5bdf92fd0fc7314a44a671b5f4e (patch)
treecf8ed40be5a456dd6b61eda6d339cd4b8341f655 /arch
parentb160544cccb403310cf38ddb3ebc156ea454848a (diff)
powerpc: Fix swapcontext system for VSX + old ucontext size
Since VSX support was added, we now have two sizes of ucontext_t; the older, smaller size without the extra VSX state, and the new larger size with the extra VSX state. A program using the sys_swapcontext system call and supplying smaller ucontext_t structures will currently get an EINVAL error if the task has used VSX (e.g. because of calling library code that uses VSX) and the old_ctx argument is non-NULL (i.e. the program is asking for its current context to be saved). Thus the program will start getting EINVAL errors on calls that previously worked. This commit changes this behaviour so that we don't send an EINVAL in this case. It will now return the smaller context but the VSX MSR bit will always be cleared to indicate that the ucontext_t doesn't include the extra VSX state, even if the task has executed VSX instructions. Both 32 and 64 bit cases are updated. [paulus@samba.org - also fix some access_ok() and get_user() calls] Thanks to Ben Herrenschmidt for noticing this problem. Signed-off-by: Michael Neuling <mikey@neuling.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/kernel/signal_32.c36
-rw-r--r--arch/powerpc/kernel/signal_64.c33
2 files changed, 30 insertions, 39 deletions
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index 3e80aa32b8b0..a6a43103655e 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -410,7 +410,7 @@ inline unsigned long copy_fpr_from_user(struct task_struct *task,
410 * altivec/spe instructions at some point. 410 * altivec/spe instructions at some point.
411 */ 411 */
412static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, 412static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
413 int sigret) 413 int sigret, int ctx_has_vsx_region)
414{ 414{
415 unsigned long msr = regs->msr; 415 unsigned long msr = regs->msr;
416 416
@@ -451,7 +451,7 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
451 * the saved MSR value to indicate that frame->mc_vregs 451 * the saved MSR value to indicate that frame->mc_vregs
452 * contains valid data 452 * contains valid data
453 */ 453 */
454 if (current->thread.used_vsr) { 454 if (current->thread.used_vsr && ctx_has_vsx_region) {
455 __giveup_vsx(current); 455 __giveup_vsx(current);
456 if (copy_vsx_to_user(&frame->mc_vsregs, current)) 456 if (copy_vsx_to_user(&frame->mc_vsregs, current))
457 return 1; 457 return 1;
@@ -858,11 +858,11 @@ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka,
858 frame = &rt_sf->uc.uc_mcontext; 858 frame = &rt_sf->uc.uc_mcontext;
859 addr = frame; 859 addr = frame;
860 if (vdso32_rt_sigtramp && current->mm->context.vdso_base) { 860 if (vdso32_rt_sigtramp && current->mm->context.vdso_base) {
861 if (save_user_regs(regs, frame, 0)) 861 if (save_user_regs(regs, frame, 0, 1))
862 goto badframe; 862 goto badframe;
863 regs->link = current->mm->context.vdso_base + vdso32_rt_sigtramp; 863 regs->link = current->mm->context.vdso_base + vdso32_rt_sigtramp;
864 } else { 864 } else {
865 if (save_user_regs(regs, frame, __NR_rt_sigreturn)) 865 if (save_user_regs(regs, frame, __NR_rt_sigreturn, 1))
866 goto badframe; 866 goto badframe;
867 regs->link = (unsigned long) frame->tramp; 867 regs->link = (unsigned long) frame->tramp;
868 } 868 }
@@ -936,12 +936,13 @@ long sys_swapcontext(struct ucontext __user *old_ctx,
936 int ctx_size, int r6, int r7, int r8, struct pt_regs *regs) 936 int ctx_size, int r6, int r7, int r8, struct pt_regs *regs)
937{ 937{
938 unsigned char tmp; 938 unsigned char tmp;
939 int ctx_has_vsx_region = 0;
939 940
940#ifdef CONFIG_PPC64 941#ifdef CONFIG_PPC64
941 unsigned long new_msr = 0; 942 unsigned long new_msr = 0;
942 943
943 if (new_ctx && 944 if (new_ctx &&
944 __get_user(new_msr, &new_ctx->uc_mcontext.mc_gregs[PT_MSR])) 945 get_user(new_msr, &new_ctx->uc_mcontext.mc_gregs[PT_MSR]))
945 return -EFAULT; 946 return -EFAULT;
946 /* 947 /*
947 * Check that the context is not smaller than the original 948 * Check that the context is not smaller than the original
@@ -956,16 +957,9 @@ long sys_swapcontext(struct ucontext __user *old_ctx,
956 if ((ctx_size < sizeof(struct ucontext)) && 957 if ((ctx_size < sizeof(struct ucontext)) &&
957 (new_msr & MSR_VSX)) 958 (new_msr & MSR_VSX))
958 return -EINVAL; 959 return -EINVAL;
959#ifdef CONFIG_VSX 960 /* Does the context have enough room to store VSX data? */
960 /* 961 if (ctx_size >= sizeof(struct ucontext))
961 * If userspace doesn't provide enough room for VSX data, 962 ctx_has_vsx_region = 1;
962 * but current thread has used VSX, we don't have anywhere
963 * to store the full context back into.
964 */
965 if ((ctx_size < sizeof(struct ucontext)) &&
966 (current->thread.used_vsr && old_ctx))
967 return -EINVAL;
968#endif
969#else 963#else
970 /* Context size is for future use. Right now, we only make sure 964 /* Context size is for future use. Right now, we only make sure
971 * we are passed something we understand 965 * we are passed something we understand
@@ -985,17 +979,17 @@ long sys_swapcontext(struct ucontext __user *old_ctx,
985 */ 979 */
986 mctx = (struct mcontext __user *) 980 mctx = (struct mcontext __user *)
987 ((unsigned long) &old_ctx->uc_mcontext & ~0xfUL); 981 ((unsigned long) &old_ctx->uc_mcontext & ~0xfUL);
988 if (!access_ok(VERIFY_WRITE, old_ctx, sizeof(*old_ctx)) 982 if (!access_ok(VERIFY_WRITE, old_ctx, ctx_size)
989 || save_user_regs(regs, mctx, 0) 983 || save_user_regs(regs, mctx, 0, ctx_has_vsx_region)
990 || put_sigset_t(&old_ctx->uc_sigmask, &current->blocked) 984 || put_sigset_t(&old_ctx->uc_sigmask, &current->blocked)
991 || __put_user(to_user_ptr(mctx), &old_ctx->uc_regs)) 985 || __put_user(to_user_ptr(mctx), &old_ctx->uc_regs))
992 return -EFAULT; 986 return -EFAULT;
993 } 987 }
994 if (new_ctx == NULL) 988 if (new_ctx == NULL)
995 return 0; 989 return 0;
996 if (!access_ok(VERIFY_READ, new_ctx, sizeof(*new_ctx)) 990 if (!access_ok(VERIFY_READ, new_ctx, ctx_size)
997 || __get_user(tmp, (u8 __user *) new_ctx) 991 || __get_user(tmp, (u8 __user *) new_ctx)
998 || __get_user(tmp, (u8 __user *) (new_ctx + 1) - 1)) 992 || __get_user(tmp, (u8 __user *) new_ctx + ctx_size - 1))
999 return -EFAULT; 993 return -EFAULT;
1000 994
1001 /* 995 /*
@@ -1196,11 +1190,11 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka,
1196 goto badframe; 1190 goto badframe;
1197 1191
1198 if (vdso32_sigtramp && current->mm->context.vdso_base) { 1192 if (vdso32_sigtramp && current->mm->context.vdso_base) {
1199 if (save_user_regs(regs, &frame->mctx, 0)) 1193 if (save_user_regs(regs, &frame->mctx, 0, 1))
1200 goto badframe; 1194 goto badframe;
1201 regs->link = current->mm->context.vdso_base + vdso32_sigtramp; 1195 regs->link = current->mm->context.vdso_base + vdso32_sigtramp;
1202 } else { 1196 } else {
1203 if (save_user_regs(regs, &frame->mctx, __NR_sigreturn)) 1197 if (save_user_regs(regs, &frame->mctx, __NR_sigreturn, 1))
1204 goto badframe; 1198 goto badframe;
1205 regs->link = (unsigned long) frame->mctx.tramp; 1199 regs->link = (unsigned long) frame->mctx.tramp;
1206 } 1200 }
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index c6a8f2326b6f..e132891d3cea 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -74,7 +74,8 @@ static const char fmt64[] = KERN_INFO \
74 */ 74 */
75 75
76static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, 76static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
77 int signr, sigset_t *set, unsigned long handler) 77 int signr, sigset_t *set, unsigned long handler,
78 int ctx_has_vsx_region)
78{ 79{
79 /* When CONFIG_ALTIVEC is set, we _always_ setup v_regs even if the 80 /* When CONFIG_ALTIVEC is set, we _always_ setup v_regs even if the
80 * process never used altivec yet (MSR_VEC is zero in pt_regs of 81 * process never used altivec yet (MSR_VEC is zero in pt_regs of
@@ -121,7 +122,7 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
121 * then out to userspace. Update v_regs to point after the 122 * then out to userspace. Update v_regs to point after the
122 * VMX data. 123 * VMX data.
123 */ 124 */
124 if (current->thread.used_vsr) { 125 if (current->thread.used_vsr && ctx_has_vsx_region) {
125 __giveup_vsx(current); 126 __giveup_vsx(current);
126 v_regs += ELF_NVRREG; 127 v_regs += ELF_NVRREG;
127 err |= copy_vsx_to_user(v_regs, current); 128 err |= copy_vsx_to_user(v_regs, current);
@@ -282,9 +283,10 @@ int sys_swapcontext(struct ucontext __user *old_ctx,
282 unsigned char tmp; 283 unsigned char tmp;
283 sigset_t set; 284 sigset_t set;
284 unsigned long new_msr = 0; 285 unsigned long new_msr = 0;
286 int ctx_has_vsx_region = 0;
285 287
286 if (new_ctx && 288 if (new_ctx &&
287 __get_user(new_msr, &new_ctx->uc_mcontext.gp_regs[PT_MSR])) 289 get_user(new_msr, &new_ctx->uc_mcontext.gp_regs[PT_MSR]))
288 return -EFAULT; 290 return -EFAULT;
289 /* 291 /*
290 * Check that the context is not smaller than the original 292 * Check that the context is not smaller than the original
@@ -299,28 +301,23 @@ int sys_swapcontext(struct ucontext __user *old_ctx,
299 if ((ctx_size < sizeof(struct ucontext)) && 301 if ((ctx_size < sizeof(struct ucontext)) &&
300 (new_msr & MSR_VSX)) 302 (new_msr & MSR_VSX))
301 return -EINVAL; 303 return -EINVAL;
302#ifdef CONFIG_VSX 304 /* Does the context have enough room to store VSX data? */
303 /* 305 if (ctx_size >= sizeof(struct ucontext))
304 * If userspace doesn't provide enough room for VSX data, 306 ctx_has_vsx_region = 1;
305 * but current thread has used VSX, we don't have anywhere 307
306 * to store the full context back into.
307 */
308 if ((ctx_size < sizeof(struct ucontext)) &&
309 (current->thread.used_vsr && old_ctx))
310 return -EINVAL;
311#endif
312 if (old_ctx != NULL) { 308 if (old_ctx != NULL) {
313 if (!access_ok(VERIFY_WRITE, old_ctx, sizeof(*old_ctx)) 309 if (!access_ok(VERIFY_WRITE, old_ctx, ctx_size)
314 || setup_sigcontext(&old_ctx->uc_mcontext, regs, 0, NULL, 0) 310 || setup_sigcontext(&old_ctx->uc_mcontext, regs, 0, NULL, 0,
311 ctx_has_vsx_region)
315 || __copy_to_user(&old_ctx->uc_sigmask, 312 || __copy_to_user(&old_ctx->uc_sigmask,
316 &current->blocked, sizeof(sigset_t))) 313 &current->blocked, sizeof(sigset_t)))
317 return -EFAULT; 314 return -EFAULT;
318 } 315 }
319 if (new_ctx == NULL) 316 if (new_ctx == NULL)
320 return 0; 317 return 0;
321 if (!access_ok(VERIFY_READ, new_ctx, sizeof(*new_ctx)) 318 if (!access_ok(VERIFY_READ, new_ctx, ctx_size)
322 || __get_user(tmp, (u8 __user *) new_ctx) 319 || __get_user(tmp, (u8 __user *) new_ctx)
323 || __get_user(tmp, (u8 __user *) (new_ctx + 1) - 1)) 320 || __get_user(tmp, (u8 __user *) new_ctx + ctx_size - 1))
324 return -EFAULT; 321 return -EFAULT;
325 322
326 /* 323 /*
@@ -423,7 +420,7 @@ int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info,
423 &frame->uc.uc_stack.ss_flags); 420 &frame->uc.uc_stack.ss_flags);
424 err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); 421 err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
425 err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, signr, NULL, 422 err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, signr, NULL,
426 (unsigned long)ka->sa.sa_handler); 423 (unsigned long)ka->sa.sa_handler, 1);
427 err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); 424 err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
428 if (err) 425 if (err)
429 goto badframe; 426 goto badframe;