aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/kernel/signal.c
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2013-10-15 10:08:34 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2013-10-24 11:17:11 -0400
commit4725c86055f5bbdcdfe47199c0715881893a2c79 (patch)
treee201bd8d8a11e58117e9ecf360f3fab43c6267e9 /arch/s390/kernel/signal.c
parent01a7cfa24afd8e18ce691d3c2f1e1f093cf3a900 (diff)
s390: fix save and restore of the floating-point-control register
The FPC_VALID_MASK has been used to check the validity of the value to be loaded into the floating-point-control register. With the introduction of the floating-point extension facility and the decimal-floating-point additional bits have been defined which need to be checked in a non straight forward way. So far these bits have been ignored which can cause an incorrect results for decimal- floating-point operations, e.g. an incorrect rounding mode to be set after signal return. The static check with the FPC_VALID_MASK is replaced with a trial load of the floating-point-control value, see test_fp_ctl. In addition an information leak with the padding word between the floating-point-control word and the floating-point registers in the s390_fp_regs is fixed. Reported-by: Heiko Carstens <heiko.carstens@de.ibm.com> Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel/signal.c')
-rw-r--r--arch/s390/kernel/signal.c21
1 files changed, 13 insertions, 8 deletions
diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c
index 5c0ce019a8ae..4c28c39e3718 100644
--- a/arch/s390/kernel/signal.c
+++ b/arch/s390/kernel/signal.c
@@ -62,14 +62,15 @@ static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
62 user_sregs.regs.psw.addr = regs->psw.addr; 62 user_sregs.regs.psw.addr = regs->psw.addr;
63 memcpy(&user_sregs.regs.gprs, &regs->gprs, sizeof(sregs->regs.gprs)); 63 memcpy(&user_sregs.regs.gprs, &regs->gprs, sizeof(sregs->regs.gprs));
64 memcpy(&user_sregs.regs.acrs, current->thread.acrs, 64 memcpy(&user_sregs.regs.acrs, current->thread.acrs,
65 sizeof(sregs->regs.acrs)); 65 sizeof(user_sregs.regs.acrs));
66 /* 66 /*
67 * We have to store the fp registers to current->thread.fp_regs 67 * We have to store the fp registers to current->thread.fp_regs
68 * to merge them with the emulated registers. 68 * to merge them with the emulated registers.
69 */ 69 */
70 save_fp_regs(&current->thread.fp_regs); 70 save_fp_ctl(&current->thread.fp_regs.fpc);
71 save_fp_regs(current->thread.fp_regs.fprs);
71 memcpy(&user_sregs.fpregs, &current->thread.fp_regs, 72 memcpy(&user_sregs.fpregs, &current->thread.fp_regs,
72 sizeof(s390_fp_regs)); 73 sizeof(user_sregs.fpregs));
73 if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs))) 74 if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs)))
74 return -EFAULT; 75 return -EFAULT;
75 return 0; 76 return 0;
@@ -82,8 +83,13 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
82 /* Alwys make any pending restarted system call return -EINTR */ 83 /* Alwys make any pending restarted system call return -EINTR */
83 current_thread_info()->restart_block.fn = do_no_restart_syscall; 84 current_thread_info()->restart_block.fn = do_no_restart_syscall;
84 85
85 if (__copy_from_user(&user_sregs, sregs, sizeof(_sigregs))) 86 if (__copy_from_user(&user_sregs, sregs, sizeof(user_sregs)))
86 return -EFAULT; 87 return -EFAULT;
88
89 /* Loading the floating-point-control word can fail. Do that first. */
90 if (restore_fp_ctl(&user_sregs.fpregs.fpc))
91 return -EINVAL;
92
87 /* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */ 93 /* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */
88 regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) | 94 regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
89 (user_sregs.regs.psw.mask & PSW_MASK_USER); 95 (user_sregs.regs.psw.mask & PSW_MASK_USER);
@@ -97,14 +103,13 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
97 regs->psw.addr = user_sregs.regs.psw.addr; 103 regs->psw.addr = user_sregs.regs.psw.addr;
98 memcpy(&regs->gprs, &user_sregs.regs.gprs, sizeof(sregs->regs.gprs)); 104 memcpy(&regs->gprs, &user_sregs.regs.gprs, sizeof(sregs->regs.gprs));
99 memcpy(&current->thread.acrs, &user_sregs.regs.acrs, 105 memcpy(&current->thread.acrs, &user_sregs.regs.acrs,
100 sizeof(sregs->regs.acrs)); 106 sizeof(current->thread.acrs));
101 restore_access_regs(current->thread.acrs); 107 restore_access_regs(current->thread.acrs);
102 108
103 memcpy(&current->thread.fp_regs, &user_sregs.fpregs, 109 memcpy(&current->thread.fp_regs, &user_sregs.fpregs,
104 sizeof(s390_fp_regs)); 110 sizeof(current->thread.fp_regs));
105 current->thread.fp_regs.fpc &= FPC_VALID_MASK;
106 111
107 restore_fp_regs(&current->thread.fp_regs); 112 restore_fp_regs(current->thread.fp_regs.fprs);
108 clear_thread_flag(TIF_SYSCALL); /* No longer in a system call */ 113 clear_thread_flag(TIF_SYSCALL); /* No longer in a system call */
109 return 0; 114 return 0;
110} 115}