fedora core 2.6.10-1.12-FC2
[linux-2.6.git] / arch / ia64 / kernel / ptrace.c
index 0432abe..817caef 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Kernel support for the ptrace() and syscall tracing interfaces.
  *
- * Copyright (C) 1999-2003 Hewlett-Packard Co
+ * Copyright (C) 1999-2004 Hewlett-Packard Co
  *     David Mosberger-Tang <davidm@hpl.hp.com>
  *
  * Derived from the x86 and Alpha versions.  Most of the code in here
@@ -152,7 +152,7 @@ ia64_increment_ip (struct pt_regs *regs)
                ri = 0;
                regs->cr_iip += 16;
        } else if (ri == 2) {
-               get_user(w0, (char *) regs->cr_iip + 0);
+               get_user(w0, (char __user *) regs->cr_iip + 0);
                if (((w0 >> 1) & 0xf) == IA64_MLX_TEMPLATE) {
                        /*
                         * rfi'ing to slot 2 of an MLX bundle causes
@@ -174,7 +174,7 @@ ia64_decrement_ip (struct pt_regs *regs)
        if (ia64_psr(regs)->ri == 0) {
                regs->cr_iip -= 16;
                ri = 2;
-               get_user(w0, (char *) regs->cr_iip + 0);
+               get_user(w0, (char __user *) regs->cr_iip + 0);
                if (((w0 >> 1) & 0xf) == IA64_MLX_TEMPLATE) {
                        /*
                         * rfi'ing to slot 2 of an MLX bundle causes
@@ -304,7 +304,6 @@ put_rnat (struct task_struct *task, struct switch_stack *sw,
        long num_regs, nbits;
        struct pt_regs *pt;
        unsigned long cfm, *urbs_kargs;
-       struct unw_frame_info info;
 
        pt = ia64_task_regs(task);
        kbsp = (unsigned long *) sw->ar_bspstore;
@@ -316,11 +315,8 @@ put_rnat (struct task_struct *task, struct switch_stack *sw,
                 * If entered via syscall, don't allow user to set rnat bits
                 * for syscall args.
                 */
-               unw_init_from_blocked_task(&info,task);
-               if (unw_unwind_to_user(&info) == 0) {
-                       unw_get_cfm(&info,&cfm);
-                       urbs_kargs = ia64_rse_skip_regs(urbs_end,-(cfm & 0x7f));
-               }
+               cfm = pt->cr_ifs;
+               urbs_kargs = ia64_rse_skip_regs(urbs_end, -(cfm & 0x7f));
        }
 
        if (urbs_kargs >= urnat_addr)
@@ -480,27 +476,18 @@ ia64_poke (struct task_struct *child, struct switch_stack *child_stack, unsigned
 unsigned long
 ia64_get_user_rbs_end (struct task_struct *child, struct pt_regs *pt, unsigned long *cfmp)
 {
-       unsigned long *krbs, *bspstore, cfm;
-       struct unw_frame_info info;
+       unsigned long *krbs, *bspstore, cfm = pt->cr_ifs;
        long ndirty;
 
        krbs = (unsigned long *) child + IA64_RBS_OFFSET/8;
        bspstore = (unsigned long *) pt->ar_bspstore;
        ndirty = ia64_rse_num_regs(krbs, krbs + (pt->loadrs >> 19));
-       cfm = pt->cr_ifs & ~(1UL << 63);
 
-       if (in_syscall(pt)) {
-               /*
-                * If bit 63 of cr.ifs is cleared, the kernel was entered via a system
-                * call and we need to recover the CFM that existed on entry to the
-                * kernel by unwinding the kernel stack.
-                */
-               unw_init_from_blocked_task(&info, child);
-               if (unw_unwind_to_user(&info) == 0) {
-                       unw_get_cfm(&info, &cfm);
-                       ndirty += (cfm & 0x7f);
-               }
-       }
+       if (in_syscall(pt))
+               ndirty += (cfm & 0x7f);
+       else
+               cfm &= ~(1UL << 63);    /* clear valid bit */
+
        if (cfmp)
                *cfmp = cfm;
        return (unsigned long) ia64_rse_skip_regs(bspstore, ndirty);
@@ -833,7 +820,7 @@ access_uarea (struct task_struct *child, unsigned long addr, unsigned long *data
                      case PT_CFM:
                        urbs_end = ia64_get_user_rbs_end(child, pt, &cfm);
                        if (write_access) {
-                               if (((cfm ^ *data) & 0x3fffffffffU) != 0) {
+                               if (((cfm ^ *data) & 0x3fffffffffUL) != 0) {
                                        if (ia64_sync_user_rbs(child, sw,
                                                               pt->ar_bspstore, urbs_end) < 0)
                                                return -1;
@@ -997,12 +984,14 @@ access_uarea (struct task_struct *child, unsigned long addr, unsigned long *data
 }
 
 static long
-ptrace_getregs (struct task_struct *child, struct pt_all_user_regs *ppr)
+ptrace_getregs (struct task_struct *child, struct pt_all_user_regs __user *ppr)
 {
+       unsigned long psr, ec, lc, rnat, bsp, cfm, nat_bits, val;
+       struct unw_frame_info info;
+       struct ia64_fpreg fpval;
        struct switch_stack *sw;
        struct pt_regs *pt;
        long ret, retval;
-       struct unw_frame_info info;
        char nat = 0;
        int i;
 
@@ -1023,12 +1012,21 @@ ptrace_getregs (struct task_struct *child, struct pt_all_user_regs *ppr)
                return -EIO;
        }
 
+       if (access_uarea(child, PT_CR_IPSR, &psr, 0) < 0
+           || access_uarea(child, PT_AR_EC, &ec, 0) < 0
+           || access_uarea(child, PT_AR_LC, &lc, 0) < 0
+           || access_uarea(child, PT_AR_RNAT, &rnat, 0) < 0
+           || access_uarea(child, PT_AR_BSP, &bsp, 0) < 0
+           || access_uarea(child, PT_CFM, &cfm, 0)
+           || access_uarea(child, PT_NAT_BITS, &nat_bits, 0))
+               return -EIO;
+
        retval = 0;
 
        /* control regs */
 
        retval |= __put_user(pt->cr_iip, &ppr->cr_iip);
-       retval |= access_uarea(child, PT_CR_IPSR, &ppr->cr_ipsr, 0);
+       retval |= __put_user(psr, &ppr->cr_ipsr);
 
        /* app regs */
 
@@ -1039,11 +1037,11 @@ ptrace_getregs (struct task_struct *child, struct pt_all_user_regs *ppr)
        retval |= __put_user(pt->ar_ccv, &ppr->ar[PT_AUR_CCV]);
        retval |= __put_user(pt->ar_fpsr, &ppr->ar[PT_AUR_FPSR]);
 
-       retval |= access_uarea(child, PT_AR_EC, &ppr->ar[PT_AUR_EC], 0);
-       retval |= access_uarea(child, PT_AR_LC, &ppr->ar[PT_AUR_LC], 0);
-       retval |= access_uarea(child, PT_AR_RNAT, &ppr->ar[PT_AUR_RNAT], 0);
-       retval |= access_uarea(child, PT_AR_BSP, &ppr->ar[PT_AUR_BSP], 0);
-       retval |= access_uarea(child, PT_CFM, &ppr->cfm, 0);
+       retval |= __put_user(ec, &ppr->ar[PT_AUR_EC]);
+       retval |= __put_user(lc, &ppr->ar[PT_AUR_LC]);
+       retval |= __put_user(rnat, &ppr->ar[PT_AUR_RNAT]);
+       retval |= __put_user(bsp, &ppr->ar[PT_AUR_BSP]);
+       retval |= __put_user(cfm, &ppr->cfm);
 
        /* gr1-gr3 */
 
@@ -1053,7 +1051,9 @@ ptrace_getregs (struct task_struct *child, struct pt_all_user_regs *ppr)
        /* gr4-gr7 */
 
        for (i = 4; i < 8; i++) {
-               retval |= unw_access_gr(&info, i, &ppr->gr[i], &nat, 0);
+               if (unw_access_gr(&info, i, &val, &nat, 0) < 0)
+                       return -EIO;
+               retval |= __put_user(val, &ppr->gr[i]);
        }
 
        /* gr8-gr11 */
@@ -1077,7 +1077,9 @@ ptrace_getregs (struct task_struct *child, struct pt_all_user_regs *ppr)
        /* b1-b5 */
 
        for (i = 1; i < 6; i++) {
-               retval |= unw_access_br(&info, i, &ppr->br[i], 0);
+               if (unw_access_br(&info, i, &val, 0) < 0)
+                       return -EIO;
+               __put_user(val, &ppr->br[i]);
        }
 
        /* b6-b7 */
@@ -1088,8 +1090,9 @@ ptrace_getregs (struct task_struct *child, struct pt_all_user_regs *ppr)
        /* fr2-fr5 */
 
        for (i = 2; i < 6; i++) {
-               retval |= access_fr(&info, i, 0, (unsigned long *) &ppr->fr[i], 0);
-               retval |= access_fr(&info, i, 1, (unsigned long *) &ppr->fr[i] + 1, 0);
+               if (unw_get_fr(&info, i, &fpval) < 0)
+                       return -EIO;
+               retval |= __copy_to_user(&ppr->fr[i], &fpval, sizeof (fpval));
        }
 
        /* fr6-fr11 */
@@ -1103,8 +1106,9 @@ ptrace_getregs (struct task_struct *child, struct pt_all_user_regs *ppr)
        /* fr16-fr31 */
 
        for (i = 16; i < 32; i++) {
-               retval |= access_fr(&info, i, 0, (unsigned long *) &ppr->fr[i], 0);
-               retval |= access_fr(&info, i, 1, (unsigned long *) &ppr->fr[i] + 1, 0);
+               if (unw_get_fr(&info, i, &fpval) < 0)
+                       return -EIO;
+               retval |= __copy_to_user(&ppr->fr[i], &fpval, sizeof (fpval));
        }
 
        /* fph */
@@ -1118,22 +1122,25 @@ ptrace_getregs (struct task_struct *child, struct pt_all_user_regs *ppr)
 
        /* nat bits */
 
-       retval |= access_uarea(child, PT_NAT_BITS, &ppr->nat, 0);
+       retval |= __put_user(nat_bits, &ppr->nat);
 
        ret = retval ? -EIO : 0;
        return ret;
 }
 
 static long
-ptrace_setregs (struct task_struct *child, struct pt_all_user_regs *ppr)
+ptrace_setregs (struct task_struct *child, struct pt_all_user_regs __user *ppr)
 {
+       unsigned long psr, ec, lc, rnat, bsp, cfm, nat_bits, val = 0;
+       struct unw_frame_info info;
        struct switch_stack *sw;
+       struct ia64_fpreg fpval;
        struct pt_regs *pt;
        long ret, retval;
-       struct unw_frame_info info;
-       char nat = 0;
        int i;
 
+       memset(&fpval, 0, sizeof(fpval));
+
        retval = verify_area(VERIFY_READ, ppr, sizeof(struct pt_all_user_regs));
        if (retval != 0) {
                return -EIO;
@@ -1156,7 +1163,7 @@ ptrace_setregs (struct task_struct *child, struct pt_all_user_regs *ppr)
        /* control regs */
 
        retval |= __get_user(pt->cr_iip, &ppr->cr_iip);
-       retval |= access_uarea(child, PT_CR_IPSR, &ppr->cr_ipsr, 1);
+       retval |= __get_user(psr, &ppr->cr_ipsr);
 
        /* app regs */
 
@@ -1167,11 +1174,11 @@ ptrace_setregs (struct task_struct *child, struct pt_all_user_regs *ppr)
        retval |= __get_user(pt->ar_ccv, &ppr->ar[PT_AUR_CCV]);
        retval |= __get_user(pt->ar_fpsr, &ppr->ar[PT_AUR_FPSR]);
 
-       retval |= access_uarea(child, PT_AR_EC, &ppr->ar[PT_AUR_EC], 1);
-       retval |= access_uarea(child, PT_AR_LC, &ppr->ar[PT_AUR_LC], 1);
-       retval |= access_uarea(child, PT_AR_RNAT, &ppr->ar[PT_AUR_RNAT], 1);
-       retval |= access_uarea(child, PT_AR_BSP, &ppr->ar[PT_AUR_BSP], 1);
-       retval |= access_uarea(child, PT_CFM, &ppr->cfm, 1);
+       retval |= __get_user(ec, &ppr->ar[PT_AUR_EC]);
+       retval |= __get_user(lc, &ppr->ar[PT_AUR_LC]);
+       retval |= __get_user(rnat, &ppr->ar[PT_AUR_RNAT]);
+       retval |= __get_user(bsp, &ppr->ar[PT_AUR_BSP]);
+       retval |= __get_user(cfm, &ppr->cfm);
 
        /* gr1-gr3 */
 
@@ -1181,11 +1188,9 @@ ptrace_setregs (struct task_struct *child, struct pt_all_user_regs *ppr)
        /* gr4-gr7 */
 
        for (i = 4; i < 8; i++) {
-               long ret = unw_get_gr(&info, i, &ppr->gr[i], &nat);
-               if (ret < 0) {
-                       return ret;
-               }
-               retval |= unw_access_gr(&info, i, &ppr->gr[i], &nat, 1);
+               retval |= __get_user(val, &ppr->gr[i]);
+               if (unw_set_gr(&info, i, val, 0) < 0)    /* NaT bit will be set via PT_NAT_BITS */
+                       return -EIO;
        }
 
        /* gr8-gr11 */
@@ -1209,7 +1214,8 @@ ptrace_setregs (struct task_struct *child, struct pt_all_user_regs *ppr)
        /* b1-b5 */
 
        for (i = 1; i < 6; i++) {
-               retval |= unw_access_br(&info, i, &ppr->br[i], 1);
+               retval |= __get_user(val, &ppr->br[i]);
+               unw_set_br(&info, i, val);
        }
 
        /* b6-b7 */
@@ -1220,8 +1226,9 @@ ptrace_setregs (struct task_struct *child, struct pt_all_user_regs *ppr)
        /* fr2-fr5 */
 
        for (i = 2; i < 6; i++) {
-               retval |= access_fr(&info, i, 0, (unsigned long *) &ppr->fr[i], 1);
-               retval |= access_fr(&info, i, 1, (unsigned long *) &ppr->fr[i] + 1, 1);
+               retval |= __copy_from_user(&fpval, &ppr->fr[i], sizeof(fpval));
+               if (unw_set_fr(&info, i, fpval) < 0)
+                       return -EIO;
        }
 
        /* fr6-fr11 */
@@ -1235,8 +1242,9 @@ ptrace_setregs (struct task_struct *child, struct pt_all_user_regs *ppr)
        /* fr16-fr31 */
 
        for (i = 16; i < 32; i++) {
-               retval |= access_fr(&info, i, 0, (unsigned long *) &ppr->fr[i], 1);
-               retval |= access_fr(&info, i, 1, (unsigned long *) &ppr->fr[i] + 1, 1);
+               retval |= __copy_from_user(&fpval, &ppr->fr[i], sizeof(fpval));
+               if (unw_set_fr(&info, i, fpval) < 0)
+                       return -EIO;
        }
 
        /* fph */
@@ -1250,7 +1258,15 @@ ptrace_setregs (struct task_struct *child, struct pt_all_user_regs *ppr)
 
        /* nat bits */
 
-       retval |= access_uarea(child, PT_NAT_BITS, &ppr->nat, 1);
+       retval |= __get_user(nat_bits, &ppr->nat);
+
+       retval |= access_uarea(child, PT_CR_IPSR, &psr, 1);
+       retval |= access_uarea(child, PT_AR_EC, &ec, 1);
+       retval |= access_uarea(child, PT_AR_LC, &lc, 1);
+       retval |= access_uarea(child, PT_AR_RNAT, &rnat, 1);
+       retval |= access_uarea(child, PT_AR_BSP, &bsp, 1);
+       retval |= access_uarea(child, PT_CFM, &cfm, 1);
+       retval |= access_uarea(child, PT_NAT_BITS, &nat_bits, 1);
 
        ret = retval ? -EIO : 0;
        return ret;
@@ -1393,7 +1409,7 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data,
                 * sigkill.  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 */
                        goto out_tsk;
                child->exit_code = SIGKILL;
 
@@ -1429,11 +1445,11 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data,
                goto out_tsk;
 
              case PTRACE_GETREGS:
-               ret = ptrace_getregs(child, (struct pt_all_user_regs*) data);
+               ret = ptrace_getregs(child, (struct pt_all_user_regs __user *) data);
                goto out_tsk;
 
              case PTRACE_SETREGS:
-               ret = ptrace_setregs(child, (struct pt_all_user_regs*) data);
+               ret = ptrace_setregs(child, (struct pt_all_user_regs __user *) data);
                goto out_tsk;
 
              default:
@@ -1447,9 +1463,8 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data,
        return ret;
 }
 
-/* "asmlinkage" so the input arguments are preserved... */
 
-asmlinkage void
+void
 syscall_trace (void)
 {
        if (!test_thread_flag(TIF_SYSCALL_TRACE))
@@ -1472,3 +1487,38 @@ syscall_trace (void)
                current->exit_code = 0;
        }
 }
+
+/* "asmlinkage" so the input arguments are preserved... */
+
+asmlinkage void
+syscall_trace_enter (long arg0, long arg1, long arg2, long arg3,
+                    long arg4, long arg5, long arg6, long arg7, long stack)
+{
+       struct pt_regs *regs = (struct pt_regs *) &stack;
+       long syscall;
+
+       if (unlikely(current->audit_context)) {
+               if (IS_IA32_PROCESS(regs))
+                       syscall = regs->r1;
+               else
+                       syscall = regs->r15;
+
+               audit_syscall_entry(current, syscall, arg0, arg1, arg2, arg3);
+       }
+
+       if (test_thread_flag(TIF_SYSCALL_TRACE) && (current->ptrace & PT_PTRACED))
+               syscall_trace();
+}
+
+/* "asmlinkage" so the input arguments are preserved... */
+
+asmlinkage void
+syscall_trace_leave (long arg0, long arg1, long arg2, long arg3,
+                    long arg4, long arg5, long arg6, long arg7, long stack)
+{
+       if (unlikely(current->audit_context))
+               audit_syscall_exit(current, ((struct pt_regs *) &stack)->r8);
+
+       if (test_thread_flag(TIF_SYSCALL_TRACE) && (current->ptrace & PT_PTRACED))
+               syscall_trace();
+}