rpms/kernel/devel kernel.spec, 1.1145, 1.1146 linux-2.6-utrace.patch, 1.103, 1.104

Kyle McMartin kyle at fedoraproject.org
Thu Dec 4 18:34:59 UTC 2008


Author: kyle

Update of /cvs/pkgs/rpms/kernel/devel
In directory cvs1.fedora.phx.redhat.com:/tmp/cvs-serv10411

Modified Files:
	kernel.spec linux-2.6-utrace.patch 
Log Message:
* Thu Dec 04 2008 Kyle McMartin <kyle at redhat.com>
- linux-2.6-utrace.patch updates



Index: kernel.spec
===================================================================
RCS file: /cvs/pkgs/rpms/kernel/devel/kernel.spec,v
retrieving revision 1.1145
retrieving revision 1.1146
diff -u -r1.1145 -r1.1146
--- kernel.spec	4 Dec 2008 18:21:00 -0000	1.1145
+++ kernel.spec	4 Dec 2008 18:34:27 -0000	1.1146
@@ -1920,6 +1920,9 @@
 
 %changelog
 * Thu Dec 04 2008 Kyle McMartin <kyle at redhat.com>
+- linux-2.6-utrace.patch updates
+
+* Thu Dec 04 2008 Kyle McMartin <kyle at redhat.com>
 - 2.6.28-rc7-git3
 
 * Tue Dec 02 2008 Dave Jones <davej at redhat.com>

linux-2.6-utrace.patch:

Index: linux-2.6-utrace.patch
===================================================================
RCS file: /cvs/pkgs/rpms/kernel/devel/linux-2.6-utrace.patch,v
retrieving revision 1.103
retrieving revision 1.104
diff -u -r1.103 -r1.104
--- linux-2.6-utrace.patch	14 Nov 2008 21:50:55 -0000	1.103
+++ linux-2.6-utrace.patch	4 Dec 2008 18:34:27 -0000	1.104
@@ -7,10 +7,10 @@
  include/linux/utrace.h            |  707 ++++++++++
  init/Kconfig                      |   26 +
  kernel/Makefile                   |    1 +
- kernel/ptrace.c                   |  612 +++++++++-
+ kernel/ptrace.c                   |  793 +++++++++++-
  kernel/signal.c                   |   14 +-
- kernel/utrace.c                   | 2586 +++++++++++++++++++++++++++++++++++++
- 12 files changed, 4605 insertions(+), 9 deletions(-)
+ kernel/utrace.c                   | 2587 +++++++++++++++++++++++++++++++++++++
+ 12 files changed, 4783 insertions(+), 13 deletions(-)
 
 diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile
 index 9b1f6ca..e39334b 100644  
@@ -662,10 +662,10 @@
   * ptrace_init_task - initialize ptrace state for a new child
   * @child:		new child task
 diff --git a/include/linux/sched.h b/include/linux/sched.h
-index 644ffbd..97f772d 100644  
+index 55e30d1..b82b5f4 100644  
 --- a/include/linux/sched.h
 +++ b/include/linux/sched.h
