diff options
author | Heiko Carstens <heiko.carstens@de.ibm.com> | 2013-10-16 07:58:01 (GMT) |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2013-10-24 15:17:11 (GMT) |
commit | 5ebf250dabbae83ad875f0dda5a108503cf78f3b (patch) | |
tree | 3b4cd4dd3540cbd9c9852293801b84c9b25146f8 /arch/s390/kernel/compat_signal.c | |
parent | 4725c86055f5bbdcdfe47199c0715881893a2c79 (diff) | |
download | linux-fsl-qoriq-5ebf250dabbae83ad875f0dda5a108503cf78f3b.tar.xz |
s390: fix handling of runtime instrumentation psw bit
Fix the following bugs:
- When returning from a signal the signal handler copies the saved psw mask
from user space and uses parts of it. Especially it restores the RI bit
unconditionally. If however the machine doesn't support RI, or RI is
disabled for the task, the last lpswe instruction which returns to user
space will generate a specification exception.
To fix this check if the RI bit is allowed to be set and kill the task
if not.
- In the compat mode signal handler code the RI bit of the psw mask gets
propagated to the mask of the return psw: if user space enables RI in the
signal handler, RI will also be enabled after the signal handler is
finished.
This is a different behaviour than with 64 bit tasks. So change this to
match the 64 bit semantics, which restores the original RI bit value.
- Fix similar oddities within the ptrace code as well.
Reviewed-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel/compat_signal.c')
-rw-r--r-- | arch/s390/kernel/compat_signal.c | 9 |
1 files changed, 7 insertions, 2 deletions
diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c index ceeaaa6..8764c88 100644 --- a/arch/s390/kernel/compat_signal.c +++ b/arch/s390/kernel/compat_signal.c @@ -156,8 +156,9 @@ static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs) _sigregs32 user_sregs; int i; - user_sregs.regs.psw.mask = psw32_user_bits | - ((__u32)(regs->psw.mask >> 32) & PSW32_MASK_USER); + user_sregs.regs.psw.mask = (__u32)(regs->psw.mask >> 32); + user_sregs.regs.psw.mask &= PSW32_MASK_USER | PSW32_MASK_RI; + user_sregs.regs.psw.mask |= psw32_user_bits; user_sregs.regs.psw.addr = (__u32) regs->psw.addr | (__u32)(regs->psw.mask & PSW_MASK_BA); for (i = 0; i < NUM_GPRS; i++) @@ -185,6 +186,9 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs) if (__copy_from_user(&user_sregs, &sregs->regs, sizeof(user_sregs))) return -EFAULT; + if (!is_ri_task(current) && (user_sregs.regs.psw.mask & PSW32_MASK_RI)) + return -EINVAL; + /* Loading the floating-point-control word can fail. Do that first. */ if (restore_fp_ctl(&user_sregs.fpregs.fpc)) return -EINVAL; @@ -192,6 +196,7 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs) /* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */ regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) | (__u64)(user_sregs.regs.psw.mask & PSW32_MASK_USER) << 32 | + (__u64)(user_sregs.regs.psw.mask & PSW32_MASK_RI) << 32 | (__u64)(user_sregs.regs.psw.addr & PSW32_ADDR_AMODE); /* Check for invalid user address space control. */ if ((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_HOME) |