vserver 2.0 rc7
[linux-2.6.git] / arch / s390 / kernel / ptrace.c
index 1176b4f..3be3319 100644 (file)
@@ -31,6 +31,8 @@
 #include <linux/ptrace.h>
 #include <linux/user.h>
 #include <linux/security.h>
+#include <linux/audit.h>
+#include <linux/signal.h>
 
 #include <asm/segment.h>
 #include <asm/page.h>
@@ -38,6 +40,7 @@
 #include <asm/pgalloc.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
+#include <asm/unistd.h>
 
 #ifdef CONFIG_S390_SUPPORT
 #include "compat_ptrace.h"
@@ -128,20 +131,26 @@ static int
 peek_user(struct task_struct *child, addr_t addr, addr_t data)
 {
        struct user *dummy = NULL;
-       addr_t offset, tmp;
+       addr_t offset, tmp, mask;
 
        /*
         * Stupid gdb peeks/pokes the access registers in 64 bit with
         * an alignment of 4. Programmers from hell...
         */
-       if ((addr & 3) || addr > sizeof(struct user) - __ADDR_MASK)
+       mask = __ADDR_MASK;
+#ifdef CONFIG_ARCH_S390X
+       if (addr >= (addr_t) &dummy->regs.acrs &&
+           addr < (addr_t) &dummy->regs.orig_gpr2)
+               mask = 3;
+#endif
+       if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
                return -EIO;
 
        if (addr < (addr_t) &dummy->regs.acrs) {
                /*
                 * psw and gprs are stored on the stack
                 */
-               tmp = *(addr_t *)((addr_t) __KSTK_PTREGS(child) + addr);
+               tmp = *(addr_t *)((addr_t) &__KSTK_PTREGS(child)->psw + addr);
                if (addr == (addr_t) &dummy->regs.psw.mask)
                        /* Remove per bit from user psw. */
                        tmp &= ~PSW_MASK_PER;
@@ -151,6 +160,16 @@ peek_user(struct task_struct *child, addr_t addr, addr_t data)
                 * access registers are stored in the thread structure
                 */
                offset = addr - (addr_t) &dummy->regs.acrs;
+#ifdef CONFIG_ARCH_S390X
+               /*
+                * Very special case: old & broken 64 bit gdb reading
+                * from acrs[15]. Result is a 64 bit value. Read the
+                * 32 bit acrs[15] value and shift it by 32. Sick...
+                */
+               if (addr == (addr_t) &dummy->regs.acrs[15])
+                       tmp = ((unsigned long) child->thread.acrs[15]) << 32;
+               else
+#endif
                tmp = *(addr_t *)((addr_t) &child->thread.acrs + offset);
 
        } else if (addr == (addr_t) &dummy->regs.orig_gpr2) {
@@ -165,6 +184,9 @@ peek_user(struct task_struct *child, addr_t addr, addr_t data)
                 */
                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);
 
        } else if (addr < (addr_t) (&dummy->regs.per_info + 1)) {
                /*
@@ -176,7 +198,7 @@ peek_user(struct task_struct *child, addr_t addr, addr_t data)
        } else
                tmp = 0;
 
-       return put_user(tmp, (addr_t *) data);
+       return put_user(tmp, (addr_t __user *) data);
 }
 
 /*
@@ -189,13 +211,19 @@ static int
 poke_user(struct task_struct *child, addr_t addr, addr_t data)
 {
        struct user *dummy = NULL;
-       addr_t offset;
+       addr_t offset, mask;
 
        /*
         * Stupid gdb peeks/pokes the access registers in 64 bit with
         * an alignment of 4. Programmers from hell indeed...
         */
-       if ((addr & 3) || addr > sizeof(struct user) - __ADDR_MASK)
+       mask = __ADDR_MASK;
+#ifdef CONFIG_ARCH_S390X
+       if (addr >= (addr_t) &dummy->regs.acrs &&
+           addr < (addr_t) &dummy->regs.orig_gpr2)
+               mask = 3;
+#endif
+       if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
                return -EIO;
 
        if (addr < (addr_t) &dummy->regs.acrs) {
@@ -215,13 +243,24 @@ poke_user(struct task_struct *child, addr_t addr, addr_t data)
                           high order bit but older gdb's rely on it */
                        data |= PSW_ADDR_AMODE;
 #endif
-               *(addr_t *)((addr_t) __KSTK_PTREGS(child) + addr) = data;
+               *(addr_t *)((addr_t) &__KSTK_PTREGS(child)->psw + addr) = data;
 
        } else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) {
                /*
                 * access registers are stored in the thread structure
                 */
                offset = addr - (addr_t) &dummy->regs.acrs;
+#ifdef CONFIG_ARCH_S390X
+               /*
+                * Very special case: old & broken 64 bit gdb writing
+                * to acrs[15] with a 64 bit value. Ignore the lower
+                * half of the value and write the upper 32 bit to
+                * acrs[15]. Sick...
+                */
+               if (addr == (addr_t) &dummy->regs.acrs[15])
+                       child->thread.acrs[15] = (unsigned int) (data >> 32);
+               else
+#endif
                *(addr_t *)((addr_t) &child->thread.acrs + offset) = data;
 
        } else if (addr == (addr_t) &dummy->regs.orig_gpr2) {
@@ -235,7 +274,8 @@ 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 & ~FPC_VALID_MASK) != 0)
+                   (data & ~((unsigned long) FPC_VALID_MASK
+                             << (BITS_PER_LONG - 32))) != 0)
                        return -EINVAL;
                offset = addr - (addr_t) &dummy->regs.fp_regs;
                *(addr_t *)((addr_t) &child->thread.fp_regs + offset) = data;
