Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 1519

kernel-2.6.18-238.el5.src.rpm

From: Roland McGrath <roland@redhat.com>
Date: Tue, 18 Dec 2007 15:51:01 -0800
Subject: [ia64] ptrace: access to user register backing
Message-id: 20071218235101.5A6AB26F8C2@magilla.localdomain
O-Subject: [RHEL 5.2 PATCH] BZ#237749 ia64-restore-rse
Bugzilla: 237749

linux-2.6-ia64-restore-rse.patch

This fixes the problems with ptrace (or /proc) access to user register backing
store memory corresponding to registers used by the kernel (syscall args, etc).

diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c
index ea914cc..634b385 100644
--- a/arch/ia64/kernel/process.c
+++ b/arch/ia64/kernel/process.c
@@ -166,6 +166,7 @@ do_notify_resume_user (sigset_t *oldset, struct sigscratch *scr, long in_syscall
 		return;
 	}
 
+	clear_thread_flag(TIF_NOTIFY_RESUME);
 #ifdef CONFIG_PERFMON
 	if (current->thread.pfm_needs_checking)
 		pfm_handle_work();
@@ -174,6 +175,10 @@ do_notify_resume_user (sigset_t *oldset, struct sigscratch *scr, long in_syscall
 	/* deal with pending signal delivery */
 	if (test_thread_flag(TIF_SIGPENDING))
 		ia64_do_signal(oldset, scr, in_syscall);
+
+	/* copy user rbs to kernel rbs */
+	if (unlikely(test_thread_flag(TIF_RESTORE_RSE)))
+		ia64_sync_krbs();
 }
 
 static int pal_halt        = 1;
diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c
index bb2bcef..7f8334f 100644
--- a/arch/ia64/kernel/ptrace.c
+++ b/arch/ia64/kernel/ptrace.c
@@ -554,6 +554,78 @@ ia64_sync_user_rbs (struct task_struct *child, struct switch_stack *sw,
 	return 0;
 }
 
