aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHeiko Carstens <heiko.carstens@de.ibm.com>2013-10-16 03:58:01 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2013-10-24 11:17:11 -0400
commit5ebf250dabbae83ad875f0dda5a108503cf78f3b (patch)
tree3b4cd4dd3540cbd9c9852293801b84c9b25146f8
parent4725c86055f5bbdcdfe47199c0715881893a2c79 (diff)
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>
-rw-r--r--arch/s390/include/asm/compat.h1
-rw-r--r--arch/s390/include/asm/processor.h3
-rw-r--r--arch/s390/include/uapi/asm/ptrace.h2
-rw-r--r--arch/s390/kernel/compat_signal.c9
-rw-r--r--arch/s390/kernel/ptrace.c30
-rw-r--r--arch/s390/kernel/signal.c7
6 files changed, 37 insertions, 15 deletions
diff --git a/arch/s390/include/asm/compat.h b/arch/s390/include/asm/compat.h
index c1e7c646727c..7604cabf02c0 100644
--- a/arch/s390/include/asm/compat.h
+++ b/arch/s390/include/asm/compat.h
@@ -22,6 +22,7 @@
22#define PSW32_MASK_ASC 0x0000C000UL 22#define PSW32_MASK_ASC 0x0000C000UL
23#define PSW32_MASK_CC 0x00003000UL 23#define PSW32_MASK_CC 0x00003000UL
24#define PSW32_MASK_PM 0x00000f00UL 24#define PSW32_MASK_PM 0x00000f00UL
25#define PSW32_MASK_RI 0x00000080UL
25 26
26#define PSW32_MASK_USER 0x0000FF00UL 27#define PSW32_MASK_USER 0x0000FF00UL
27 28
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h
index 3caaf6548ef5..a56e63483e0f 100644
--- a/arch/s390/include/asm/processor.h
+++ b/arch/s390/include/asm/processor.h
@@ -175,6 +175,9 @@ unsigned long get_wchan(struct task_struct *p);
175#define KSTK_EIP(tsk) (task_pt_regs(tsk)->psw.addr) 175#define KSTK_EIP(tsk) (task_pt_regs(tsk)->psw.addr)
176#define KSTK_ESP(tsk) (task_pt_regs(tsk)->gprs[15]) 176#define KSTK_ESP(tsk) (task_pt_regs(tsk)->gprs[15])
177 177
178/* Has task runtime instrumentation enabled ? */
179#define is_ri_task(tsk) (!!(tsk)->thread.ri_cb)
180
178static inline unsigned short stap(void) 181static inline unsigned short stap(void)
179{ 182{
180 unsigned short cpu_address; 183 unsigned short cpu_address;
diff --git a/arch/s390/include/uapi/asm/ptrace.h b/arch/s390/include/uapi/asm/ptrace.h
index 1d2f47548e1d..7e0b498a2c2b 100644
--- a/arch/s390/include/uapi/asm/ptrace.h
+++ b/arch/s390/include/uapi/asm/ptrace.h
@@ -263,7 +263,7 @@ typedef struct
263#define PSW_MASK_EA 0x0000000100000000UL 263#define PSW_MASK_EA 0x0000000100000000UL
264#define PSW_MASK_BA 0x0000000080000000UL 264#define PSW_MASK_BA 0x0000000080000000UL
265 265
266#define PSW_MASK_USER 0x0000FF8180000000UL 266#define PSW_MASK_USER 0x0000FF0180000000UL
267 267
268#define PSW_ADDR_AMODE 0x0000000000000000UL 268#define PSW_ADDR_AMODE 0x0000000000000000UL
269#define PSW_ADDR_INSN 0xFFFFFFFFFFFFFFFFUL 269#define PSW_ADDR_INSN 0xFFFFFFFFFFFFFFFFUL
diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c
index ceeaaa6633a1..8764c88a84fe 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)
156 _sigregs32 user_sregs; 156 _sigregs32 user_sregs;
157 int i; 157 int i;
158 158
159 user_sregs.regs.psw.mask = psw32_user_bits | 159 user_sregs.regs.psw.mask = (__u32)(regs->psw.mask >> 32);
160 ((__u32)(regs->psw.mask >> 32) & PSW32_MASK_USER); 160 user_sregs.regs.psw.mask &= PSW32_MASK_USER | PSW32_MASK_RI;
161 user_sregs.regs.psw.mask |= psw32_user_bits;
161 user_sregs.regs.psw.addr = (__u32) regs->psw.addr | 162 user_sregs.regs.psw.addr = (__u32) regs->psw.addr |
162 (__u32)(regs->psw.mask & PSW_MASK_BA); 163 (__u32)(regs->psw.mask & PSW_MASK_BA);
163 for (i = 0; i < NUM_GPRS; i++) 164 for (i = 0; i < NUM_GPRS; i++)
@@ -185,6 +186,9 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
185 if (__copy_from_user(&user_sregs, &sregs->regs, sizeof(user_sregs))) 186 if (__copy_from_user(&user_sregs, &sregs->regs, sizeof(user_sregs)))
186 return -EFAULT; 187 return -EFAULT;
187 188
189 if (!is_ri_task(current) && (user_sregs.regs.psw.mask & PSW32_MASK_RI))
190 return -EINVAL;
191
188 /* Loading the floating-point-control word can fail. Do that first. */ 192 /* Loading the floating-point-control word can fail. Do that first. */
189 if (restore_fp_ctl(&user_sregs.fpregs.fpc)) 193 if (restore_fp_ctl(&user_sregs.fpregs.fpc))
190 return -EINVAL; 194 return -EINVAL;
@@ -192,6 +196,7 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
192 /* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */ 196 /* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */
193 regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) | 197 regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
194 (__u64)(user_sregs.regs.psw.mask & PSW32_MASK_USER) << 32 | 198 (__u64)(user_sregs.regs.psw.mask & PSW32_MASK_USER) << 32 |
199 (__u64)(user_sregs.regs.psw.mask & PSW32_MASK_RI) << 32 |
195 (__u64)(user_sregs.regs.psw.addr & PSW32_ADDR_AMODE); 200 (__u64)(user_sregs.regs.psw.addr & PSW32_ADDR_AMODE);
196 /* Check for invalid user address space control. */ 201 /* Check for invalid user address space control. */
197 if ((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_HOME) 202 if ((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_HOME)
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index 67db29efae32..1d1f9c30c5fa 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -198,9 +198,11 @@ static unsigned long __peek_user(struct task_struct *child, addr_t addr)
198 * psw and gprs are stored on the stack 198 * psw and gprs are stored on the stack
199 */ 199 */
200 tmp = *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr); 200 tmp = *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr);
201 if (addr == (addr_t) &dummy->regs.psw.mask) 201 if (addr == (addr_t) &dummy->regs.psw.mask) {
202 /* Return a clean psw mask. */ 202 /* Return a clean psw mask. */
203 tmp = PSW_USER_BITS | (tmp & PSW_MASK_USER); 203 tmp &= PSW_MASK_USER | PSW_MASK_RI;
204 tmp |= PSW_USER_BITS;
205 }
204 206
205 } else if (addr < (addr_t) &dummy->regs.orig_gpr2) { 207 } else if (addr < (addr_t) &dummy->regs.orig_gpr2) {
206 /* 208 /*
@@ -320,11 +322,15 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
320 /* 322 /*
321 * psw and gprs are stored on the stack 323 * psw and gprs are stored on the stack
322 */ 324 */
323 if (addr == (addr_t) &dummy->regs.psw.mask && 325 if (addr == (addr_t) &dummy->regs.psw.mask) {
324 ((data & ~PSW_MASK_USER) != PSW_USER_BITS || 326 unsigned long mask = PSW_MASK_USER;
325 ((data & PSW_MASK_EA) && !(data & PSW_MASK_BA)))) 327
326 /* Invalid psw mask. */ 328 mask |= is_ri_task(child) ? PSW_MASK_RI : 0;
327 return -EINVAL; 329 if ((data & ~mask) != PSW_USER_BITS)
330 return -EINVAL;
331 if ((data & PSW_MASK_EA) && !(data & PSW_MASK_BA))
332 return -EINVAL;
333 }
328 *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr) = data; 334 *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr) = data;
329 335
330 } else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) { 336 } else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) {
@@ -556,7 +562,8 @@ static u32 __peek_user_compat(struct task_struct *child, addr_t addr)
556 if (addr == (addr_t) &dummy32->regs.psw.mask) { 562 if (addr == (addr_t) &dummy32->regs.psw.mask) {
557 /* Fake a 31 bit psw mask. */ 563 /* Fake a 31 bit psw mask. */
558 tmp = (__u32)(regs->psw.mask >> 32); 564 tmp = (__u32)(regs->psw.mask >> 32);
559 tmp = psw32_user_bits | (tmp & PSW32_MASK_USER); 565 tmp &= PSW32_MASK_USER | PSW32_MASK_RI;
566 tmp |= psw32_user_bits;
560 } else if (addr == (addr_t) &dummy32->regs.psw.addr) { 567 } else if (addr == (addr_t) &dummy32->regs.psw.addr) {
561 /* Fake a 31 bit psw address. */ 568 /* Fake a 31 bit psw address. */
562 tmp = (__u32) regs->psw.addr | 569 tmp = (__u32) regs->psw.addr |
@@ -653,13 +660,16 @@ static int __poke_user_compat(struct task_struct *child,
653 * psw, gprs, acrs and orig_gpr2 are stored on the stack 660 * psw, gprs, acrs and orig_gpr2 are stored on the stack
654 */ 661 */
655 if (addr == (addr_t) &dummy32->regs.psw.mask) { 662 if (addr == (addr_t) &dummy32->regs.psw.mask) {
663 __u32 mask = PSW32_MASK_USER;
664
665 mask |= is_ri_task(child) ? PSW32_MASK_RI : 0;
656 /* Build a 64 bit psw mask from 31 bit mask. */ 666 /* Build a 64 bit psw mask from 31 bit mask. */
657 if ((tmp & ~PSW32_MASK_USER) != psw32_user_bits) 667 if ((tmp & ~mask) != psw32_user_bits)
658 /* Invalid psw mask. */ 668 /* Invalid psw mask. */
659 return -EINVAL; 669 return -EINVAL;
660 regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) | 670 regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
661 (regs->psw.mask & PSW_MASK_BA) | 671 (regs->psw.mask & PSW_MASK_BA) |
662 (__u64)(tmp & PSW32_MASK_USER) << 32; 672 (__u64)(tmp & mask) << 32;
663 } else if (addr == (addr_t) &dummy32->regs.psw.addr) { 673 } else if (addr == (addr_t) &dummy32->regs.psw.addr) {
664 /* Build a 64 bit psw address from 31 bit address. */ 674 /* Build a 64 bit psw address from 31 bit address. */
665 regs->psw.addr = (__u64) tmp & PSW32_ADDR_INSN; 675 regs->psw.addr = (__u64) tmp & PSW32_ADDR_INSN;
diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c
index 4c28c39e3718..fb535874a246 100644
--- a/arch/s390/kernel/signal.c
+++ b/arch/s390/kernel/signal.c
@@ -58,7 +58,7 @@ static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
58 /* Copy a 'clean' PSW mask to the user to avoid leaking 58 /* Copy a 'clean' PSW mask to the user to avoid leaking
59 information about whether PER is currently on. */ 59 information about whether PER is currently on. */
60 user_sregs.regs.psw.mask = PSW_USER_BITS | 60 user_sregs.regs.psw.mask = PSW_USER_BITS |
61 (regs->psw.mask & PSW_MASK_USER); 61 (regs->psw.mask & (PSW_MASK_USER | PSW_MASK_RI));
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,
@@ -86,13 +86,16 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
86 if (__copy_from_user(&user_sregs, sregs, sizeof(user_sregs))) 86 if (__copy_from_user(&user_sregs, sregs, sizeof(user_sregs)))
87 return -EFAULT; 87 return -EFAULT;
88 88
89 if (!is_ri_task(current) && (user_sregs.regs.psw.mask & PSW_MASK_RI))
90 return -EINVAL;
91
89 /* Loading the floating-point-control word can fail. Do that first. */ 92 /* Loading the floating-point-control word can fail. Do that first. */
90 if (restore_fp_ctl(&user_sregs.fpregs.fpc)) 93 if (restore_fp_ctl(&user_sregs.fpregs.fpc))
91 return -EINVAL; 94 return -EINVAL;
92 95
93 /* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */ 96 /* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */
94 regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) | 97 regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
95 (user_sregs.regs.psw.mask & PSW_MASK_USER); 98 (user_sregs.regs.psw.mask & (PSW_MASK_USER | PSW_MASK_RI));
96 /* Check for invalid user address space control. */ 99 /* Check for invalid user address space control. */
97 if ((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_HOME) 100 if ((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_HOME)
98 regs->psw.mask = PSW_ASC_PRIMARY | 101 regs->psw.mask = PSW_ASC_PRIMARY |