diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2013-10-15 10:08:34 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2013-10-24 11:17:11 -0400 |
commit | 4725c86055f5bbdcdfe47199c0715881893a2c79 (patch) | |
tree | e201bd8d8a11e58117e9ecf360f3fab43c6267e9 /arch/s390/kernel/signal.c | |
parent | 01a7cfa24afd8e18ce691d3c2f1e1f093cf3a900 (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.c | 21 |
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, ®s->gprs, sizeof(sregs->regs.gprs)); | 63 | memcpy(&user_sregs.regs.gprs, ®s->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(¤t->thread.fp_regs); | 70 | save_fp_ctl(¤t->thread.fp_regs.fpc); |
71 | save_fp_regs(current->thread.fp_regs.fprs); | ||
71 | memcpy(&user_sregs.fpregs, ¤t->thread.fp_regs, | 72 | memcpy(&user_sregs.fpregs, ¤t->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(®s->gprs, &user_sregs.regs.gprs, sizeof(sregs->regs.gprs)); | 104 | memcpy(®s->gprs, &user_sregs.regs.gprs, sizeof(sregs->regs.gprs)); |
99 | memcpy(¤t->thread.acrs, &user_sregs.regs.acrs, | 105 | memcpy(¤t->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(¤t->thread.fp_regs, &user_sregs.fpregs, | 109 | memcpy(¤t->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(¤t->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 | } |