summaryrefslogtreecommitdiff
path: root/arch/s390/kernel/ptrace.c
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2013-10-15 16:08:34 +0200
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2013-10-24 17:17:11 +0200
commit4725c86055f5bbdcdfe47199c0715881893a2c79 (patch)
treee201bd8d8a11e58117e9ecf360f3fab43c6267e9 /arch/s390/kernel/ptrace.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/ptrace.c')
-rw-r--r--arch/s390/kernel/ptrace.c40
1 files changed, 22 insertions, 18 deletions
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index e6abd5bd31b0..67db29efae32 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -239,8 +239,7 @@ static unsigned long __peek_user(struct task_struct *child, addr_t addr)
offset = addr - (addr_t) &dummy->regs.fp_regs;
tmp = *(addr_t *)((addr_t) &child->thread.fp_regs + offset);
if (addr == (addr_t) &dummy->regs.fp_regs.fpc)
- tmp &= (unsigned long) FPC_VALID_MASK
- << (BITS_PER_LONG - 32);
+ tmp <<= BITS_PER_LONG - 32;
} else if (addr < (addr_t) (&dummy->regs.per_info + 1)) {
/*
@@ -363,10 +362,10 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
/*
* floating point regs. are stored in the thread structure
*/
- if (addr == (addr_t) &dummy->regs.fp_regs.fpc &&
- (data & ~((unsigned long) FPC_VALID_MASK
- << (BITS_PER_LONG - 32))) != 0)
- return -EINVAL;
+ if (addr == (addr_t) &dummy->regs.fp_regs.fpc)
+ if ((unsigned int) data != 0 ||
+ test_fp_ctl(data >> (BITS_PER_LONG - 32)))
+ return -EINVAL;
offset = addr - (addr_t) &dummy->regs.fp_regs;
*(addr_t *)((addr_t) &child->thread.fp_regs + offset) = data;
@@ -696,8 +695,7 @@ static int __poke_user_compat(struct task_struct *child,
* floating point regs. are stored in the thread structure
*/
if (addr == (addr_t) &dummy32->regs.fp_regs.fpc &&
- (tmp & ~FPC_VALID_MASK) != 0)
- /* Invalid floating point control. */
+ test_fp_ctl(tmp))
return -EINVAL;
offset = addr - (addr_t) &dummy32->regs.fp_regs;
*(__u32 *)((addr_t) &child->thread.fp_regs + offset) = tmp;
@@ -895,8 +893,10 @@ static int s390_fpregs_get(struct task_struct *target,
const struct user_regset *regset, unsigned int pos,
unsigned int count, void *kbuf, void __user *ubuf)
{
- if (target == current)
- save_fp_regs(&target->thread.fp_regs);
+ if (target == current) {
+ save_fp_ctl(&target->thread.fp_regs.fpc);
+ save_fp_regs(target->thread.fp_regs.fprs);
+ }
return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
&target->thread.fp_regs, 0, -1);
@@ -909,19 +909,21 @@ static int s390_fpregs_set(struct task_struct *target,
{
int rc = 0;
- if (target == current)
- save_fp_regs(&target->thread.fp_regs);
+ if (target == current) {
+ save_fp_ctl(&target->thread.fp_regs.fpc);
+ save_fp_regs(target->thread.fp_regs.fprs);
+ }
/* If setting FPC, must validate it first. */
if (count > 0 && pos < offsetof(s390_fp_regs, fprs)) {
- u32 fpc[2] = { target->thread.fp_regs.fpc, 0 };
- rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &fpc,
+ u32 ufpc[2] = { target->thread.fp_regs.fpc, 0 };
+ rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ufpc,
0, offsetof(s390_fp_regs, fprs));
if (rc)
return rc;
- if ((fpc[0] & ~FPC_VALID_MASK) != 0 || fpc[1] != 0)
+ if (ufpc[1] != 0 || test_fp_ctl(ufpc[0]))
return -EINVAL;
- target->thread.fp_regs.fpc = fpc[0];
+ target->thread.fp_regs.fpc = ufpc[0];
}
if (rc == 0 && count > 0)
@@ -929,8 +931,10 @@ static int s390_fpregs_set(struct task_struct *target,
target->thread.fp_regs.fprs,
offsetof(s390_fp_regs, fprs), -1);
- if (rc == 0 && target == current)
- restore_fp_regs(&target->thread.fp_regs);
+ if (rc == 0 && target == current) {
+ restore_fp_ctl(&target->thread.fp_regs.fpc);
+ restore_fp_regs(target->thread.fp_regs.fprs);
+ }
return rc;
}