+static long
+ia64_sync_kernel_rbs (struct task_struct *child, struct switch_stack *sw,
+		unsigned long user_rbs_start, unsigned long user_rbs_end)
+{
+	unsigned long addr, val;
+	long ret;
+
+	/* now copy word for word from user rbs to kernel rbs: */
+	for (addr = user_rbs_start; addr < user_rbs_end; addr += 8) {
+		if (access_process_vm(child, addr, &val, sizeof(val), 0)
+				!= sizeof(val))
+			return -EIO;
+
+		ret = ia64_poke(child, sw, user_rbs_end, addr, val);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+typedef long (*syncfunc_t)(struct task_struct *, struct switch_stack *,
+			    unsigned long, unsigned long);
+
+static void do_sync_rbs(struct unw_frame_info *info, void *arg)
+{
+	struct pt_regs *pt;
+	unsigned long urbs_end;
+	syncfunc_t fn = arg;
+
+	if (unw_unwind_to_user(info) < 0)
+		return;
+	pt = task_pt_regs(info->task);
+	urbs_end = ia64_get_user_rbs_end(info->task, pt, NULL);
+
+	fn(info->task, info->sw, pt->ar_bspstore, urbs_end);
+}
+
+/*
+ * 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. TIF_RESTORE_RSE is the flag to indicate we need
+ * synchronize user RSE to kernel.
+ */
+static int gpregs_writeback(struct task_struct *tsk,
+			    const struct utrace_regset *regset,
+			    int now)
+{
+	if (test_and_set_tsk_thread_flag(tsk, TIF_RESTORE_RSE))
+		return 0;
+	set_tsk_thread_flag(tsk, TIF_NOTIFY_RESUME);
+	unw_init_running(do_sync_rbs, ia64_sync_user_rbs);
+	return 0;		/* Ignore EFAULT.  */
+}
+
+/*
+ * This is called to read back the register backing store.
+ */
+void ia64_sync_krbs(void)
+{
+	clear_tsk_thread_flag(current, TIF_RESTORE_RSE);
+#ifdef CONFIG_PERFMON
+	if (!current->thread.pfm_needs_checking)
+		clear_thread_flag(TIF_NOTIFY_RESUME);
+#endif
+
+	unw_init_running(do_sync_rbs, ia64_sync_kernel_rbs);
+}
+
 /*
  * Write f32-f127 back to task->thread.fph if it has been modified.
  */
@@ -728,6 +800,10 @@ syscall_trace_enter (long arg0, long arg1, long arg2, long arg3,
 	if (test_thread_flag(TIF_SYSCALL_TRACE))
 		tracehook_report_syscall(&regs, 0);
 
+	/* copy user rbs to kernel rbs */
+	if (test_thread_flag(TIF_RESTORE_RSE))
+		ia64_sync_krbs();
+
 	if (unlikely(current->audit_context)) {
 		long syscall;
 		int arch;
@@ -768,6 +844,10 @@ syscall_trace_leave (long arg0, long arg1, long arg2, long arg3,
 		force_sig(SIGTRAP, current); /* XXX */
 		tracehook_report_syscall_step(&regs);
 	}
+
+	/* copy user rbs to kernel rbs */
+	if (test_thread_flag(TIF_RESTORE_RSE))
+		ia64_sync_krbs();
 }
 
 
@@ -1398,31 +1478,6 @@ static int gpregs_set(struct task_struct *target,
 	return do_regset_call(do_gpregs_set, target, regset, pos, count, kbuf, ubuf);
 }
 
-static void do_gpregs_writeback(struct unw_frame_info *info, void *arg)
-{
-	struct pt_regs *pt;
-	utrace_getset_t *dst = arg;
-	unsigned long urbs_end;
-
-	if (unw_unwind_to_user(info) < 0)
-		return;
-	pt = task_pt_regs(dst->target);
-	urbs_end = ia64_get_user_rbs_end(dst->target, pt, NULL);
-	dst->ret = ia64_sync_user_rbs(dst->target, info->sw, pt->ar_bspstore, urbs_end);
-}
-/*
- * This is called to write back the register backing store.
- * ptrace does this before it stops, so that a tracer reading the user
- * memory after the thread stops will get the current register data.
- */
-static int
-gpregs_writeback(struct task_struct *target,
-		 const struct utrace_regset *regset,
-		 int now)
-{
-	return do_regset_call(do_gpregs_writeback, target, regset, 0, 0, NULL, NULL);
-}
-
 static int
 fpregs_active(struct task_struct *target, const struct utrace_regset *regset)
 {
diff --git a/include/asm-ia64/ptrace.h b/include/asm-ia64/ptrace.h
index f4ef87a..13435f7 100644
--- a/include/asm-ia64/ptrace.h
+++ b/include/asm-ia64/ptrace.h
@@ -292,6 +292,7 @@ struct switch_stack {
 			 unsigned long, long);
   extern void ia64_flush_fph (struct task_struct *);
   extern void ia64_sync_fph (struct task_struct *);
+  extern void ia64_sync_krbs(void);
   extern long ia64_sync_user_rbs (struct task_struct *, struct switch_stack *,
 				  unsigned long, unsigned long);
 
@@ -303,6 +304,12 @@ struct switch_stack {
   extern void ia64_increment_ip (struct pt_regs *pt);
   extern void ia64_decrement_ip (struct pt_regs *pt);
 
+  extern void ia64_ptrace_stop(void);
+  #define arch_ptrace_stop(code, info) \
+	ia64_ptrace_stop()
+  #define arch_ptrace_stop_needed(code, info) \
+	(!test_thread_flag(TIF_RESTORE_RSE))
+
 #endif /* !__KERNEL__ */
 
 /* pt_all_user_regs is used for PTRACE_GETREGS PTRACE_SETREGS */
diff --git a/include/asm-ia64/thread_info.h b/include/asm-ia64/thread_info.h
index d324d4c..ebca7a1 100644
--- a/include/asm-ia64/thread_info.h
+++ b/include/asm-ia64/thread_info.h
@@ -89,6 +89,7 @@ struct thread_info {
 #define TIF_MEMDIE		17
 #define TIF_MCA_INIT		18	/* this task is processing MCA or INIT */
 #define TIF_DB_DISABLED		19	/* debug trap disabled for fsyscall */
+#define TIF_RESTORE_RSE		21	/* user RBS is newer than kernel RBS */
 
 #define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE)
 #define _TIF_SYSCALL_AUDIT	(1 << TIF_SYSCALL_AUDIT)
@@ -100,6 +101,7 @@ struct thread_info {
 #define _TIF_POLLING_NRFLAG	(1 << TIF_POLLING_NRFLAG)
 #define _TIF_MCA_INIT		(1 << TIF_MCA_INIT)
 #define _TIF_DB_DISABLED	(1 << TIF_DB_DISABLED)
+#define _TIF_RESTORE_RSE	(1 << TIF_RESTORE_RSE)
 
 /* "work to do on user-return" bits */
 #define TIF_ALLWORK_MASK	(_TIF_NOTIFY_RESUME|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT)