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
- Previous message (by thread): rpms/mkinitrd/devel .cvsignore, 1.212, 1.213 mkinitrd.spec, 1.299, 1.300 sources, 1.254, 1.255
- Next message (by thread): rpms/python-suds/devel .cvsignore, 1.3, 1.4 python-suds.spec, 1.3, 1.4 sources, 1.3, 1.4
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
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))
- Previous message (by thread): rpms/mkinitrd/devel .cvsignore, 1.212, 1.213 mkinitrd.spec, 1.299, 1.300 sources, 1.254, 1.255
- Next message (by thread): rpms/python-suds/devel .cvsignore, 1.3, 1.4 python-suds.spec, 1.3, 1.4 sources, 1.3, 1.4
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
More information about the fedora-extras-commits
mailing list