]> Pileus Git - ~andy/linux/commitdiff
s390: fix handling of runtime instrumentation psw bit
authorHeiko Carstens <heiko.carstens@de.ibm.com>
Wed, 16 Oct 2013 07:58:01 +0000 (09:58 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Thu, 24 Oct 2013 15:17:11 +0000 (17:17 +0200)
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>
arch/s390/include/asm/compat.h
arch/s390/include/asm/processor.h
arch/s390/include/uapi/asm/ptrace.h
arch/s390/kernel/compat_signal.c
arch/s390/kernel/ptrace.c
arch/s390/kernel/signal.c

index c1e7c646727cd4c71c56a5040a477a6d8618d565..7604cabf02c04ea0b135c3b2afeff2a2b39b226d 100644 (file)
@@ -22,6 +22,7 @@
 #define PSW32_MASK_ASC         0x0000C000UL
 #define PSW32_MASK_CC          0x00003000UL
 #define PSW32_MASK_PM          0x00000f00UL
+#define PSW32_MASK_RI          0x00000080UL
 
 #define PSW32_MASK_USER                0x0000FF00UL
 
index 3caaf6548ef5d62140e9b15949221bd9907971af..a56e63483e0f73bc36b8493c1a34b5cd1d3f5579 100644 (file)
@@ -175,6 +175,9 @@ unsigned long get_wchan(struct task_struct *p);
 #define KSTK_EIP(tsk)  (task_pt_regs(tsk)->psw.addr)
 #define KSTK_ESP(tsk)  (task_pt_regs(tsk)->gprs[15])
 
+/* Has task runtime instrumentation enabled ? */
+#define is_ri_task(tsk) (!!(tsk)->thread.ri_cb)
+
 static inline unsigned short stap(void)
 {
        unsigned short cpu_address;
index 1d2f47548e1d72b93c2788b472608900822872c3..7e0b498a2c2ba95c8ca56537e673b18c4a0065d3 100644 (file)
@@ -263,7 +263,7 @@ typedef struct
 #define PSW_MASK_EA            0x0000000100000000UL
 #define PSW_MASK_BA            0x0000000080000000UL
 
-#define PSW_MASK_USER          0x0000FF8180000000UL
+#define PSW_MASK_USER          0x0000FF0180000000UL
 
 #define PSW_ADDR_AMODE         0x0000000000000000UL
 #define PSW_ADDR_INSN          0xFFFFFFFFFFFFFFFFUL
index ceeaaa6633a182d857f15220a3ae24e83ee21ce9..8764c88a84fef76da2e7d46dd2def988bb013f92 100644 (file)
@@ -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)
index 67db29efae324d3583ce52c2db1ef676507cd9f1..1d1f9c30c5fa38d54223093e6d2b167a6007ad81 100644 (file)
@@ -198,9 +198,11 @@ static unsigned long __peek_user(struct task_struct *child, addr_t addr)
                 * psw and gprs are stored on the stack
                 */
                tmp = *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr);
-               if (addr == (addr_t) &dummy->regs.psw.mask)
+               if (addr == (addr_t) &dummy->regs.psw.mask) {
                        /* Return a clean psw mask. */
-                       tmp = PSW_USER_BITS | (tmp & PSW_MASK_USER);
+                       tmp &= PSW_MASK_USER | PSW_MASK_RI;
+                       tmp |= PSW_USER_BITS;
+               }
 
        } else if (addr < (addr_t) &dummy->regs.orig_gpr2) {
                /*
@@ -320,11 +322,15 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
                /*
                 * psw and gprs are stored on the stack
                 */
-               if (addr == (addr_t) &dummy->regs.psw.mask &&
-                   ((data & ~PSW_MASK_USER) != PSW_USER_BITS ||
-                    ((data & PSW_MASK_EA) && !(data & PSW_MASK_BA))))
-                       /* Invalid psw mask. */
-                       return -EINVAL;
+               if (addr == (addr_t) &dummy->regs.psw.mask) {
+                       unsigned long mask = PSW_MASK_USER;
+
+                       mask |= is_ri_task(child) ? PSW_MASK_RI : 0;
+                       if ((data & ~mask) != PSW_USER_BITS)
+                               return -EINVAL;
+                       if ((data & PSW_MASK_EA) && !(data & PSW_MASK_BA))
+                               return -EINVAL;
+               }
                *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr) = data;
 
        } 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)
                if (addr == (addr_t) &dummy32->regs.psw.mask) {
                        /* Fake a 31 bit psw mask. */
                        tmp = (__u32)(regs->psw.mask >> 32);
-                       tmp = psw32_user_bits | (tmp & PSW32_MASK_USER);
+                       tmp &= PSW32_MASK_USER | PSW32_MASK_RI;
+                       tmp |= psw32_user_bits;
                } else if (addr == (addr_t) &dummy32->regs.psw.addr) {
                        /* Fake a 31 bit psw address. */
                        tmp = (__u32) regs->psw.addr |
@@ -653,13 +660,16 @@ static int __poke_user_compat(struct task_struct *child,
                 * psw, gprs, acrs and orig_gpr2 are stored on the stack
                 */
                if (addr == (addr_t) &dummy32->regs.psw.mask) {
+                       __u32 mask = PSW32_MASK_USER;
+
+                       mask |= is_ri_task(child) ? PSW32_MASK_RI : 0;
                        /* Build a 64 bit psw mask from 31 bit mask. */
-                       if ((tmp & ~PSW32_MASK_USER) != psw32_user_bits)
+                       if ((tmp & ~mask) != psw32_user_bits)
                                /* Invalid psw mask. */
                                return -EINVAL;
                        regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
                                (regs->psw.mask & PSW_MASK_BA) |
-                               (__u64)(tmp & PSW32_MASK_USER) << 32;
+                               (__u64)(tmp & mask) << 32;
                } else if (addr == (addr_t) &dummy32->regs.psw.addr) {
                        /* Build a 64 bit psw address from 31 bit address. */
                        regs->psw.addr = (__u64) tmp & PSW32_ADDR_INSN;
index 4c28c39e37185a98a9174f930b9468121db534b2..fb535874a2464853168c9a9182620acf10e37c74 100644 (file)
@@ -58,7 +58,7 @@ static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
        /* Copy a 'clean' PSW mask to the user to avoid leaking
           information about whether PER is currently on.  */
        user_sregs.regs.psw.mask = PSW_USER_BITS |
-               (regs->psw.mask & PSW_MASK_USER);
+               (regs->psw.mask & (PSW_MASK_USER | PSW_MASK_RI));
        user_sregs.regs.psw.addr = regs->psw.addr;
        memcpy(&user_sregs.regs.gprs, &regs->gprs, sizeof(sregs->regs.gprs));
        memcpy(&user_sregs.regs.acrs, current->thread.acrs,
@@ -86,13 +86,16 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
        if (__copy_from_user(&user_sregs, sregs, sizeof(user_sregs)))
                return -EFAULT;
 
+       if (!is_ri_task(current) && (user_sregs.regs.psw.mask & PSW_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;
 
        /* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */
        regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
-               (user_sregs.regs.psw.mask & PSW_MASK_USER);
+               (user_sregs.regs.psw.mask & (PSW_MASK_USER | PSW_MASK_RI));
        /* Check for invalid user address space control. */
        if ((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_HOME)
                regs->psw.mask = PSW_ASC_PRIMARY |