aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorPetr Tesarik <ptesarik@suse.cz>2007-12-12 09:23:34 -0500
committerTony Luck <tony.luck@intel.com>2008-02-08 15:01:18 -0500
commit3b2ce0b17824c42bc2e46f7dd903b4acf5e9fff9 (patch)
treeacf3913f8c0a479615060ffed4cec9283047761c /arch
parent5aa92ffda1b6244b4a248df0b95c07d183ab96d2 (diff)
[IA64] Synchronize kernel RSE to user-space and back
This is base kernel patch for ptrace RSE bug. It's basically a backport from the utrace RSE patch I sent out several weeks ago. please review. when a thread is stopped (ptraced), debugger might change thread's user stack (change memory directly), and we must avoid the RSE stored in kernel to override user stack (user space's RSE is newer than kernel's in the case). To workaround the issue, we copy kernel RSE to user RSE before the task is stopped, so user RSE has updated data. we then copy user RSE to kernel after the task is resummed from traced stop and kernel will use the newer RSE to return to user. Signed-off-by: Shaohua Li <shaohua.li@intel.com> Signed-off-by: Petr Tesarik <ptesarik@suse.cz> CC: Roland McGrath <roland@redhat.com> Signed-off-by: Tony Luck <tony.luck@intel.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/ia64/kernel/process.c6
-rw-r--r--arch/ia64/kernel/ptrace.c82
2 files changed, 88 insertions, 0 deletions
diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c
index 5c9efe626563..be6c6f7be027 100644
--- a/arch/ia64/kernel/process.c
+++ b/arch/ia64/kernel/process.c
@@ -163,6 +163,8 @@ void tsk_clear_notify_resume(struct task_struct *tsk)
163 if (tsk->thread.pfm_needs_checking) 163 if (tsk->thread.pfm_needs_checking)
164 return; 164 return;
165#endif 165#endif
166 if (test_ti_thread_flag(task_thread_info(tsk), TIF_RESTORE_RSE))
167 return;
166 clear_ti_thread_flag(task_thread_info(tsk), TIF_NOTIFY_RESUME); 168 clear_ti_thread_flag(task_thread_info(tsk), TIF_NOTIFY_RESUME);
167} 169}
168 170
@@ -184,6 +186,10 @@ do_notify_resume_user (sigset_t *unused, struct sigscratch *scr, long in_syscall
184 /* deal with pending signal delivery */ 186 /* deal with pending signal delivery */
185 if (test_thread_flag(TIF_SIGPENDING)||test_thread_flag(TIF_RESTORE_SIGMASK)) 187 if (test_thread_flag(TIF_SIGPENDING)||test_thread_flag(TIF_RESTORE_SIGMASK))
186 ia64_do_signal(scr, in_syscall); 188 ia64_do_signal(scr, in_syscall);
189
190 /* copy user rbs to kernel rbs */
191 if (unlikely(test_thread_flag(TIF_RESTORE_RSE)))
192 ia64_sync_krbs();
187} 193}
188 194
189static int pal_halt = 1; 195static int pal_halt = 1;
diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c
index 2e96f17b2f3b..2de5a524a0ee 100644
--- a/arch/ia64/kernel/ptrace.c
+++ b/arch/ia64/kernel/ptrace.c
@@ -547,6 +547,72 @@ ia64_sync_user_rbs (struct task_struct *child, struct switch_stack *sw,
547 return 0; 547 return 0;
548} 548}
549 549
550static long
551ia64_sync_kernel_rbs (struct task_struct *child, struct switch_stack *sw,
552 unsigned long user_rbs_start, unsigned long user_rbs_end)
553{
554 unsigned long addr, val;
555 long ret;
556
557 /* now copy word for word from user rbs to kernel rbs: */
558 for (addr = user_rbs_start; addr < user_rbs_end; addr += 8) {
559 if (access_process_vm(child, addr, &val, sizeof(val), 0)
560 != sizeof(val))
561 return -EIO;
562
563 ret = ia64_poke(child, sw, user_rbs_end, addr, val);
564 if (ret < 0)
565 return ret;
566 }
567 return 0;
568}
569
570typedef long (*syncfunc_t)(struct task_struct *, struct switch_stack *,
571 unsigned long, unsigned long);
572
573static void do_sync_rbs(struct unw_frame_info *info, void *arg)
574{
575 struct pt_regs *pt;
576 unsigned long urbs_end;
577 syncfunc_t fn = arg;
578
579 if (unw_unwind_to_user(info) < 0)
580 return;
581 pt = task_pt_regs(info->task);
582 urbs_end = ia64_get_user_rbs_end(info->task, pt, NULL);
583
584 fn(info->task, info->sw, pt->ar_bspstore, urbs_end);
585}
586
587/*
588 * when a thread is stopped (ptraced), debugger might change thread's user
589 * stack (change memory directly), and we must avoid the RSE stored in kernel
590 * to override user stack (user space's RSE is newer than kernel's in the
591 * case). To workaround the issue, we copy kernel RSE to user RSE before the
592 * task is stopped, so user RSE has updated data. we then copy user RSE to
593 * kernel after the task is resummed from traced stop and kernel will use the
594 * newer RSE to return to user. TIF_RESTORE_RSE is the flag to indicate we need
595 * synchronize user RSE to kernel.
596 */
597void ia64_ptrace_stop(void)
598{
599 if (test_and_set_tsk_thread_flag(current, TIF_RESTORE_RSE))
600 return;
601 tsk_set_notify_resume(current);
602 unw_init_running(do_sync_rbs, ia64_sync_user_rbs);
603}
604
605/*
606 * This is called to read back the register backing store.
607 */
608void ia64_sync_krbs(void)
609{
610 clear_tsk_thread_flag(current, TIF_RESTORE_RSE);
611 tsk_clear_notify_resume(current);
612
613 unw_init_running(do_sync_rbs, ia64_sync_kernel_rbs);
614}
615
550static inline int 616static inline int
551thread_matches (struct task_struct *thread, unsigned long addr) 617thread_matches (struct task_struct *thread, unsigned long addr)
552{ 618{
@@ -1422,6 +1488,7 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data)
1422 struct task_struct *child; 1488 struct task_struct *child;
1423 struct switch_stack *sw; 1489 struct switch_stack *sw;
1424 long ret; 1490 long ret;
1491 struct unw_frame_info info;
1425 1492
1426 lock_kernel(); 1493 lock_kernel();
1427 ret = -EPERM; 1494 ret = -EPERM;
@@ -1453,6 +1520,8 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data)
1453 1520
1454 if (request == PTRACE_ATTACH) { 1521 if (request == PTRACE_ATTACH) {
1455 ret = ptrace_attach(child); 1522 ret = ptrace_attach(child);
1523 if (!ret)
1524 arch_ptrace_attach(child);
1456 goto out_tsk; 1525 goto out_tsk;
1457 } 1526 }
1458 1527
@@ -1481,6 +1550,11 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data)
1481 /* write the word at location addr */ 1550 /* write the word at location addr */
1482 urbs_end = ia64_get_user_rbs_end(child, pt, NULL); 1551 urbs_end = ia64_get_user_rbs_end(child, pt, NULL);
1483 ret = ia64_poke(child, sw, urbs_end, addr, data); 1552 ret = ia64_poke(child, sw, urbs_end, addr, data);
1553
1554 /* Make sure user RBS has the latest data */
1555 unw_init_from_blocked_task(&info, child);
1556 do_sync_rbs(&info, ia64_sync_user_rbs);
1557
1484 goto out_tsk; 1558 goto out_tsk;
1485 1559
1486 case PTRACE_PEEKUSR: 1560 case PTRACE_PEEKUSR:
@@ -1634,6 +1708,10 @@ syscall_trace_enter (long arg0, long arg1, long arg2, long arg3,
1634 && (current->ptrace & PT_PTRACED)) 1708 && (current->ptrace & PT_PTRACED))
1635 syscall_trace(); 1709 syscall_trace();
1636 1710
1711 /* copy user rbs to kernel rbs */
1712 if (test_thread_flag(TIF_RESTORE_RSE))
1713 ia64_sync_krbs();
1714
1637 if (unlikely(current->audit_context)) { 1715 if (unlikely(current->audit_context)) {
1638 long syscall; 1716 long syscall;
1639 int arch; 1717 int arch;
@@ -1671,4 +1749,8 @@ syscall_trace_leave (long arg0, long arg1, long arg2, long arg3,
1671 || test_thread_flag(TIF_SINGLESTEP)) 1749 || test_thread_flag(TIF_SINGLESTEP))
1672 && (current->ptrace & PT_PTRACED)) 1750 && (current->ptrace & PT_PTRACED))
1673 syscall_trace(); 1751 syscall_trace();
1752
1753 /* copy user rbs to kernel rbs */
1754 if (test_thread_flag(TIF_RESTORE_RSE))
1755 ia64_sync_krbs();
1674} 1756}