diff options
| -rw-r--r-- | arch/powerpc/kernel/signal_32.c | 39 | ||||
| -rw-r--r-- | arch/powerpc/kernel/signal_64.c | 36 |
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 | */ |
| 273 | int sys_swapcontext(struct ucontext __user *old_ctx, | 280 | int 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) |