@@ -269,7 +309,7 @@ do_ptrace_normal(struct task_struct *child, long request, long addr, long data)
                copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
                if (copied != sizeof(tmp))
                        return -EIO;
-               return put_user(tmp, (unsigned long *) data);
+               return put_user(tmp, (unsigned long __user *) data);
 
        case PTRACE_PEEKUSR:
                /* read the word at location addr in the USER area. */
@@ -291,7 +331,8 @@ do_ptrace_normal(struct task_struct *child, long request, long addr, long data)
 
        case PTRACE_PEEKUSR_AREA:
        case PTRACE_POKEUSR_AREA:
-               if (copy_from_user(&parea, (void *) addr, sizeof(parea)))
+               if (copy_from_user(&parea, (void __user *) addr,
+                                                       sizeof(parea)))
                        return -EFAULT;
                addr = parea.kernel_addr;
                data = parea.process_addr;
@@ -301,7 +342,7 @@ do_ptrace_normal(struct task_struct *child, long request, long addr, long data)
                                ret = peek_user(child, addr, data);
                        else {
                                addr_t tmp;
-                               if (get_user (tmp, (addr_t *) data))
+                               if (get_user (tmp, (addr_t __user *) data))
                                        return -EFAULT;
                                ret = poke_user(child, addr, tmp);
                        }
@@ -360,7 +401,7 @@ peek_user_emu31(struct task_struct *child, addr_t addr, addr_t data)
                                PSW32_ADDR_AMODE31;
                } else {
                        /* gpr 0-15 */
-                       tmp = *(__u32 *)((addr_t) __KSTK_PTREGS(child) + 
+                       tmp = *(__u32 *)((addr_t) &__KSTK_PTREGS(child)->psw +
                                         addr*2 + 4);
                }
        } else if (addr < (addr_t) (&dummy32->regs.orig_gpr2)) {
@@ -402,7 +443,7 @@ peek_user_emu31(struct task_struct *child, addr_t addr, addr_t data)
        } else
                tmp = 0;
 
-       return put_user(tmp, (__u32 *) data);
+       return put_user(tmp, (__u32 __user *) data);
 }
 
 /*
@@ -439,8 +480,8 @@ poke_user_emu31(struct task_struct *child, addr_t addr, addr_t data)
                                (__u64) tmp & PSW32_ADDR_INSN;
                } else {
                        /* gpr 0-15 */
-                       *(__u32*)((addr_t) __KSTK_PTREGS(child) + addr*2 + 4) =
-                               tmp;
+                       *(__u32*)((addr_t) &__KSTK_PTREGS(child)->psw
+                                 + addr*2 + 4) = tmp;
                }
        } else if (addr < (addr_t) (&dummy32->regs.orig_gpr2)) {
                /*
@@ -509,7 +550,7 @@ do_ptrace_emu31(struct task_struct *child, long request, long addr, long data)
                copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
                if (copied != sizeof(tmp))
                        return -EIO;
-               return put_user(tmp, (unsigned int *) data);
+               return put_user(tmp, (unsigned int __user *) data);
 
        case PTRACE_PEEKUSR:
                /* read the word at location addr in the USER area. */
@@ -530,7 +571,8 @@ do_ptrace_emu31(struct task_struct *child, long request, long addr, long data)
 
        case PTRACE_PEEKUSR_AREA:
        case PTRACE_POKEUSR_AREA:
-               if (copy_from_user(&parea, (void *) addr, sizeof(parea)))
+               if (copy_from_user(&parea, (void __user *) addr,
+                                                       sizeof(parea)))
                        return -EFAULT;
                addr = parea.kernel_addr;
                data = parea.process_addr;
@@ -540,7 +582,7 @@ do_ptrace_emu31(struct task_struct *child, long request, long addr, long data)
                                ret = peek_user_emu31(child, addr, data);
                        else {
                                __u32 tmp;
-                               if (get_user (tmp, (__u32 *) data))
+                               if (get_user (tmp, (__u32 __user *) data))
                                        return -EFAULT;
                                ret = poke_user_emu31(child, addr, tmp);
                        }
@@ -551,6 +593,19 @@ do_ptrace_emu31(struct task_struct *child, long request, long addr, long data)
                        copied += sizeof(unsigned int);
                }
                return 0;
