Merge refs/heads/upstream from master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
[linux-2.6-block.git] / arch / ia64 / kernel / ptrace.c
index 907464ee7273221468afa4c59cc811042ed2fd11..bbb8bc7c0552cdb860a466df3e302698b35da58c 100644 (file)
@@ -635,11 +635,17 @@ ia64_flush_fph (struct task_struct *task)
 {
        struct ia64_psr *psr = ia64_psr(ia64_task_regs(task));
 
+       /*
+        * Prevent migrating this task while
+        * we're fiddling with the FPU state
+        */
+       preempt_disable();
        if (ia64_is_local_fpu_owner(task) && psr->mfh) {
                psr->mfh = 0;
                task->thread.flags |= IA64_THREAD_FPH_VALID;
                ia64_save_fpu(&task->thread.fph[0]);
        }
+       preempt_enable();
 }
 
 /*
@@ -692,25 +698,59 @@ convert_to_non_syscall (struct task_struct *child, struct pt_regs  *pt,
                        unsigned long cfm)
 {
        struct unw_frame_info info, prev_info;
-       unsigned long ip, pr;
+       unsigned long ip, sp, pr;
 
        unw_init_from_blocked_task(&info, child);
        while (1) {
                prev_info = info;
                if (unw_unwind(&info) < 0)
                        return;
-               if (unw_get_rp(&info, &ip) < 0)
+
+               unw_get_sp(&info, &sp);
+               if ((long)((unsigned long)child + IA64_STK_OFFSET - sp)
+                   < IA64_PT_REGS_SIZE) {
+                       dprintk("ptrace.%s: ran off the top of the kernel "
+                               "stack\n", __FUNCTION__);
+                       return;
+               }
+               if (unw_get_pr (&prev_info, &pr) < 0) {
+                       unw_get_rp(&prev_info, &ip);
+                       dprintk("ptrace.%s: failed to read "
+                               "predicate register (ip=0x%lx)\n",
+                               __FUNCTION__, ip);
                        return;
-               if (ip < FIXADDR_USER_END)
+               }
+               if (unw_is_intr_frame(&info)
+                   && (pr & (1UL << PRED_USER_STACK)))
                        break;
        }
 
+       /*
+        * Note: at the time of this call, the target task is blocked
+        * in notify_resume_user() and by clearling PRED_LEAVE_SYSCALL
+        * (aka, "pLvSys") we redirect execution from
+        * .work_pending_syscall_end to .work_processed_kernel.
+        */
        unw_get_pr(&prev_info, &pr);
-       pr &= ~(1UL << PRED_SYSCALL);
+       pr &= ~((1UL << PRED_SYSCALL) | (1UL << PRED_LEAVE_SYSCALL));
        pr |=  (1UL << PRED_NON_SYSCALL);
        unw_set_pr(&prev_info, pr);
 
        pt->cr_ifs = (1UL << 63) | cfm;
+       /*
+        * Clear the memory that is NOT written on syscall-entry to
+        * ensure we do not leak kernel-state to user when execution
+        * resumes.
+        */
+       pt->r2 = 0;
+       pt->r3 = 0;
+       pt->r14 = 0;
+       memset(&pt->r16, 0, 16*8);      /* clear r16-r31 */
+       memset(&pt->f6, 0, 6*16);       /* clear f6-f11 */
+       pt->b7 = 0;
+       pt->ar_ccv = 0;
+       pt->ar_csd = 0;
+       pt->ar_ssd = 0;
 }
 
 static int
@@ -925,6 +965,13 @@ access_uarea (struct task_struct *child, unsigned long addr,
                                *data = (pt->cr_ipsr & IPSR_MASK);
                        return 0;
 
+                     case PT_AR_RSC:
+                       if (write_access)
+                               pt->ar_rsc = *data | (3 << 2); /* force PL3 */
+                       else
+                               *data = pt->ar_rsc;
+                       return 0;
+
                      case PT_AR_RNAT:
                        urbs_end = ia64_get_user_rbs_end(child, pt, NULL);
                        rnat_addr = (long) ia64_rse_rnat_addr((long *)
@@ -976,9 +1023,6 @@ access_uarea (struct task_struct *child, unsigned long addr,
                      case PT_AR_BSPSTORE:
                        ptr = pt_reg_addr(pt, ar_bspstore);
                        break;
-                     case PT_AR_RSC:
-                       ptr = pt_reg_addr(pt, ar_rsc);
-                       break;
                      case PT_AR_UNAT:
                        ptr = pt_reg_addr(pt, ar_unat);
                        break;
@@ -1214,7 +1258,7 @@ ptrace_getregs (struct task_struct *child, struct pt_all_user_regs __user *ppr)
 static long
 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;
+       unsigned long psr, rsc, ec, lc, rnat, bsp, cfm, nat_bits, val = 0;
        struct unw_frame_info info;
        struct switch_stack *sw;
        struct ia64_fpreg fpval;
@@ -1247,7 +1291,7 @@ ptrace_setregs (struct task_struct *child, struct pt_all_user_regs __user *ppr)
        /* app regs */
 
        retval |= __get_user(pt->ar_pfs, &ppr->ar[PT_AUR_PFS]);
-       retval |= __get_user(pt->ar_rsc, &ppr->ar[PT_AUR_RSC]);
+       retval |= __get_user(rsc, &ppr->ar[PT_AUR_RSC]);
        retval |= __get_user(pt->ar_bspstore, &ppr->ar[PT_AUR_BSPSTORE]);
        retval |= __get_user(pt->ar_unat, &ppr->ar[PT_AUR_UNAT]);
        retval |= __get_user(pt->ar_ccv, &ppr->ar[PT_AUR_CCV]);
@@ -1345,6 +1389,7 @@ ptrace_setregs (struct task_struct *child, struct pt_all_user_regs __user *ppr)
        retval |= __get_user(nat_bits, &ppr->nat);
 
        retval |= access_uarea(child, PT_CR_IPSR, &psr, 1);
+       retval |= access_uarea(child, PT_AR_RSC, &rsc, 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);