-@@ -1239,6 +1239,11 @@ struct task_struct {
+@@ -1243,6 +1243,11 @@ struct task_struct {
  #endif
  	seccomp_t seccomp;
  
@@ -677,7 +677,7 @@
  /* Thread group tracking */
     	u32 parent_exec_id;
     	u32 self_exec_id;
-@@ -1825,6 +1830,7 @@ extern int kill_pgrp(struct pid *pid, in
+@@ -1829,6 +1834,7 @@ extern int kill_pgrp(struct pid *pid, in
  extern int kill_pid(struct pid *pid, int sig, int priv);
  extern int kill_proc_info(int, struct siginfo *, pid_t);
  extern int do_notify_parent(struct task_struct *, int);
@@ -1632,7 +1632,7 @@
 +
 +#endif	/* linux/utrace.h */
 diff --git a/init/Kconfig b/init/Kconfig
-index 86b00c5..f84117e 100644  
+index f763762..df75f9c 100644  
 --- a/init/Kconfig
 +++ b/init/Kconfig
 @@ -923,6 +923,32 @@ config STOP_MACHINE
@@ -1669,10 +1669,10 @@
  
  config PREEMPT_NOTIFIERS
 diff --git a/kernel/Makefile b/kernel/Makefile
-index 9a3ec66..1f14166 100644  
+index 19fad00..16a30b0 100644  
 --- a/kernel/Makefile
 +++ b/kernel/Makefile
-@@ -66,6 +66,7 @@ obj-$(CONFIG_IKCONFIG) += configs.o
+@@ -64,6 +64,7 @@ obj-$(CONFIG_IKCONFIG) += configs.o
  obj-$(CONFIG_RESOURCE_COUNTERS) += res_counter.o
  obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
  obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
@@ -1681,7 +1681,7 @@
  obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
  obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
 diff --git a/kernel/ptrace.c b/kernel/ptrace.c
-index 1e68e4c..516520a 100644  
+index 4c8bcd7..8d19d04 100644  
 --- a/kernel/ptrace.c
 +++ b/kernel/ptrace.c
 @@ -16,6 +16,7 @@
@@ -1700,7 +1700,7 @@
  /*
   * Turn a tracing stop into a normal stop now, since with no tracer there
   * would be no way to wake it up with SIGCONT or SIGKILL.  If there was a
-@@ -58,6 +60,55 @@ static void ptrace_untrace(struct task_s
+@@ -58,6 +60,72 @@ static void ptrace_untrace(struct task_s
  	spin_unlock(&child->sighand->siglock);
  }
  
@@ -1710,7 +1710,7 @@
 +		ptrace_untrace(child);
 +}
 +
-+static void ptrace_detach_task(struct task_struct *child)
++static void ptrace_detach_task(struct task_struct *child, int sig)
 +{
 +	/* Architecture-specific hardware disable .. */
 +	ptrace_disable(child);
@@ -1725,17 +1725,34 @@
 +
 +static const struct utrace_engine_ops ptrace_utrace_ops; /* forward decl */
 +
-+static void ptrace_detach_task(struct task_struct *child)
++static void ptrace_detach_task(struct task_struct *child, int sig)
 +{
++	enum utrace_resume_action action = UTRACE_DETACH;
 +	struct utrace_attached_engine *engine;
++	int ret;
++
 +	engine = utrace_attach_task(child, UTRACE_ATTACH_MATCH_OPS,
 +				    &ptrace_utrace_ops, NULL);
-+	if (likely(!IS_ERR(engine))) {
-+		int ret = utrace_control(child, engine, UTRACE_DETACH);
-+		WARN_ON(ret && ret != -EINPROGRESS &&
-+			ret != -ESRCH && ret != -EALREADY);
-+		utrace_engine_put(engine);
++	if (unlikely(IS_ERR(engine)))
++		return;
++
++	/*
++	 * When leaving a parting signal to deliver, we cannot detach our
++	 * utrace engine yet.  We need it there to get a report_signal()
++	 * callback that can inject our parting signal.  Instead, we'll
++	 * mark our engine by clearing its data pointer.  Our callbacks
++	 * will see this and know it's a PTRACE_DETACH still in progress.
++	 */
++	if (sig) {
++		engine->data = NULL;
++		action = UTRACE_INTERRUPT;
 +	}
++
++	ret = utrace_control(child, engine, action);
++	WARN_ON(ret && ret != -EINPROGRESS &&
++		ret != -ESRCH && ret != -EALREADY);
++
++	utrace_engine_put(engine);
 +}
 +
 +void ptrace_utrace_exit(struct task_struct *task)
@@ -1743,7 +1760,7 @@
 +	struct task_struct *child;
 +	read_lock(&tasklist_lock);
 +	list_for_each_entry(child, &task->ptraced, ptrace_entry)
-+		ptrace_detach_task(child);
++		ptrace_detach_task(child, 0);
 +	read_unlock(&tasklist_lock);
 +}
 +
@@ -1756,7 +1773,7 @@
  /*
   * unptrace a task: move it back to its original parent and
   * remove it from the ptrace list.
-@@ -72,10 +123,11 @@ void __ptrace_unlink(struct task_struct 
+@@ -72,10 +140,11 @@ void __ptrace_unlink(struct task_struct 
  	child->parent = child->real_parent;
  	list_del_init(&child->ptrace_entry);
  
@@ -1770,19 +1787,21 @@
  /*
   * Check that we have indeed attached to the thing..
   */
-@@ -113,6 +165,464 @@ int ptrace_check_attach(struct task_stru
+@@ -113,6 +182,536 @@ int ptrace_check_attach(struct task_stru
  	return ret;
  }
  
-+static struct utrace_attached_engine *ptrace_attach_utrace(
-+	struct task_struct *task)
++static struct utrace_attached_engine *prepare_ptrace_attach(
++	struct task_struct *child, struct task_struct *parent)
 +{
 +	return NULL;
 +}
 +
-+static void ptrace_detach_utrace(struct task_struct *task,
-+				 struct utrace_attached_engine *engine)
++static int finish_ptrace_attach(struct task_struct *task,
++				 struct utrace_attached_engine *engine,
++				 int retval)
 +{
++	return retval;
 +}
 +
 +static int ptrace_update_utrace(struct task_struct *task,
@@ -1867,7 +1886,31 @@
 +	return (task->ptrace >> 16) & UTRACE_SYSCALL_MASK;
 +}
 +
-+static u32 utrace_ptrace_report(u32 action, struct task_struct *task, int code)
++/*
++ * Remember which event stop this was.  We have to keep a record that
++ * won't be wiped by ptrace_do_wait() when @task->exit_code is cleared.
++ * So we store the PTRACE_EVENT_* value in a nybble of @task->ptrace.
++ */
++static void ptrace_set_stop_event(struct task_struct *task, int event)
++{
++	task->ptrace &= 0xf0ffffffU;
++	task->ptrace |= event << 24;
++}
++
++static int ptrace_stop_event(struct task_struct *task)
++{
++	return (task->ptrace >> 24) & 0xf;
++}
++
++/*
++ * This doesn't really exist in the user API, but we pick an unused value
++ * encoded with ptrace_set_stop_event() to record when the last stop was
++ * for a syscall stop.
++ */
++#define PTRACE_EVENT_SYSCALL	0xf
++
++static u32 utrace_ptrace_report(u32 action, struct task_struct *task,
++				int event, int code)
 +{
 +	/*
 +	 * Special kludge magic in utrace.c (utrace_stop) sees this
@@ -1876,7 +1919,15 @@
 +	 */
 +	ptrace_set_action(task, UTRACE_STOP, 0);
 +
-+	task->exit_code = code;
++	/*
++	 * If we already have a pending stop event, then don't override it.
++	 * We'll simulate this stop when the pending one is cleared,
++	 * in ptrace_resume(), below.
++	 */
++	if (ptrace_stop_event(task) == 0) {
++		ptrace_set_stop_event(task, event);
++		task->exit_code = code;
++	}
 +
 +	return action | UTRACE_STOP;
 +}
@@ -1885,7 +1936,7 @@
 +			       int event, unsigned long msg)
 +{
 +	task->ptrace_message = msg;
-+	return utrace_ptrace_report(0, task, (event << 8) | SIGTRAP);
++	return utrace_ptrace_report(0, task, event, (event << 8) | SIGTRAP);
 +}
 +
 +static u32 ptrace_report_exec(enum utrace_resume_action action,
@@ -1913,8 +1964,6 @@
 +	return utrace_ptrace_event(task, PTRACE_EVENT_EXIT, *code);
 +}
 +
-+#define	PT_VFORKING	PT_DTRACE /* reuse obsolete bit */
-+
 +static u32 ptrace_report_clone(enum utrace_resume_action action,
 +			       struct utrace_attached_engine *engine,
 +			       struct task_struct *parent,
@@ -1924,22 +1973,20 @@
 +	int event;
 +	struct utrace_attached_engine *child_engine;
 +
-+	/*
-+	 * To simulate vfork-done tracing, we'll have to catch the
-+	 * parent's syscall-exit event for this vfork/clone system call.
-+	 * Since PTRACE_SETOPTIONS can enable PTRACE_O_TRACEVFORKDONE
-+	 * during the PTRACE_EVENT_VFORK stop, we must do this if either
-+	 * is enabled right now.
-+	 */
 +	if ((clone_flags & CLONE_VFORK) &&
-+	    (parent->ptrace & (PT_TRACE_VFORK | PT_TRACE_VFORK_DONE))) {
-+		if (!(engine->flags & UTRACE_EVENT(SYSCALL_EXIT))) {
-+			int ret = utrace_set_events(parent, engine,
-+						    engine->flags |
-+						    UTRACE_EVENT(SYSCALL_EXIT));
-+			WARN_ON(ret);
-+		}
-+		parent->ptrace |= PT_VFORKING;
++	    (parent->ptrace & PT_TRACE_VFORK_DONE)) {
++		/*
++		 * Stash the child PID for a PTRACE_EVENT_VFORK_DONE report,
++		 * even if we don't report this clone event itself.  Mark
++		 * that we vfork'd even if we don't actually stop here, so
++		 * that we'll know to do the vfork-done report.  (This
++		 * matters when is PTRACE_O_TRACEVFORKDONE set but
++		 * PTRACE_O_TRACEVFORK is not set.)
++		 */
++		int rc = utrace_control(parent, engine, UTRACE_REPORT);
++		WARN_ON(rc);
++		parent->ptrace_message = child->pid;
++		ptrace_set_stop_event(parent, PTRACE_EVENT_VFORK);
 +	}
 +
 +	if (clone_flags & CLONE_UNTRACED)
@@ -1964,7 +2011,8 @@
 +		child_engine = utrace_attach_task(child, UTRACE_ATTACH_CREATE |
 +						  UTRACE_ATTACH_EXCLUSIVE |
 +						  UTRACE_ATTACH_MATCH_OPS,
-+						  &ptrace_utrace_ops, NULL);
++						  &ptrace_utrace_ops,
++						  parent->parent);
 +		if (unlikely(IS_ERR(child_engine))) {
 +			WARN_ON(1);	/* XXX */
 +		} else {
@@ -1986,13 +2034,12 @@
 +	return UTRACE_RESUME;
 +}
 +
-+
 +static u32 ptrace_report_syscall(u32 action, struct task_struct *task)
 +{
 +	int code = SIGTRAP;
 +	if (task->ptrace & PT_TRACESYSGOOD)
 +		code |= 0x80;
-+	return utrace_ptrace_report(action, task, code);
++	return utrace_ptrace_report(action, task, PTRACE_EVENT_SYSCALL, code);
 +}
 +
 +static u32 ptrace_report_syscall_entry(u32 action,
@@ -2010,38 +2057,31 @@
 +	return ptrace_report_syscall(UTRACE_SYSCALL_RUN, task);
 +}
 +
++/*
++ * Report a vfork-done event to precede another event report.
++ * There is no utrace callback associated directly with vfork-done.
++ * Instead, after seeing CLONE_VFORK in report_clone() we notice
++ * the following syscall-exit, signal, or quiesce callback and
++ * inject the PTRACE_EVENT_VFORK_DONE stop there.
++ */
++static u32 ptrace_report_vfork_done(struct task_struct *task)
++{
++	/*
++	 * We got here after a vfork, when vfork-done tracing
++	 * was enabled.
++	 */
++	ptrace_set_stop_event(task, 0);
++	return utrace_ptrace_event(task, PTRACE_EVENT_VFORK_DONE,
++				   task->ptrace_message);
++}
++
 +static u32 ptrace_report_syscall_exit(enum utrace_resume_action action,
 +				      struct utrace_attached_engine *engine,
 +				      struct task_struct *task,
 +				      struct pt_regs *regs)
 +{
-+	if (!(engine->flags & UTRACE_EVENT(SYSCALL_ENTRY))) {
-+		/*
-+		 * We were not really using PTRACE_SYSCALL.
-+		 * SYSCALL_EXIT was only caught for vfork-done tracing.
-+		 */
-+		int ret = utrace_set_events(
-+			task, engine,
-+			engine->flags & ~UTRACE_EVENT(SYSCALL_EXIT));
-+		WARN_ON(ret);
-+		WARN_ON(!(task->ptrace & PT_VFORKING));
-+		task->ptrace &= ~PT_VFORKING;
-+		if (task->ptrace & PT_TRACE_VFORK_DONE)
-+			return utrace_ptrace_event(task,
-+						   PTRACE_EVENT_VFORK_DONE, 0);
-+		return UTRACE_RESUME;
-+	}
-+
-+	if (task->ptrace & PT_VFORKING) {
-+		/*
-+		 * If we're reporting vfork-done, we'll have to
-+		 * remember to report syscall-exit after that.
-+		 */
-+		if (task->ptrace & PT_TRACE_VFORK_DONE)
-+			return utrace_ptrace_event(task,
-+						   PTRACE_EVENT_VFORK_DONE, 0);
-+		task->ptrace &= ~PT_VFORKING;
-+	}
++	if (unlikely(ptrace_stop_event(task) == PTRACE_EVENT_VFORK))
++		return ptrace_report_vfork_done(task);
 +
 +	if (unlikely(ptrace_syscall_action(task)) &&
 +	    unlikely(ptrace_resume_action(task) == UTRACE_SINGLESTEP))
@@ -2055,24 +2095,43 @@
 +	return ptrace_report_syscall(0, task);
 +}
 +
-+static u32 ptrace_resumed(struct task_struct *task, struct pt_regs *regs,
++static u32 ptrace_resumed(struct task_struct *task,
++			  struct utrace_attached_engine *engine,
 +			  siginfo_t *info, struct k_sigaction *return_ka)
 +{
++	enum utrace_resume_action resume = ptrace_resume_action(task);
++
++	if (unlikely(ptrace_stop_event(task) == PTRACE_EVENT_VFORK))
++		return UTRACE_SIGNAL_REPORT | ptrace_report_vfork_done(task);
++
++	/*
++	 * If PTRACE_DETACH had a signal for us to deliver,
++	 * it didn't detach the engine, but marked it for us.
++	 */
++	if (!engine->data) {
++		read_lock(&tasklist_lock);
++		if (task->ptrace & PT_PTRACED)
++			engine->data = task->parent;
++		else
++			resume = UTRACE_DETACH;
++		read_unlock(&tasklist_lock);
++	}
++
 +	/*
-+	 * This is not a new signal, but just a notification we
-+	 * asked for.  Either we're stopping after another report
-+	 * like exec or syscall, or we're resuming.
++	 * If we're stopping, or we haven't reported any signal,
++	 * then we're all done for now.
 +	 */
-+	if (ptrace_resume_action(task) == UTRACE_STOP)
-+		return UTRACE_SIGNAL_REPORT | UTRACE_STOP;
++	if (resume == UTRACE_STOP || !task->last_siginfo)
++		return UTRACE_SIGNAL_REPORT | resume;
 +
 +	/*
 +	 * We're resuming.  If there's no signal to deliver, just go.
 +	 * If we were given a signal, deliver it now.
 +	 */
++	WARN_ON(task->last_siginfo != info);
 +	task->last_siginfo = NULL;
 +	if (!task->exit_code)
-+		return UTRACE_SIGNAL_REPORT | ptrace_resume_action(task);
++		return UTRACE_SIGNAL_REPORT | resume;
 +
 +	/* Update the siginfo structure if the signal has
 +	   changed.  If the debugger wanted something
@@ -2092,7 +2151,7 @@
 +	*return_ka = task->sighand->action[info->si_signo - 1];
 +	spin_unlock_irq(&task->sighand->siglock);
 +
-+	return UTRACE_SIGNAL_DELIVER | ptrace_resume_action(task);
++	return UTRACE_SIGNAL_DELIVER | resume;
 +}
 +
 +static u32 ptrace_report_signal(u32 action,
@@ -2103,28 +2162,12 @@
 +				const struct k_sigaction *orig_ka,
 +				struct k_sigaction *return_ka)
 +{
-+	/*
-+	 * Deal with a pending vfork-done event.  We'll stop again now
-+	 * for the syscall-exit report that was replaced with vfork-done.
-+	 */
-+	if (unlikely(task->ptrace & PT_VFORKING)) {
-+		task->ptrace &= ~PT_VFORKING;
-+		if ((engine->flags & UTRACE_EVENT(SYSCALL_ENTRY)) &&
-+		    utrace_signal_action(action) == UTRACE_SIGNAL_REPORT) {
-+			/*
-+			 * Make sure we get another report on wakeup.
-+			 */
-+			int x = utrace_control(task, engine, UTRACE_INTERRUPT);
-+			WARN_ON(x);
-+			return ptrace_report_syscall(UTRACE_SIGNAL_REPORT,
-+						     task);
-+		}
-+	}
-+
 +	switch (utrace_signal_action(action)) {
 +	default:
++		WARN_ON(ptrace_stop_event(task) && info->si_signo != SIGKILL);
 +		break;
 +	case UTRACE_SIGNAL_HANDLER:
++		WARN_ON(ptrace_stop_event(task));
 +		/*
 +		 * A handler was set up.  If we are stepping, pretend
 +		 * another SIGTRAP arrived.
@@ -2140,11 +2183,17 @@
 +		}
 +		/* Fall through.  */
 +	case UTRACE_SIGNAL_REPORT:
-+		return ptrace_resumed(task, regs, info, return_ka);
++		/*
++		 * This is not a new signal, but just a notification we
++		 * asked for.  Either we're stopping after another report
++		 * like exec or syscall, or we're resuming.
++		 */
++		return ptrace_resumed(task, engine, info, return_ka);
 +	}
 +
++	WARN_ON(!valid_signal(info->si_signo));
 +	task->last_siginfo = info;
-+	return utrace_ptrace_report(UTRACE_SIGNAL_IGN, task, info->si_signo);
++	return utrace_ptrace_report(UTRACE_SIGNAL_IGN, task, 0, info->si_signo);
 +}
 +
 +static u32 ptrace_report_quiesce(u32 action,
@@ -2152,13 +2201,13 @@
 +				 struct task_struct *task,
 +				 unsigned long event)
 +{
-+	/*
-+	 * Make sure we deal with a pending vfork-done event (see above).
-+	 */
-+	if (unlikely(task->ptrace & PT_VFORKING))
-+		return UTRACE_INTERRUPT;
++	if (event == 0) {
++		task->last_siginfo = NULL;
++
++		if (unlikely(ptrace_stop_event(task) == PTRACE_EVENT_VFORK))
++			return ptrace_report_vfork_done(task);
++	}
 +
-+	task->last_siginfo = NULL;
 +	return ptrace_resume_action(task);
 +}
 +
@@ -2175,34 +2224,74 @@
 +};
 +
 +/*
-+ * Detach the utrace engine.
++ * Detach the utrace engine on error.  On success, set @engine->data to
++ * the ptracer's task_struct pointer.  This distinguishes an engine for
++ * attached ptrace from an engine left behind after a PTRACE_DETACH call
++ * that left a parting signal to deliver.
 + */
-+static void ptrace_detach_utrace(struct task_struct *task,
-+				 struct utrace_attached_engine *engine)
++static int finish_ptrace_attach(struct task_struct *task,
++				struct utrace_attached_engine *engine,
++				int retval)
 +{
-+	int ret = utrace_control(task, engine, UTRACE_DETACH);
-+	WARN_ON(ret && ret != -ESRCH && ret != -EALREADY);
++	if (retval) {
++		int error = utrace_control(task, engine, UTRACE_DETACH);
++		WARN_ON(error && error != -ESRCH && error != -EALREADY);
++	} else {
++		engine->data = task->parent;
++	}
++	utrace_engine_put(engine);
++	return retval;
 +}
 +
 +/*
 + * Attach a utrace engine for ptrace and set up its event mask.
 + * Returns the engine pointer or an IS_ERR() pointer.
 + */
-+static struct utrace_attached_engine *ptrace_attach_utrace(
-+	struct task_struct *child)
++static struct utrace_attached_engine *prepare_ptrace_attach(
++	struct task_struct *child, struct task_struct *parent)
 +{
 +	struct utrace_attached_engine *engine;
++restart:
 +	engine = utrace_attach_task(child, UTRACE_ATTACH_CREATE |
 +				    UTRACE_ATTACH_EXCLUSIVE |
 +				    UTRACE_ATTACH_MATCH_OPS,
-+				    &ptrace_utrace_ops, NULL);
-+	if (IS_ERR(engine))
-+		return engine;
-+	if (likely(!ptrace_update_utrace(child, engine)))
-+		return engine;
-+	ptrace_detach_utrace(child, engine);
-+	utrace_engine_put(engine);
-+	return ERR_PTR(-ESRCH);
++				    &ptrace_utrace_ops, parent);
++	if (engine == ERR_PTR(-EEXIST)) {
++		/*
++		 * There is already some ptrace engine attached.  Either
++		 * this attach will fail because another ptracer is
++		 * already attached, or else this engine was left behind
++		 * by PTRACE_DETACH to deliver a signal.
++		 */
++		engine = utrace_attach_task(child,
++					    UTRACE_ATTACH_MATCH_OPS,
++					    &ptrace_utrace_ops, NULL);
++		if (IS_ERR(engine)) {
++			BUG_ON(PTR_ERR(engine) != -ENOENT);
++			goto restart;
++		}
++		if (engine->data) {
++			utrace_engine_put(engine);
++			engine = ERR_PTR(-EEXIST);
++		}
++	}
++	if (IS_ERR(engine)) {
++		if (engine != ERR_PTR(-ESRCH))
++			engine = ERR_PTR(-EPERM);
++	} else {
++		int ret = ptrace_update_utrace(child, engine);
++		if (ret == -EINPROGRESS) {
++			ret = utrace_barrier(child, engine);
++			utrace_engine_put(engine);
++			if (ret != -ERESTARTSYS)
++				goto restart;
++			return ERR_PTR(-ERESTARTNOINTR);
++		}
++		if (ret)
++			engine = ERR_PTR(finish_ptrace_attach(child, engine,
++							      -ESRCH));
++	}
++	return engine;
 +}
 +
 +int ptrace_check_attach(struct task_struct *child, int kill)
@@ -2235,7 +2324,7 @@
  int __ptrace_may_access(struct task_struct *task, unsigned int mode)
  {
  	/* May we inspect the given task?
-@@ -156,6 +666,7 @@ int ptrace_attach(struct task_struct *ta
+@@ -156,12 +755,17 @@ int ptrace_attach(struct task_struct *ta
  {
  	int retval;
  	unsigned long flags;
@@ -2243,44 +2332,40 @@
  
  	audit_ptrace(task);
  
-@@ -163,6 +674,13 @@ int ptrace_attach(struct task_struct *ta
+ 	retval = -EPERM;
  	if (same_thread_group(task, current))
- 		goto out;
- 
-+	engine = ptrace_attach_utrace(task);
-+	if (unlikely(IS_ERR(engine))) {
-+		if (PTR_ERR(engine) == -ESRCH)
-+			retval = -ESRCH;
-+		goto out;
-+	}
+-		goto out;
++		return retval;
 +
++	engine = prepare_ptrace_attach(task, current);
++	if (unlikely(IS_ERR(engine)))
++		return PTR_ERR(engine);
+ 
  repeat:
  	/*
- 	 * Nasty, nasty.
-@@ -202,6 +720,11 @@ repeat:
+@@ -202,8 +806,8 @@ repeat:
  bad:
  	write_unlock_irqrestore(&tasklist_lock, flags);
  	task_unlock(task);
-+	if (!IS_ERR(engine)) {
-+		if (retval)
-+			ptrace_detach_utrace(task, engine);
-+		utrace_engine_put(engine);
-+	}
- out:
- 	return retval;
+-out:
+-	return retval;
++
++	return finish_ptrace_attach(task, engine, retval);
  }
-@@ -221,9 +744,7 @@ int ptrace_detach(struct task_struct *ch
+ 
+ static inline void __ptrace_detach(struct task_struct *child, unsigned int data)
+@@ -221,9 +825,7 @@ int ptrace_detach(struct task_struct *ch
  	if (!valid_signal(data))
  		return -EIO;
  
 -	/* Architecture-specific hardware disable .. */
 -	ptrace_disable(child);
 -	clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
-+	ptrace_detach_task(child);
++	ptrace_detach_task(child, data);
  
  	write_lock_irq(&tasklist_lock);
  	/* protect against de_thread()->release_task() */
-@@ -309,6 +830,8 @@ static int ptrace_setoptions(struct task
+@@ -309,6 +911,8 @@ static int ptrace_setoptions(struct task
  	if (data & PTRACE_O_TRACEEXIT)
  		child->ptrace |= PT_TRACE_EXIT;
  
@@ -2289,7 +2374,7 @@
  	return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
  }
  
-@@ -367,6 +890,7 @@ static int ptrace_setsiginfo(struct task
+@@ -367,6 +971,7 @@ static int ptrace_setsiginfo(struct task
  #define is_sysemu_singlestep(request)	0
  #endif
  
@@ -2297,7 +2382,7 @@
  static int ptrace_resume(struct task_struct *child, long request, long data)
  {
  	if (!valid_signal(data))
-@@ -401,6 +925,76 @@ static int ptrace_resume(struct task_str
+@@ -401,6 +1006,170 @@ static int ptrace_resume(struct task_str
  
  	return 0;
  }
@@ -2346,24 +2431,118 @@
 +	}
 +
 +	if (!ret) {
-+		child->exit_code = data;
++		/*
++		 * If there was a non-signal event reported last time,
++		 * we may need to simulate another event that follows
++		 * it, or do some other special case.  We'll set @event
++		 * nonzero here if there is an event to simulate.
++		 */
++		int event = ptrace_stop_event(child);
++		ptrace_set_stop_event(child, 0);
 +
-+		ptrace_set_action(child, action, syscall);
++		switch (event) {
++		case 0:
++			break;
++
++		default:
++			WARN_ON(1);
++		case PTRACE_EVENT_EXIT:
++			event = 0;
++			break;
++
++		case PTRACE_EVENT_FORK:
++		case PTRACE_EVENT_CLONE:
++		case PTRACE_EVENT_EXEC:
++		case PTRACE_EVENT_VFORK_DONE:
++			/*
++			 * At these events, @data is ignored.
++			 * After these there might be a syscall-exit stop.
++			 */
++			if (request == PTRACE_SYSCALL) {
++				data = SIGTRAP;
++				if (child->ptrace & PT_TRACESYSGOOD)
++					data |= 0x80;
++				ptrace_set_stop_event(child,
++						      PTRACE_EVENT_SYSCALL);
++			} else {
++				data = 0;
++				event = 0;
++			}
++			break;
++
++		case PTRACE_EVENT_SYSCALL:
++			/*
++			 * After a syscall stop, @data is treated specially.
++			 * It just queues the signal.
++			 */
++			if (data)
++				send_sig(data, child, 1);
++			data = 0;
++			event = 0;
++			break;
 +
-+		if (task_is_stopped(child)) {
-+			spin_lock_irq(&child->sighand->siglock);
-+			child->signal->flags &= ~SIGNAL_STOP_STOPPED;
-+			spin_unlock_irq(&child->sighand->siglock);
++		case PTRACE_EVENT_VFORK:
++			/*
++			 * For this event, @data is ignored.  We did have a
++			 * utrace stop right after, so on resuming there
++			 * can be a normal syscall-exit stop next if needed.
++			 * But we may need to simulate a vfork-done stop,
++			 * so we leave the marker in place.
++			 */
++			data = 0;
++			event = 0;
++			if (child->ptrace & PT_TRACE_VFORK_DONE) {
++				ptrace_set_stop_event(child,
++						      PTRACE_EVENT_VFORK);
++				action = UTRACE_REPORT;
++			}
++			break;
 +		}
 +
++		child->exit_code = data;
++
++		ptrace_set_action(child, action, syscall);
++
 +		/*
-+		 * To resume with a signal we must hit ptrace_report_signal.
++		 * Whatever action we want to resume with, we need to make
++		 * sure the child gets into ptrace_report_quiesce() so that
++		 * it clears last_siginfo before going back to user mode.
 +		 */
-+		if (data)
-+			action = UTRACE_INTERRUPT;
++		if (child->last_siginfo)
++			action = UTRACE_REPORT;
 +
-+		if (utrace_control(child, engine, action))
-+			ret = -ESRCH;
++		if (event) {
++			/*
++			 * We have a stacked event.  That is, @child is
++			 * already at the last utrace stop point before
++			 * returning to user mode.  But the ptrace API
++			 * wants multiple stops on the way out.  Here the
++			 * original ptrace would resume the child and have
++			 * to stop again almost immediately.  Instead we
++			 * just leave it stopped and simulate the wake-up
++			 * and new stop by notifying the parent, i.e.
++			 * current's own thread group.
++			 */
++			read_lock(&tasklist_lock);
++			do_notify_parent_cldstop(child, CLD_TRAPPED);
++			read_unlock(&tasklist_lock);
++		} else {
++			if (task_is_stopped(child)) {
++				spin_lock_irq(&child->sighand->siglock);
++				child->signal->flags &= ~SIGNAL_STOP_STOPPED;
++				spin_unlock_irq(&child->sighand->siglock);
++			}
++
++			/*
++			 * To resume with a signal we must arrange
++			 * to enter ptrace_report_signal().
++			 */
++			if (data)
++				action = UTRACE_INTERRUPT;
++
++			if (utrace_control(child, engine, action))
++				ret = -ESRCH;
++		}
 +	}
 +
 +	utrace_engine_put(engine);
@@ -2374,28 +2553,28 @@
  
  int ptrace_request(struct task_struct *child, long request,
  		   long addr, long data)
-@@ -480,6 +1074,11 @@ int ptrace_request(struct task_struct *c
+@@ -480,6 +1249,11 @@ int ptrace_request(struct task_struct *c
  int ptrace_traceme(void)
  {
  	int ret = -EPERM;
 +	struct utrace_attached_engine *engine;
 +
-+	engine = ptrace_attach_utrace(current);
++	engine = prepare_ptrace_attach(current, current->parent);
 +	if (unlikely(IS_ERR(engine)))
-+		return ret;
++		return PTR_ERR(engine);
  
  	/*
  	 * Are we already being traced?
-@@ -513,6 +1112,9 @@ repeat:
+@@ -513,7 +1287,8 @@ repeat:
  		write_unlock_irqrestore(&tasklist_lock, flags);
  	}
  	task_unlock(current);
-+	if (ret)
-+		ptrace_detach_utrace(current, engine);
-+	utrace_engine_put(engine);
- 	return ret;
+-	return ret;
++
++	return finish_ptrace_attach(current, engine, ret);
  }
  
+ /**
 diff --git a/kernel/signal.c b/kernel/signal.c
 index 4530fc6..0e85826 100644  
 --- a/kernel/signal.c
@@ -2451,10 +2630,10 @@
  			  struct pt_regs *regs, void *cookie)
 diff --git a/kernel/utrace.c b/kernel/utrace.c
 new file mode 100644
-index ...ff58017 100644  
+index ...3e2191c 100644  
 --- /dev/null
 +++ b/kernel/utrace.c
-@@ -0,0 +1,2586 @@
+@@ -0,0 +1,2587 @@
 +/*
 + * utrace infrastructure interface for debugging user processes
 + *
@@ -2990,7 +3169,8 @@
 +	 * be done after we are in TASK_TRACED.  This makes the
 +	 * synchronization with ptrace_do_wait() work right.
 +	 */
-+	if ((task->ptrace >> 16) == UTRACE_RESUME - UTRACE_STOP) {
++	if (((task->ptrace >> 16) & UTRACE_RESUME_MASK) ==
++	    UTRACE_RESUME - UTRACE_STOP) {
 +		read_lock(&tasklist_lock);
 +		do_notify_parent_cldstop(task, CLD_TRAPPED);
 +		read_unlock(&tasklist_lock);
@@ -3030,6 +3210,15 @@
 +	}
 +	rcu_read_unlock();
 +
++	/*
++	 * While we were in TASK_TRACED, complete_signal() considered
++	 * us "uninterested" in signal wakeups.  Now make sure we our
++	 * TIF_SIGPENDING state is correct for normal running.
++	 */
++	spin_lock_irq(&task->sighand->siglock);
++	recalc_sigpending();
++	spin_unlock_irq(&task->sighand->siglock);
++
 +	return killed;
 +}
 +
@@ -4324,12 +4513,8 @@
 +
 +/*
 + * Finish the last reporting pass before returning to user mode.
-+ *
-+ * Returns true if we might have been in TASK_TRACED and then resumed.
-+ * In that event, signal_pending() might not be set when it should be,
-+ * as the signals code passes us over while we're in TASK_TRACED.
 + */
-+static bool finish_resume_report(struct utrace_report *report,
++static void finish_resume_report(struct utrace_report *report,
 +				 struct task_struct *task,
 +				 struct utrace *utrace)
 +{
@@ -4354,7 +4539,7 @@
 +
 +	case UTRACE_STOP:
 +		report->killed = utrace_stop(task, utrace);
-+		return likely(!report->killed);
++		break;
 +
 +	case UTRACE_REPORT:
 +	case UTRACE_RESUME:
@@ -4362,8 +4547,6 @@
 +		user_disable_single_step(task);
 +		break;
 +	}
-+
-+	return false;
 +}
 +
 +/*
@@ -4418,15 +4601,8 @@
 +
 +	/*
 +	 * Finish the report and either stop or get ready to resume.
-+	 * If we stop and then signal_pending() is clear, we
-+	 * should recompute it before returning to user mode.
 +	 */
-+	if (finish_resume_report(&report, task, utrace) &&
-+	    !signal_pending(task)) {
-+		spin_lock_irq(&task->sighand->siglock);
-+		recalc_sigpending();
-+		spin_unlock_irq(&task->sighand->siglock);
-+	}
++	finish_resume_report(&report, task, utrace);
 +}
 +
 +/*
@@ -4745,16 +4921,14 @@
 +		} else if (return_ka->sa.sa_handler != SIG_IGN &&
 +			   likely(signr)) {
 +			/*
++			 * Complete the bookkeeping after the report.
 +			 * The handler will run.  If an engine wanted to
 +			 * stop or step, then make sure we do another
 +			 * report after signal handler setup.
 +			 */
-+			if (report.action != UTRACE_RESUME) {
-+				spin_lock(&utrace->lock);
-+				utrace->interrupt = 1;
-+				spin_unlock(&utrace->lock);
-+				set_tsk_thread_flag(task, TIF_SIGPENDING);
-+			}
++			if (report.action != UTRACE_RESUME)
++				report.action = UTRACE_INTERRUPT;
++			finish_report(&report, task, utrace);
 +
 +			if (unlikely(report.result & UTRACE_SIGNAL_HOLD))
 +				push_back_signal(task, info);
@@ -4817,6 +4991,12 @@
 +		return -1;
 +	}
 +
++	/*
++	 * Complete the bookkeeping after the report.
++	 * This sets utrace->report if UTRACE_STOP was used.
++	 */
++	finish_report(&report, task, utrace);
++
 +	return_ka->sa.sa_handler = SIG_DFL;
 +
 +	if (unlikely(report.result & UTRACE_SIGNAL_HOLD))




More information about the fedora-extras-commits mailing list