+       case PTRACE_GETEVENTMSG:
+               return put_user((__u32) child->ptrace_message,
+                               (unsigned int __user *) data);
+       case PTRACE_GETSIGINFO:
+               if (child->last_siginfo == NULL)
+                       return -EINVAL;
+               return copy_siginfo_to_user32((compat_siginfo_t __user *) data,
+                                             child->last_siginfo);
+       case PTRACE_SETSIGINFO:
+               if (child->last_siginfo == NULL)
+                       return -EINVAL;
+               return copy_siginfo_from_user32(child->last_siginfo,
+                                               (compat_siginfo_t __user *) data);
        }
        return ptrace_request(child, request, addr, data);
 }
@@ -593,7 +648,7 @@ do_ptrace(struct task_struct *child, long request, long addr, long data)
                /* continue and stop at next (return from) syscall */
        case PTRACE_CONT:
                /* restart after signal. */
-               if ((unsigned long) data >= _NSIG)
+               if (!valid_signal(data))
                        return -EIO;
                if (request == PTRACE_SYSCALL)
                        set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
@@ -611,7 +666,7 @@ do_ptrace(struct task_struct *child, long request, long addr, long data)
                 * perhaps it should be put in the status that it wants to 
                 * exit.
                 */
-               if (child->state == TASK_ZOMBIE) /* already dead */
+               if (child->exit_state == EXIT_ZOMBIE) /* already dead */
                        return 0;
                child->exit_code = SIGKILL;
                /* make sure the single step bit is not set. */
@@ -621,11 +676,14 @@ do_ptrace(struct task_struct *child, long request, long addr, long data)
 
        case PTRACE_SINGLESTEP:
                /* set the trap flag. */
-               if ((unsigned long) data >= _NSIG)
+               if (!valid_signal(data))
                        return -EIO;
                clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
                child->exit_code = data;
-               set_single_step(child);
+               if (data)
+                       set_tsk_thread_flag(child, TIF_SINGLE_STEP);
+               else
+                       set_single_step(child);
                /* give it a chance to run. */
                wake_up_process(child);
                return 0;
@@ -680,9 +738,11 @@ sys_ptrace(long request, long pid, long addr, long data)
        read_unlock(&tasklist_lock);
        if (!child)
                goto out;
+       if (!vx_check(vx_task_xid(child), VX_WATCH|VX_IDENT))
+               goto out_tsk;
 
        ret = do_ptrace(child, request, addr, data);
-
+out_tsk:
        put_task_struct(child);
 out:
        unlock_kernel();
@@ -692,21 +752,23 @@ out:
 asmlinkage void
 syscall_trace(struct pt_regs *regs, int entryexit)
 {
-       if (unlikely(current->audit_context)) {
-               if (!entryexit)
-                       audit_syscall_entry(current, regs->gprs[2],
-                                           regs->orig_gpr2, regs->gprs[3],
-                                           regs->gprs[4], regs->gprs[5]);
-               else
-                       audit_syscall_exit(current, regs->gprs[2]);
-       }
+       if (unlikely(current->audit_context) && entryexit)
+               audit_syscall_exit(current, AUDITSC_RESULT(regs->gprs[2]), regs->gprs[2]);
+
        if (!test_thread_flag(TIF_SYSCALL_TRACE))
-               return;
+               goto out;
        if (!(current->ptrace & PT_PTRACED))
-               return;
+               goto out;
        ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
                                 ? 0x80 : 0));
 
+       /*
+        * If the debuffer has set an invalid system call number,
+        * we prepare to skip the system call restart handling.
+        */
+       if (!entryexit && regs->gprs[2] >= NR_syscalls)
+               regs->trap = -1;
+
        /*
         * this isn't the same as continuing with a signal, but it will do
         * for normal use.  strace only continues with a signal if the
@@ -716,4 +778,10 @@ syscall_trace(struct pt_regs *regs, int entryexit)
                send_sig(current->exit_code, current, 1);
                current->exit_code = 0;
        }
+ out:
+       if (unlikely(current->audit_context) && !entryexit)
+               audit_syscall_entry(current, 
+                                   test_thread_flag(TIF_31BIT)?AUDIT_ARCH_S390:AUDIT_ARCH_S390X,
+                                   regs->gprs[2], regs->orig_gpr2, regs->gprs[3],
+                                   regs->gprs[4], regs->gprs[5]);
 }