aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorMichael Neuling <mikey@neuling.org>2008-07-08 04:43:41 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2008-07-09 02:30:47 -0400
commitc1cb299ead405f0ac065c4430729549b187e5b32 (patch)
treef61b78d06c8bc49e5b671bd065464613758dcefb /arch
parent1b17adf19b4d66858f366acd82b4e81cba5edc93 (diff)
powerpc: fix swapcontext backwards compat. with VSX ucontext changes
When the ucontext changed to add the VSX context, this broke backwards compatibly on swapcontext. swapcontext only compares the ucontext size passed in from the user to the new kernel ucontext size. This adds a check against the old ucontext size (with VMX but without VSX). It also adds some sanity check for ucontexts without VSX, but where VSX is used according the MSR. Fixes for both 32 and 64bit processes on 64bit kernels Kudos to Paulus for noticing. Signed-off-by: Michael Neuling <mikey@neuling.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/kernel/signal_32.c39
-rw-r--r--arch/powerpc/kernel/signal_64.c36
2 files changed, 70 insertions, 5 deletions
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index 9991e2a58bf4..6f6810db0a74 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -68,6 +68,13 @@
68#define ucontext ucontext32 68#define ucontext ucontext32
69 69
70/* 70/*
71 * Userspace code may pass a ucontext which doesn't include VSX added
72 * at the end. We need to check for this case.
73 */
74#define UCONTEXTSIZEWITHOUTVSX \
75 (sizeof(struct ucontext) - sizeof(elf_vsrreghalf_t32))
76
77/*
71 * Returning 0 means we return to userspace via 78 * Returning 0 means we return to userspace via
72 * ret_from_except and thus restore all user 79 * ret_from_except and thus restore all user
73 * registers from *regs. This is what we need 80 * registers from *regs. This is what we need
@@ -930,12 +937,42 @@ long sys_swapcontext(struct ucontext __user *old_ctx,
930{ 937{
931 unsigned char tmp; 938 unsigned char tmp;
932 939
940#ifdef CONFIG_PPC64
941 unsigned long new_msr = 0;
942
943 if (new_ctx &&
944 __get_user(new_msr, &new_ctx->uc_mcontext.mc_gregs[PT_MSR]))
945 return -EFAULT;
946 /*
947 * Check that the context is not smaller than the original
948 * size (with VMX but without VSX)
949 */
950 if (ctx_size < UCONTEXTSIZEWITHOUTVSX)
951 return -EINVAL;
952 /*
953 * If the new context state sets the MSR VSX bits but
954 * it doesn't provide VSX state.
955 */
956 if ((ctx_size < sizeof(struct ucontext)) &&
957 (new_msr & MSR_VSX))
958 return -EINVAL;
959#ifdef CONFIG_VSX
960 /*
961 * If userspace doesn't provide enough room for VSX data,
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
933 /* Context size is for future use. Right now, we only make sure 970 /* Context size is for future use. Right now, we only make sure
934 * we are passed something we understand 971 * we are passed something we understand
935 */ 972 */
936 if (ctx_size < sizeof(struct ucontext)) 973 if (ctx_size < sizeof(struct ucontext))
937 return -EINVAL; 974 return -EINVAL;
938 975#endif
939 if (old_ctx != NULL) { 976 if (old_ctx != NULL) {
940 struct mcontext __user *mctx; 977 struct mcontext __user *mctx;
941 978
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index 93ebfb6944b6..5f9d2ef2e24b 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -268,6 +268,13 @@ static long setup_trampoline(unsigned int syscall, unsigned int __user *tramp)
268} 268}
269 269
270/* 270/*
271 * Userspace code may pass a ucontext which doesn't include VSX added
272 * at the end. We need to check for this case.
273 */
274#define UCONTEXTSIZEWITHOUTVSX \
275 (sizeof(struct ucontext) - 32*sizeof(long))
276
277/*
271 * Handle {get,set,swap}_context operations 278 * Handle {get,set,swap}_context operations
272 */ 279 */
273int sys_swapcontext(struct ucontext __user *old_ctx, 280int sys_swapcontext(struct ucontext __user *old_ctx,
@@ -276,13 +283,34 @@ int sys_swapcontext(struct ucontext __user *old_ctx,
276{ 283{
277 unsigned char tmp; 284 unsigned char tmp;
278 sigset_t set; 285 sigset_t set;
286 unsigned long new_msr = 0;
279 287
280 /* Context size is for future use. Right now, we only make sure 288 if (new_ctx &&
281 * we are passed something we understand 289 __get_user(new_msr, &new_ctx->uc_mcontext.gp_regs[PT_MSR]))
290 return -EFAULT;
291 /*
292 * Check that the context is not smaller than the original
293 * size (with VMX but without VSX)
282 */ 294 */
283 if (ctx_size < sizeof(struct ucontext)) 295 if (ctx_size < UCONTEXTSIZEWITHOUTVSX)
284 return -EINVAL; 296 return -EINVAL;
285 297 /*
298 * If the new context state sets the MSR VSX bits but
299 * it doesn't provide VSX state.
300 */
301 if ((ctx_size < sizeof(struct ucontext)) &&
302 (new_msr & MSR_VSX))
303 return -EINVAL;
304#ifdef CONFIG_VSX
305 /*
306 * If userspace doesn't provide enough room for VSX data,
307 * but current thread has used VSX, we don't have anywhere
308 * to store the full context back into.
309 */
310 if ((ctx_size < sizeof(struct ucontext)) &&
311 (current->thread.used_vsr && old_ctx))
312 return -EINVAL;
313#endif
286 if (old_ctx != NULL) { 314 if (old_ctx != NULL) {
287 if (!access_ok(VERIFY_WRITE, old_ctx, sizeof(*old_ctx)) 315 if (!access_ok(VERIFY_WRITE, old_ctx, sizeof(*old_ctx))
288 || setup_sigcontext(&old_ctx->uc_mcontext, regs, 0, NULL, 0) 316 || setup_sigcontext(&old_ctx->uc_mcontext, regs, 0, NULL, 0)