rpms/kernel/devel linux-2.6-ptrace-cleanup.patch, NONE, 1.1 linux-2.6-tracehook.patch, NONE, 1.1 config-generic, 1.123, 1.124 config-ia64, 1.1, 1.2 kernel.spec, 1.744, 1.745 linux-2.6-utrace.patch, 1.78, 1.79

Roland McGrath (roland) fedora-extras-commits at redhat.com
Wed Jul 9 03:38:01 UTC 2008


Author: roland

Update of /cvs/pkgs/rpms/kernel/devel
In directory cvs-int.fedora.redhat.com:/tmp/cvs-serv419

Modified Files:
	config-generic config-ia64 kernel.spec linux-2.6-utrace.patch 
Added Files:
	linux-2.6-ptrace-cleanup.patch linux-2.6-tracehook.patch 
Log Message:
new bleeding-edge utrace code

linux-2.6-ptrace-cleanup.patch:

--- NEW FILE linux-2.6-ptrace-cleanup.patch ---
 arch/powerpc/kernel/entry_32.S    |    4 +-
 arch/powerpc/kernel/ptrace.c      |   19 ++-
 arch/powerpc/kernel/signal.c      |   12 +-
 arch/powerpc/kernel/signal_32.c   |    2 +-
 arch/ppc/kernel/entry.S           |    4 +-
 arch/x86/kernel/entry_32.S        |   11 +-
 arch/x86/kernel/ptrace.c          |  134 ++++--------
 arch/x86/kernel/step.c            |   22 ++-
 include/asm-powerpc/processor.h   |    2 +
 include/asm-powerpc/ptrace.h      |    4 +
 include/asm-powerpc/thread_info.h |   17 +-
 include/asm-x86/ptrace-abi.h      |    6 +-
 include/asm-x86/thread_info_64.h  |    2 +
 include/linux/init_task.h         |    4 +-
 include/linux/sched.h             |   26 +--
 kernel/exit.c                     |  450 +++++++++++++++++++++++--------------
 kernel/fork.c                     |    6 +-
 kernel/ptrace.c                   |   37 ++-
 18 files changed, 428 insertions(+), 334 deletions(-)

diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S
index 0c8614d..3a05e9f 100644  
--- a/arch/powerpc/kernel/entry_32.S
+++ b/arch/powerpc/kernel/entry_32.S
@@ -668,7 +668,7 @@ user_exc_return:		/* r10 contains MSR_KE
 	/* Check current_thread_info()->flags */
 	rlwinm	r9,r1,0,0,(31-THREAD_SHIFT)
 	lwz	r9,TI_FLAGS(r9)
-	andi.	r0,r9,(_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK|_TIF_NEED_RESCHED)
+	andi.	r0,r9,_TIF_USER_WORK_MASK
 	bne	do_work
 
 restore_user:
@@ -925,7 +925,7 @@ recheck:
 	lwz	r9,TI_FLAGS(r9)
 	andi.	r0,r9,_TIF_NEED_RESCHED
 	bne-	do_resched
-	andi.	r0,r9,_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK
+	andi.	r0,r9,_TIF_USER_WORK_MASK
 	beq	restore_user
 do_user_signal:			/* r10 contains MSR_KERNEL here */
 	ori	r10,r10,MSR_EE
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index 2a9fe97..91ee077 100644  
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -619,12 +619,29 @@ void user_enable_single_step(struct task
 		task->thread.dbcr0 = DBCR0_IDM | DBCR0_IC;
 		regs->msr |= MSR_DE;
 #else
+		regs->msr &= ~MSR_BE;
 		regs->msr |= MSR_SE;
 #endif
 	}
 	set_tsk_thread_flag(task, TIF_SINGLESTEP);
 }
 
+void user_enable_block_step(struct task_struct *task)
+{
+	struct pt_regs *regs = task->thread.regs;
+
+	if (regs != NULL) {
+#if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
+		task->thread.dbcr0 = DBCR0_IDM | DBCR0_BT;
+		regs->msr |= MSR_DE;
+#else
+		regs->msr &= ~MSR_SE;
+		regs->msr |= MSR_BE;
+#endif
+	}
+	set_tsk_thread_flag(task, TIF_SINGLESTEP);
+}
+
 void user_disable_single_step(struct task_struct *task)
 {
 	struct pt_regs *regs = task->thread.regs;
@@ -634,7 +651,7 @@ void user_disable_single_step(struct tas
 		task->thread.dbcr0 = 0;
 		regs->msr &= ~MSR_DE;
 #else
-		regs->msr &= ~MSR_SE;
+		regs->msr &= ~(MSR_SE | MSR_BE);
 #endif
 	}
 	clear_tsk_thread_flag(task, TIF_SINGLESTEP);
diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c
index a65a44f..ad55488 100644  
--- a/arch/powerpc/kernel/signal.c
+++ b/arch/powerpc/kernel/signal.c
@@ -120,7 +120,7 @@ int do_signal(sigset_t *oldset, struct p
 	int ret;
 	int is32 = is_32bit_task();
 
-	if (test_thread_flag(TIF_RESTORE_SIGMASK))
+	if (current_thread_info()->local_flags & _TLF_RESTORE_SIGMASK)
 		oldset = &current->saved_sigmask;
 	else if (!oldset)
 		oldset = &current->blocked;
@@ -131,9 +131,10 @@ int do_signal(sigset_t *oldset, struct p
 	check_syscall_restart(regs, &ka, signr > 0);
 
 	if (signr <= 0) {
+		struct thread_info *ti = current_thread_info();
 		/* No signal to deliver -- put the saved sigmask back */
-		if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-			clear_thread_flag(TIF_RESTORE_SIGMASK);
+		if (ti->local_flags & _TLF_RESTORE_SIGMASK) {
+			ti->local_flags &= ~_TLF_RESTORE_SIGMASK;
 			sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
 		}
 		return 0;               /* no signals delivered */
@@ -169,10 +170,9 @@ int do_signal(sigset_t *oldset, struct p
 
 		/*
 		 * A signal was successfully delivered; the saved sigmask is in
-		 * its frame, and we can clear the TIF_RESTORE_SIGMASK flag.
+		 * its frame, and we can clear the TLF_RESTORE_SIGMASK flag.
 		 */
-		if (test_thread_flag(TIF_RESTORE_SIGMASK))
-			clear_thread_flag(TIF_RESTORE_SIGMASK);
+		current_thread_info()->local_flags &= ~_TLF_RESTORE_SIGMASK;
 	}
 
 	return ret;
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index ad69434..4ae16d1 100644  
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -243,7 +243,7 @@ long sys_sigsuspend(old_sigset_t mask)
 
  	current->state = TASK_INTERRUPTIBLE;
  	schedule();
- 	set_thread_flag(TIF_RESTORE_SIGMASK);
+	set_restore_sigmask();
  	return -ERESTARTNOHAND;
 }
 
diff --git a/arch/ppc/kernel/entry.S b/arch/ppc/kernel/entry.S
index 5f3a5d0..fcd830a 100644  
--- a/arch/ppc/kernel/entry.S
+++ b/arch/ppc/kernel/entry.S
@@ -647,7 +647,7 @@ user_exc_return:		/* r10 contains MSR_KE
 	/* Check current_thread_info()->flags */
 	rlwinm	r9,r1,0,0,18
 	lwz	r9,TI_FLAGS(r9)
-	andi.	r0,r9,(_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK|_TIF_NEED_RESCHED)
+	andi.	r0,r9,_TIF_USER_WORK_MASK
 	bne	do_work
 
 restore_user:
@@ -898,7 +898,7 @@ recheck:
 	lwz	r9,TI_FLAGS(r9)
 	andi.	r0,r9,_TIF_NEED_RESCHED
 	bne-	do_resched
-	andi.	r0,r9,_TIF_SIGPENDING
+	andi.	r0,r9,_TIF_USER_WORK_MASK
 	beq	restore_user
 do_user_signal:			/* r10 contains MSR_KERNEL here */
 	ori	r10,r10,MSR_EE
diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S
index c778e4f..58a5a11 100644  
--- a/arch/x86/kernel/entry_32.S
+++ b/arch/x86/kernel/entry_32.S
@@ -513,11 +513,7 @@ END(work_pending)
 syscall_trace_entry:
 	movl $-ENOSYS,PT_EAX(%esp)
 	movl %esp, %eax
-	xorl %edx,%edx
-	call do_syscall_trace
-	cmpl $0, %eax
-	jne resume_userspace		# ret != 0 -> running under PTRACE_SYSEMU,
-					# so must skip actual syscall
+	call syscall_trace_enter
 	movl PT_ORIG_EAX(%esp), %eax
 	cmpl $(nr_syscalls), %eax
 	jnae syscall_call
@@ -530,11 +526,10 @@ syscall_exit_work:
 	testb $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SINGLESTEP), %cl
 	jz work_pending
 	TRACE_IRQS_ON
-	ENABLE_INTERRUPTS(CLBR_ANY)	# could let do_syscall_trace() call
+	ENABLE_INTERRUPTS(CLBR_ANY)	# could let syscall_trace_leave() call
 					# schedule() instead
 	movl %esp, %eax
-	movl $1, %edx
-	call do_syscall_trace
+	call syscall_trace_leave
 	jmp resume_userspace
 END(syscall_exit_work)
 	CFI_ENDPROC
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index a7835f2..44ee172 100644  
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -1357,8 +1357,6 @@ const struct user_regset_view *task_user
 #endif
 }
 
-#ifdef CONFIG_X86_32
-
 void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code)
 {
 	struct siginfo info;
@@ -1377,89 +1375,10 @@ void send_sigtrap(struct task_struct *ts
 	force_sig_info(SIGTRAP, &info, tsk);
 }
 
-/* notification of system call entry/exit
- * - triggered by current->work.syscall_trace
- */
-int do_syscall_trace(struct pt_regs *regs, int entryexit)
-{
-	int is_sysemu = test_thread_flag(TIF_SYSCALL_EMU);
-	/*
-	 * With TIF_SYSCALL_EMU set we want to ignore TIF_SINGLESTEP for syscall
-	 * interception
-	 */
-	int is_singlestep = !is_sysemu && test_thread_flag(TIF_SINGLESTEP);
-	int ret = 0;
-
-	/* do the secure computing check first */
-	if (!entryexit)
-		secure_computing(regs->orig_ax);
-
-	if (unlikely(current->audit_context)) {
-		if (entryexit)
-			audit_syscall_exit(AUDITSC_RESULT(regs->ax),
-						regs->ax);
-		/* Debug traps, when using PTRACE_SINGLESTEP, must be sent only
-		 * on the syscall exit path. Normally, when TIF_SYSCALL_AUDIT is
-		 * not used, entry.S will call us only on syscall exit, not
-		 * entry; so when TIF_SYSCALL_AUDIT is used we must avoid
-		 * calling send_sigtrap() on syscall entry.
-		 *
-		 * Note that when PTRACE_SYSEMU_SINGLESTEP is used,
-		 * is_singlestep is false, despite his name, so we will still do
-		 * the correct thing.
-		 */
-		else if (is_singlestep)
-			goto out;
-	}
-
-	if (!(current->ptrace & PT_PTRACED))
-		goto out;
-
-	/* If a process stops on the 1st tracepoint with SYSCALL_TRACE
-	 * and then is resumed with SYSEMU_SINGLESTEP, it will come in
-	 * here. We have to check this and return */
-	if (is_sysemu && entryexit)
-		return 0;
-
-	/* Fake a debug trap */
-	if (is_singlestep)
-		send_sigtrap(current, regs, 0);
-
- 	if (!test_thread_flag(TIF_SYSCALL_TRACE) && !is_sysemu)
-		goto out;
-
-	/* the 0x80 provides a way for the tracing parent to distinguish
-	   between a syscall stop and SIGTRAP delivery */
-	/* Note that the debugger could change the result of test_thread_flag!*/
-	ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80:0));
-
-	/*
-	 * this isn't the same as continuing with a signal, but it will do
-	 * for normal use.  strace only continues with a signal if the
-	 * stopping signal is not SIGTRAP.  -brl
-	 */
-	if (current->exit_code) {
-		send_sig(current->exit_code, current, 1);
-		current->exit_code = 0;
-	}
-	ret = is_sysemu;
-out:
-	if (unlikely(current->audit_context) && !entryexit)
-		audit_syscall_entry(AUDIT_ARCH_I386, regs->orig_ax,
-				    regs->bx, regs->cx, regs->dx, regs->si);
-	if (ret == 0)
-		return 0;
-
-	regs->orig_ax = -1; /* force skip of syscall restarting */
-	if (unlikely(current->audit_context))
-		audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax);
-	return 1;
-}
-
-#else  /* CONFIG_X86_64 */
-
 static void syscall_trace(struct pt_regs *regs)
 {
+	if (!(current->ptrace & PT_PTRACED))
+		return;
 
 #if 0
 	printk("trace %s ip %lx sp %lx ax %d origrax %d caller %lx tiflags %x ptrace %x\n",
@@ -1481,39 +1400,64 @@ static void syscall_trace(struct pt_regs
 	}
 }
 
-asmlinkage void syscall_trace_enter(struct pt_regs *regs)
+#ifdef CONFIG_X86_32
+# define IS_IA32	1
+#elif defined CONFIG_IA32_EMULATION
+# define IS_IA32	test_thread_flag(TIF_IA32)
+#else
+# define IS_IA32	0
+#endif
+
+asmregparm void syscall_trace_enter(struct pt_regs *regs)
 {
 	/* do the secure computing check first */
 	secure_computing(regs->orig_ax);
 
-	if (test_thread_flag(TIF_SYSCALL_TRACE)
-	    && (current->ptrace & PT_PTRACED))
+	if (test_thread_flag(TIF_SYSCALL_TRACE) ||
+	    unlikely(test_thread_flag(TIF_SYSCALL_EMU)))
 		syscall_trace(regs);
 
 	if (unlikely(current->audit_context)) {
-		if (test_thread_flag(TIF_IA32)) {
+		if (IS_IA32)
 			audit_syscall_entry(AUDIT_ARCH_I386,
 					    regs->orig_ax,
 					    regs->bx, regs->cx,
 					    regs->dx, regs->si);
-		} else {
+#ifdef CONFIG_X86_64
+		else
 			audit_syscall_entry(AUDIT_ARCH_X86_64,
 					    regs->orig_ax,
 					    regs->di, regs->si,
 					    regs->dx, regs->r10);
-		}
+#endif
 	}
+
+	if (unlikely(test_thread_flag(TIF_SYSCALL_EMU)))
+		/*
+		 * Setting an invalid syscall number skips making the call
+		 * and leaves the registers as they are now (-ENOSYS in
+		 * regs->ax, or as just modified by ptrace).  This also
+		 * ensures that signal handling won't restart the call.
+		 * TIF_SYSCALL_AUDIT will still cause us to get into
+		 * syscall_trace_leave() after not making the call.
+		 */
+		regs->orig_ax = -1L;
 }
 
-asmlinkage void syscall_trace_leave(struct pt_regs *regs)
+asmregparm void syscall_trace_leave(struct pt_regs *regs)
 {
 	if (unlikely(current->audit_context))
 		audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax);
 
-	if ((test_thread_flag(TIF_SYSCALL_TRACE)
-	     || test_thread_flag(TIF_SINGLESTEP))
-	    && (current->ptrace & PT_PTRACED))
+	if (test_thread_flag(TIF_SYSCALL_TRACE))
 		syscall_trace(regs);
-}
 
-#endif	/* CONFIG_X86_32 */
+	/*
+	 * If we are single-stepping, synthesize a trap to follow the
+	 * system call instruction.
+	 */
+	if (test_thread_flag(TIF_SINGLESTEP) &&
+	    !test_thread_flag(TIF_SYSCALL_EMU) &&
+	    (current->ptrace & PT_PTRACED))
+		send_sigtrap(current, regs, 0);
+}
diff --git a/arch/x86/kernel/step.c b/arch/x86/kernel/step.c
index 92c20fe..0d2cb36 100644  
--- a/arch/x86/kernel/step.c
+++ b/arch/x86/kernel/step.c
@@ -105,6 +105,7 @@ static int is_setting_trap_flag(struct t
 static int enable_single_step(struct task_struct *child)
 {
 	struct pt_regs *regs = task_pt_regs(child);
+	unsigned long oflags;
 
 	/*
 	 * Always set TIF_SINGLESTEP - this guarantees that
@@ -113,11 +114,7 @@ static int enable_single_step(struct tas
 	 */
 	set_tsk_thread_flag(child, TIF_SINGLESTEP);
 
-	/*
-	 * If TF was already set, don't do anything else
-	 */
-	if (regs->flags & X86_EFLAGS_TF)
-		return 0;
+	oflags = regs->flags;
 
 	/* Set TF on the kernel stack.. */
 	regs->flags |= X86_EFLAGS_TF;
@@ -126,9 +123,22 @@ static int enable_single_step(struct tas
 	 * ..but if TF is changed by the instruction we will trace,
 	 * don't mark it as being "us" that set it, so that we
 	 * won't clear it by hand later.
+	 *
+	 * Note that if we don't actually execute the popf because
+	 * of a signal arriving right now or suchlike, we will lose
+	 * track of the fact that it really was "us" that set it.
 	 */
-	if (is_setting_trap_flag(child, regs))
+	if (is_setting_trap_flag(child, regs)) {
+		clear_tsk_thread_flag(child, TIF_FORCED_TF);
 		return 0;
+	}
+
+	/*
+	 * If TF was already set, check whether it was us who set it.
+	 * If not, we should never attempt a block step.
+	 */
+	if (oflags & X86_EFLAGS_TF)
+		return test_tsk_thread_flag(child, TIF_FORCED_TF);
 
 	set_tsk_thread_flag(child, TIF_FORCED_TF);
 
diff --git a/include/asm-powerpc/processor.h b/include/asm-powerpc/processor.h
index cf83f2d..865db56 100644  
--- a/include/asm-powerpc/processor.h
+++ b/include/asm-powerpc/processor.h
@@ -214,6 +214,8 @@ struct thread_struct {
 #define thread_saved_pc(tsk)    \
         ((tsk)->thread.regs? (tsk)->thread.regs->nip: 0)
 
+#define task_pt_regs(tsk)	((struct pt_regs *)(tsk)->thread.regs)
+
 unsigned long get_wchan(struct task_struct *p);
 
 #define KSTK_EIP(tsk)  ((tsk)->thread.regs? (tsk)->thread.regs->nip: 0)
diff --git a/include/asm-powerpc/ptrace.h b/include/asm-powerpc/ptrace.h
index 39023dd..6a3892d 100644  
--- a/include/asm-powerpc/ptrace.h
+++ b/include/asm-powerpc/ptrace.h
@@ -135,7 +135,9 @@ do {									      \
  * These are defined as per linux/ptrace.h, which see.
  */
 #define arch_has_single_step()	(1)
+#define arch_has_block_step()	(1)
 extern void user_enable_single_step(struct task_struct *);
+extern void user_enable_block_step(struct task_struct *);
 extern void user_disable_single_step(struct task_struct *);
 
 #endif /* __ASSEMBLY__ */
@@ -276,4 +278,6 @@ extern void user_disable_single_step(str
 #define PPC_PTRACE_PEEKUSR_3264  0x91
 #define PPC_PTRACE_POKEUSR_3264  0x90
 
+#define PTRACE_SINGLEBLOCK	0x100	/* resume execution until next branch */
+
 #endif /* _ASM_POWERPC_PTRACE_H */
diff --git a/include/asm-powerpc/thread_info.h b/include/asm-powerpc/thread_info.h
index d030f5c..e079e81 100644  
--- a/include/asm-powerpc/thread_info.h
+++ b/include/asm-powerpc/thread_info.h
@@ -116,7 +116,6 @@ static inline struct thread_info *curren
 #define TIF_SECCOMP		10	/* secure computing */
 #define TIF_RESTOREALL		11	/* Restore all regs (implies NOERROR) */
 #define TIF_NOERROR		12	/* Force successful syscall return */
-#define TIF_RESTORE_SIGMASK	13	/* Restore signal mask in do_signal */
 #define TIF_FREEZE		14	/* Freezing for suspend */
 #define TIF_RUNLATCH		15	/* Is the runlatch enabled? */
 #define TIF_ABI_PENDING		16	/* 32/64 bit switch needed */
@@ -134,21 +133,31 @@ static inline struct thread_info *curren
 #define _TIF_SECCOMP		(1<<TIF_SECCOMP)
 #define _TIF_RESTOREALL		(1<<TIF_RESTOREALL)
 #define _TIF_NOERROR		(1<<TIF_NOERROR)
-#define _TIF_RESTORE_SIGMASK	(1<<TIF_RESTORE_SIGMASK)
 #define _TIF_FREEZE		(1<<TIF_FREEZE)
 #define _TIF_RUNLATCH		(1<<TIF_RUNLATCH)
 #define _TIF_ABI_PENDING	(1<<TIF_ABI_PENDING)
 #define _TIF_SYSCALL_T_OR_A	(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP)
 
-#define _TIF_USER_WORK_MASK	( _TIF_SIGPENDING | \
-				 _TIF_NEED_RESCHED | _TIF_RESTORE_SIGMASK)
+#define _TIF_USER_WORK_MASK	(_TIF_SIGPENDING | _TIF_NEED_RESCHED)
 #define _TIF_PERSYSCALL_MASK	(_TIF_RESTOREALL|_TIF_NOERROR)
 
 /* Bits in local_flags */
 /* Don't move TLF_NAPPING without adjusting the code in entry_32.S */
 #define TLF_NAPPING		0	/* idle thread enabled NAP mode */
+#define TLF_RESTORE_SIGMASK	1	/* Restore signal mask in do_signal */
 
 #define _TLF_NAPPING		(1 << TLF_NAPPING)
+#define _TLF_RESTORE_SIGMASK	(1 << TLF_RESTORE_SIGMASK)
+
+#ifndef __ASSEMBLY__
+#define HAVE_SET_RESTORE_SIGMASK	1
+static inline void set_restore_sigmask(void)
+{
+	struct thread_info *ti = current_thread_info();
+	ti->local_flags |= _TLF_RESTORE_SIGMASK;
+	set_bit(TIF_SIGPENDING, &ti->flags);
+}
+#endif	/* !__ASSEMBLY__ */
 
 #endif /* __KERNEL__ */
 
diff --git a/include/asm-x86/ptrace-abi.h b/include/asm-x86/ptrace-abi.h
index f224eb3..72e7b9d 100644  
--- a/include/asm-x86/ptrace-abi.h
+++ b/include/asm-x86/ptrace-abi.h
@@ -73,11 +73,11 @@
 
 #ifdef __x86_64__
 # define PTRACE_ARCH_PRCTL	  30
-#else
-# define PTRACE_SYSEMU		  31
-# define PTRACE_SYSEMU_SINGLESTEP 32
 #endif
 
+#define PTRACE_SYSEMU		  31
+#define PTRACE_SYSEMU_SINGLESTEP  32
+
 #define PTRACE_SINGLEBLOCK	33	/* resume execution until next branch */
 
 #ifndef __ASSEMBLY__
diff --git a/include/asm-x86/thread_info_64.h b/include/asm-x86/thread_info_64.h
index cb69f70..79cf6ab 100644  
--- a/include/asm-x86/thread_info_64.h
+++ b/include/asm-x86/thread_info_64.h
@@ -107,6 +107,7 @@ static inline struct thread_info *stack_
 #define TIF_NEED_RESCHED	3	/* rescheduling necessary */
 #define TIF_SINGLESTEP		4	/* reenable singlestep on user return*/
 #define TIF_IRET		5	/* force IRET */
+#define TIF_SYSCALL_EMU		6	/* syscall emulation active */
 #define TIF_SYSCALL_AUDIT	7	/* syscall auditing active */
 #define TIF_SECCOMP		8	/* secure computing */
 #define TIF_MCE_NOTIFY		10	/* notify userspace of an MCE */
@@ -130,6 +131,7 @@ static inline struct thread_info *stack_
 #define _TIF_SINGLESTEP		(1 << TIF_SINGLESTEP)
 #define _TIF_NEED_RESCHED	(1 << TIF_NEED_RESCHED)
 #define _TIF_IRET		(1 << TIF_IRET)
+#define _TIF_SYSCALL_EMU	(1 << TIF_SYSCALL_EMU)
 #define _TIF_SYSCALL_AUDIT	(1 << TIF_SYSCALL_AUDIT)
 #define _TIF_SECCOMP		(1 << TIF_SECCOMP)
 #define _TIF_MCE_NOTIFY		(1 << TIF_MCE_NOTIFY)
diff --git a/include/linux/init_task.h b/include/linux/init_task.h
index 9927a88..93c45ac 100644  
--- a/include/linux/init_task.h
+++ b/include/linux/init_task.h
@@ -140,8 +140,8 @@ extern struct group_info init_groups;
 		.nr_cpus_allowed = NR_CPUS,				\
 	},								\
 	.tasks		= LIST_HEAD_INIT(tsk.tasks),			\
-	.ptrace_children= LIST_HEAD_INIT(tsk.ptrace_children),		\
-	.ptrace_list	= LIST_HEAD_INIT(tsk.ptrace_list),		\
+	.ptraced	= LIST_HEAD_INIT(tsk.ptraced),			\
+	.ptrace_entry	= LIST_HEAD_INIT(tsk.ptrace_entry),		\
 	.real_parent	= &tsk,						\
 	.parent		= &tsk,						\
 	.children	= LIST_HEAD_INIT(tsk.children),			\
diff --git a/include/linux/sched.h b/include/linux/sched.h
index c5d3f84..75145b7 100644  
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1075,12 +1075,6 @@ struct task_struct {
 #endif
 
 	struct list_head tasks;
-	/*
-	 * ptrace_list/ptrace_children forms the list of my children
-	 * that were stolen by a ptracer.
-	 */
-	struct list_head ptrace_children;
-	struct list_head ptrace_list;
 
 	struct mm_struct *mm, *active_mm;
 
@@ -1102,18 +1096,25 @@ struct task_struct {
 	/* 
 	 * pointers to (original) parent process, youngest child, younger sibling,
 	 * older sibling, respectively.  (p->father can be replaced with 
-	 * p->parent->pid)
+	 * p->real_parent->pid)
 	 */
-	struct task_struct *real_parent; /* real parent process (when being debugged) */
-	struct task_struct *parent;	/* parent process */
+	struct task_struct *real_parent; /* real parent process */
+	struct task_struct *parent; /* recipient of SIGCHLD, wait4() reports */
 	/*
-	 * children/sibling forms the list of my children plus the
-	 * tasks I'm ptracing.
+	 * children/sibling forms the list of my natural children
 	 */
 	struct list_head children;	/* list of my children */
 	struct list_head sibling;	/* linkage in my parent's children list */
 	struct task_struct *group_leader;	/* threadgroup leader */
 
+	/*
+	 * ptraced is the list of tasks this task is using ptrace on.
+	 * This includes both natural children and PTRACE_ATTACH targets.
+	 * p->ptrace_entry is p's link on the p->parent->ptraced list.
+	 */
+	struct list_head ptraced;
+	struct list_head ptrace_entry;
+
 	/* PID/PID hash table linkage. */
 	struct pid_link pids[PIDTYPE_MAX];
 	struct list_head thread_group;
@@ -1870,9 +1871,6 @@ extern void wait_task_inactive(struct ta
 #define wait_task_inactive(p)	do { } while (0)
 #endif
 
-#define remove_parent(p)	list_del_init(&(p)->sibling)
-#define add_parent(p)		list_add_tail(&(p)->sibling,&(p)->parent->children)
-
 #define next_task(p)	list_entry(rcu_dereference((p)->tasks.next), struct task_struct, tasks)
 
 #define for_each_process(p) \
diff --git a/kernel/exit.c b/kernel/exit.c
index 8f6185e..981a3bd 100644  
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -70,7 +70,7 @@ static void __unhash_process(struct task
 		__get_cpu_var(process_counts)--;
 	}
 	list_del_rcu(&p->thread_group);
-	remove_parent(p);
+	list_del_init(&p->sibling);
 }
 
 /*
@@ -151,6 +151,18 @@ static void delayed_put_task_struct(stru
 	put_task_struct(container_of(rhp, struct task_struct, rcu));
 }
 
+/*
+ * Do final ptrace-related cleanup of a zombie being reaped.
+ *
+ * Called with write_lock(&tasklist_lock) held.
+ */
+static void ptrace_release_task(struct task_struct *p)
+{
+	BUG_ON(!list_empty(&p->ptraced));
+	ptrace_unlink(p);
+	BUG_ON(!list_empty(&p->ptrace_entry));
+}
+
 void release_task(struct task_struct * p)
 {
 	struct task_struct *leader;
@@ -159,8 +171,7 @@ repeat:
 	atomic_dec(&p->user->processes);
 	proc_flush_task(p);
 	write_lock_irq(&tasklist_lock);
-	ptrace_unlink(p);
-	BUG_ON(!list_empty(&p->ptrace_list) || !list_empty(&p->ptrace_children));
+	ptrace_release_task(p);
 	__exit_signal(p);
 
 	/*
@@ -314,9 +325,8 @@ static void reparent_to_kthreadd(void)
 
 	ptrace_unlink(current);
 	/* Reparent to init */
-	remove_parent(current);
 	current->real_parent = current->parent = kthreadd_task;
-	add_parent(current);
+	list_move_tail(&current->sibling, &current->real_parent->children);
 
 	/* Set the exit signal to SIGCHLD so we signal init on exit */
 	current->exit_signal = SIGCHLD;
@@ -691,37 +701,96 @@ static void exit_mm(struct task_struct *
 	mmput(mm);
 }
 
-static void
-reparent_thread(struct task_struct *p, struct task_struct *father, int traced)
+/*
+ * Return nonzero if @parent's children should reap themselves.
+ *
+ * Called with write_lock_irq(&tasklist_lock) held.
+ */
+static int ignoring_children(struct task_struct *parent)
 {
-	if (p->pdeath_signal)
-		/* We already hold the tasklist_lock here.  */
-		group_send_sig_info(p->pdeath_signal, SEND_SIG_NOINFO, p);
+	int ret;
+	struct sighand_struct *psig = parent->sighand;
+	spin_lock(&psig->siglock);
+	ret = (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN ||
+	       (psig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT));
+	spin_unlock(&psig->siglock);
+	return ret;
+}
 
-	/* Move the child from its dying parent to the new one.  */
-	if (unlikely(traced)) {
-		/* Preserve ptrace links if someone else is tracing this child.  */
-		list_del_init(&p->ptrace_list);
-		if (ptrace_reparented(p))
-			list_add(&p->ptrace_list, &p->real_parent->ptrace_children);
-	} else {
-		/* If this child is being traced, then we're the one tracing it
-		 * anyway, so let go of it.
+/*
+ * Detach all tasks we were using ptrace on.
+ * Any that need to be release_task'd are put on the @dead list.
+ *
+ * Called with write_lock(&tasklist_lock) held.
+ */
+static void ptrace_exit(struct task_struct *parent, struct list_head *dead)
+{
+	struct task_struct *p, *n;
+	int ign = -1;
+
+	list_for_each_entry_safe(p, n, &parent->ptraced, ptrace_entry) {
+		__ptrace_unlink(p);
+
+		if (p->exit_state != EXIT_ZOMBIE)
+			continue;
+
+		/*
+		 * If it's a zombie, our attachedness prevented normal
+		 * parent notification or self-reaping.  Do notification
+		 * now if it would have happened earlier.  If it should
+		 * reap itself, add it to the @dead list.  We can't call
+		 * release_task() here because we already hold tasklist_lock.
+		 *
+		 * If it's our own child, there is no notification to do.
+		 * But if our normal children self-reap, then this child
+		 * was prevented by ptrace and we must reap it now.
 		 */
-		p->ptrace = 0;
-		remove_parent(p);
-		p->parent = p->real_parent;
-		add_parent(p);
+		if (!task_detached(p) && thread_group_empty(p)) {
+			if (!same_thread_group(p->real_parent, parent))
+				do_notify_parent(p, p->exit_signal);
+			else {
+				if (ign < 0)
+					ign = ignoring_children(parent);
+				if (ign)
+					p->exit_signal = -1;
+			}
+		}
 
-		if (task_is_traced(p)) {
+		if (task_detached(p)) {
 			/*
-			 * If it was at a trace stop, turn it into
-			 * a normal stop since it's no longer being
-			 * traced.
+			 * Mark it as in the process of being reaped.
 			 */
-			ptrace_untrace(p);
+			p->exit_state = EXIT_DEAD;
+			list_add(&p->ptrace_entry, dead);
 		}
 	}
+}
+
+/*
+ * Finish up exit-time ptrace cleanup.
+ *
+ * Called without locks.
+ */
+static void ptrace_exit_finish(struct task_struct *parent,
+			       struct list_head *dead)
+{
+	struct task_struct *p, *n;
+
+	BUG_ON(!list_empty(&parent->ptraced));
+
+	list_for_each_entry_safe(p, n, dead, ptrace_entry) {
+		list_del_init(&p->ptrace_entry);
+		release_task(p);
+	}
+}
+
+static void reparent_thread(struct task_struct *p, struct task_struct *father)
+{
+	if (p->pdeath_signal)
+		/* We already hold the tasklist_lock here.  */
+		group_send_sig_info(p->pdeath_signal, SEND_SIG_NOINFO, p);
+
+	list_move_tail(&p->sibling, &p->real_parent->children);
 
 	/* If this is a threaded reparent there is no need to
 	 * notify anyone anything has happened.
@@ -736,7 +805,8 @@ reparent_thread(struct task_struct *p, s
 	/* If we'd notified the old parent about this child's death,
 	 * also notify the new parent.
 	 */
-	if (!traced && p->exit_state == EXIT_ZOMBIE &&
+	if (!ptrace_reparented(p) &&
+	    p->exit_state == EXIT_ZOMBIE &&
 	    !task_detached(p) && thread_group_empty(p))
 		do_notify_parent(p, p->exit_signal);
 
@@ -753,12 +823,15 @@ reparent_thread(struct task_struct *p, s
 static void forget_original_parent(struct task_struct *father)
 {
 	struct task_struct *p, *n, *reaper = father;
-	struct list_head ptrace_dead;
-
-	INIT_LIST_HEAD(&ptrace_dead);
+	LIST_HEAD(ptrace_dead);
 
 	write_lock_irq(&tasklist_lock);
 
+	/*
+	 * First clean up ptrace if we were using it.
+	 */
+	ptrace_exit(father, &ptrace_dead);
+
 	do {
 		reaper = next_thread(reaper);
 		if (reaper == father) {
@@ -767,58 +840,19 @@ static void forget_original_parent(struc
 		}
 	} while (reaper->flags & PF_EXITING);
 
-	/*
-	 * There are only two places where our children can be:
-	 *
-	 * - in our child list
-	 * - in our ptraced child list
-	 *
-	 * Search them and reparent children.
-	 */
 	list_for_each_entry_safe(p, n, &father->children, sibling) {
-		int ptrace;
-
-		ptrace = p->ptrace;
-
-		/* if father isn't the real parent, then ptrace must be enabled */
-		BUG_ON(father != p->real_parent && !ptrace);
-
-		if (father == p->real_parent) {
-			/* reparent with a reaper, real father it's us */
-			p->real_parent = reaper;
-			reparent_thread(p, father, 0);
-		} else {
-			/* reparent ptraced task to its real parent */
-			__ptrace_unlink (p);
-			if (p->exit_state == EXIT_ZOMBIE && !task_detached(p) &&
-			    thread_group_empty(p))
-				do_notify_parent(p, p->exit_signal);
-		}
-
-		/*
-		 * if the ptraced child is a detached zombie we must collect
-		 * it before we exit, or it will remain zombie forever since
-		 * we prevented it from self-reap itself while it was being
-		 * traced by us, to be able to see it in wait4.
-		 */
-		if (unlikely(ptrace && p->exit_state == EXIT_ZOMBIE && task_detached(p)))
-			list_add(&p->ptrace_list, &ptrace_dead);
-	}
-
-	list_for_each_entry_safe(p, n, &father->ptrace_children, ptrace_list) {
 		p->real_parent = reaper;
-		reparent_thread(p, father, 1);
+		if (p->parent == father) {
+			BUG_ON(p->ptrace);
+			p->parent = p->real_parent;
+		}
+		reparent_thread(p, father);
 	}
 
 	write_unlock_irq(&tasklist_lock);
 	BUG_ON(!list_empty(&father->children));
-	BUG_ON(!list_empty(&father->ptrace_children));
-
-	list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_list) {
-		list_del_init(&p->ptrace_list);
-		release_task(p);
-	}
 
+	ptrace_exit_finish(father, &ptrace_dead);
 }
 
 /*
@@ -1179,13 +1213,6 @@ static int eligible_child(enum pid_type 
 			return 0;
 	}
 
-	/*
-	 * Do not consider detached threads that are
-	 * not ptraced:
-	 */
-	if (task_detached(p) && !p->ptrace)
-		return 0;
-
 	/* Wait for all children (clone and not) if __WALL is set;
 	 * otherwise, wait for clone children *only* if __WCLONE is
 	 * set; otherwise, wait for non-clone children *only*.  (Note:
@@ -1196,14 +1223,10 @@ static int eligible_child(enum pid_type 
 		return 0;
 
 	err = security_task_wait(p);
-	if (likely(!err))
-		return 1;
+	if (err)
+		return err;
 
-	if (type != PIDTYPE_PID)
-		return 0;
-	/* This child was explicitly requested, abort */
-	read_unlock(&tasklist_lock);
-	return err;
+	return 1;
 }
 
 static int wait_noreap_copyout(struct task_struct *p, pid_t pid, uid_t uid,
@@ -1237,7 +1260,7 @@ static int wait_noreap_copyout(struct ta
  * the lock and this task is uninteresting.  If we return nonzero, we have
  * released the lock and the system call should return.
  */
-static int wait_task_zombie(struct task_struct *p, int noreap,
+static int wait_task_zombie(struct task_struct *p, int options,
 			    struct siginfo __user *infop,
 			    int __user *stat_addr, struct rusage __user *ru)
 {
@@ -1245,7 +1268,10 @@ static int wait_task_zombie(struct task_
 	int retval, status, traced;
 	pid_t pid = task_pid_vnr(p);
 
-	if (unlikely(noreap)) {
+	if (!likely(options & WEXITED))
+		return 0;
+
+	if (unlikely(options & WNOWAIT)) {
 		uid_t uid = p->uid;
 		int exit_code = p->exit_code;
 		int why, status;
@@ -1395,21 +1421,24 @@ static int wait_task_zombie(struct task_
  * the lock and this task is uninteresting.  If we return nonzero, we have
  * released the lock and the system call should return.
  */
-static int wait_task_stopped(struct task_struct *p,
-			     int noreap, struct siginfo __user *infop,
+static int wait_task_stopped(int ptrace, struct task_struct *p,
+			     int options, struct siginfo __user *infop,
 			     int __user *stat_addr, struct rusage __user *ru)
 {
 	int retval, exit_code, why;
 	uid_t uid = 0; /* unneeded, required by compiler */
 	pid_t pid;
 
+	if (!(options & WUNTRACED))
+		return 0;
+
 	exit_code = 0;
 	spin_lock_irq(&p->sighand->siglock);
 
 	if (unlikely(!task_is_stopped_or_traced(p)))
 		goto unlock_sig;
 
-	if (!(p->ptrace & PT_PTRACED) && p->signal->group_stop_count > 0)
+	if (!ptrace && p->signal->group_stop_count > 0)
 		/*
 		 * A group stop is in progress and this is the group leader.
 		 * We won't report until all threads have stopped.
@@ -1420,7 +1449,7 @@ static int wait_task_stopped(struct task
 	if (!exit_code)
 		goto unlock_sig;
 
-	if (!noreap)
+	if (!unlikely(options & WNOWAIT))
 		p->exit_code = 0;
 
 	uid = p->uid;
@@ -1438,10 +1467,10 @@ unlock_sig:
 	 */
 	get_task_struct(p);
 	pid = task_pid_vnr(p);
-	why = (p->ptrace & PT_PTRACED) ? CLD_TRAPPED : CLD_STOPPED;
+	why = ptrace ? CLD_TRAPPED : CLD_STOPPED;
 	read_unlock(&tasklist_lock);
 
-	if (unlikely(noreap))
+	if (unlikely(options & WNOWAIT))
 		return wait_noreap_copyout(p, pid, uid,
 					   why, exit_code,
 					   infop, ru);
@@ -1475,7 +1504,7 @@ unlock_sig:
  * the lock and this task is uninteresting.  If we return nonzero, we have
  * released the lock and the system call should return.
  */
-static int wait_task_continued(struct task_struct *p, int noreap,
+static int wait_task_continued(struct task_struct *p, int options,
 			       struct siginfo __user *infop,
 			       int __user *stat_addr, struct rusage __user *ru)
 {
@@ -1483,6 +1512,9 @@ static int wait_task_continued(struct ta
 	pid_t pid;
 	uid_t uid;
 
+	if (!unlikely(options & WCONTINUED))
+		return 0;
+
 	if (!(p->signal->flags & SIGNAL_STOP_CONTINUED))
 		return 0;
 
@@ -1492,7 +1524,7 @@ static int wait_task_continued(struct ta
 		spin_unlock_irq(&p->sighand->siglock);
 		return 0;
 	}
-	if (!noreap)
+	if (!unlikely(options & WNOWAIT))
 		p->signal->flags &= ~SIGNAL_STOP_CONTINUED;
 	spin_unlock_irq(&p->sighand->siglock);
 
@@ -1518,89 +1550,161 @@ static int wait_task_continued(struct ta
 	return retval;
 }
 
+/*
+ * Consider @p for a wait by @parent.
+ *
+ * -ECHILD should be in *@notask_error before the first call.
+ * Returns nonzero for a final return, when we have unlocked tasklist_lock.
+ * Returns zero if the search for a child should continue;
+ * then *@notask_error is 0 if @p is an eligible child,
+ * or another error from security_task_wait(), or still -ECHILD.
+ */
+static int wait_consider_task(struct task_struct *parent, int ptrace,
+			      struct task_struct *p, int *notask_error,
+			      enum pid_type type, struct pid *pid, int options,
+			      struct siginfo __user *infop,
+			      int __user *stat_addr, struct rusage __user *ru)
+{
+	int ret = eligible_child(type, pid, options, p);
+	if (!ret)
+		return ret;
+
+	if (unlikely(ret < 0)) {
+		/*
+		 * If we have not yet seen any eligible child,
+		 * then let this error code replace -ECHILD.
+		 * A permission error will give the user a clue
+		 * to look for security policy problems, rather
+		 * than for mysterious wait bugs.
+		 */
+		if (*notask_error)
+			*notask_error = ret;
+	}
+
+	if (likely(!ptrace) && unlikely(p->ptrace)) {
+		/*
+		 * This child is hidden by ptrace.
+		 * We aren't allowed to see it now, but eventually we will.
+		 */
+		*notask_error = 0;
+		return 0;
+	}
+
+	if (p->exit_state == EXIT_DEAD)
+		return 0;
+
+	/*
+	 * We don't reap group leaders with subthreads.
+	 */
+	if (p->exit_state == EXIT_ZOMBIE && !delay_group_leader(p))
+		return wait_task_zombie(p, options, infop, stat_addr, ru);
+
+	/*
+	 * It's stopped or running now, so it might
+	 * later continue, exit, or stop again.
+	 */
+	*notask_error = 0;
+
+	if (task_is_stopped_or_traced(p))
+		return wait_task_stopped(ptrace, p, options,
+					 infop, stat_addr, ru);
+
+	return wait_task_continued(p, options, infop, stat_addr, ru);
+}
+
+/*
+ * Do the work of do_wait() for one thread in the group, @tsk.
+ *
+ * -ECHILD should be in *@notask_error before the first call.
+ * Returns nonzero for a final return, when we have unlocked tasklist_lock.
+ * Returns zero if the search for a child should continue; then
+ * *@notask_error is 0 if there were any eligible children,
+ * or another error from security_task_wait(), or still -ECHILD.
+ */
+static int do_wait_thread(struct task_struct *tsk, int *notask_error,
+			  enum pid_type type, struct pid *pid, int options,
+			  struct siginfo __user *infop, int __user *stat_addr,
+			  struct rusage __user *ru)
+{
+	struct task_struct *p;
+
+	list_for_each_entry(p, &tsk->children, sibling) {
+		/*
+		 * Do not consider detached threads.
+		 */
+		if (!task_detached(p)) {
+			int ret = wait_consider_task(tsk, 0, p, notask_error,
+						     type, pid, options,
+						     infop, stat_addr, ru);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int ptrace_do_wait(struct task_struct *tsk, int *notask_error,
+			  enum pid_type type, struct pid *pid, int options,
+			  struct siginfo __user *infop, int __user *stat_addr,
+			  struct rusage __user *ru)
+{
+	struct task_struct *p;
+
+	/*
+	 * Traditionally we see ptrace'd stopped tasks regardless of options.
+	 */
+	options |= WUNTRACED;
+
+	list_for_each_entry(p, &tsk->ptraced, ptrace_entry) {
+		int ret = wait_consider_task(tsk, 1, p, notask_error,
+					     type, pid, options,
+					     infop, stat_addr, ru);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
 static long do_wait(enum pid_type type, struct pid *pid, int options,
 		    struct siginfo __user *infop, int __user *stat_addr,
 		    struct rusage __user *ru)
 {
 	DECLARE_WAITQUEUE(wait, current);
 	struct task_struct *tsk;
-	int flag, retval;
+	int retval;
 
 	add_wait_queue(&current->signal->wait_chldexit,&wait);
 repeat:
-	/* If there is nothing that can match our critier just get out */
+	/*
+	 * If there is nothing that can match our critiera just get out.
+	 * We will clear @retval to zero if we see any child that might later
+	 * match our criteria, even if we are not able to reap it yet.
+	 */
 	retval = -ECHILD;
 	if ((type < PIDTYPE_MAX) && (!pid || hlist_empty(&pid->tasks[type])))
 		goto end;
 
-	/*
-	 * We will set this flag if we see any child that might later
-	 * match our criteria, even if we are not able to reap it yet.
-	 */
-	flag = retval = 0;
 	current->state = TASK_INTERRUPTIBLE;
 	read_lock(&tasklist_lock);
 	tsk = current;
 	do {
-		struct task_struct *p;
-
-		list_for_each_entry(p, &tsk->children, sibling) {
-			int ret = eligible_child(type, pid, options, p);
-			if (!ret)
-				continue;
-
-			if (unlikely(ret < 0)) {
-				retval = ret;
-			} else if (task_is_stopped_or_traced(p)) {
-				/*
-				 * It's stopped now, so it might later
-				 * continue, exit, or stop again.
-				 */
-				flag = 1;
-				if (!(p->ptrace & PT_PTRACED) &&
-				    !(options & WUNTRACED))
-					continue;
-
-				retval = wait_task_stopped(p,
-						(options & WNOWAIT), infop,
-						stat_addr, ru);
-			} else if (p->exit_state == EXIT_ZOMBIE &&
-					!delay_group_leader(p)) {
-				/*
-				 * We don't reap group leaders with subthreads.
-				 */
-				if (!likely(options & WEXITED))
-					continue;
-				retval = wait_task_zombie(p,
-						(options & WNOWAIT), infop,
-						stat_addr, ru);
-			} else if (p->exit_state != EXIT_DEAD) {
-				/*
-				 * It's running now, so it might later
-				 * exit, stop, or stop and then continue.
-				 */
-				flag = 1;
-				if (!unlikely(options & WCONTINUED))
-					continue;
-				retval = wait_task_continued(p,
-						(options & WNOWAIT), infop,
-						stat_addr, ru);
-			}
-			if (retval != 0) /* tasklist_lock released */
-				goto end;
-		}
-		if (!flag) {
-			list_for_each_entry(p, &tsk->ptrace_children,
-								ptrace_list) {
-				flag = eligible_child(type, pid, options, p);
-				if (!flag)
-					continue;
-				if (likely(flag > 0))
-					break;
-				retval = flag;
-				goto end;
-			}
+		int tsk_result = do_wait_thread(tsk, &retval,
+						type, pid, options,
+						infop, stat_addr, ru);
+		if (!tsk_result)
+			tsk_result = ptrace_do_wait(tsk, &retval,
+						    type, pid, options,
+						    infop, stat_addr, ru);
+		if (tsk_result) {
+			/*
+			 * tasklist_lock is unlocked and we have a final result.
+			 */
+			retval = tsk_result;
+			goto end;
 		}
+
 		if (options & __WNOTHREAD)
 			break;
 		tsk = next_thread(tsk);
@@ -1608,16 +1712,14 @@ repeat:
 	} while (tsk != current);
 	read_unlock(&tasklist_lock);
 
-	if (flag) {
-		if (options & WNOHANG)
-			goto end;
+	if (!retval && !(options & WNOHANG)) {
 		retval = -ERESTARTSYS;
-		if (signal_pending(current))
-			goto end;
-		schedule();
-		goto repeat;
+		if (!signal_pending(current)) {
+			schedule();
+			goto repeat;
+		}
 	}
-	retval = -ECHILD;
+
 end:
 	current->state = TASK_RUNNING;
 	remove_wait_queue(&current->signal->wait_chldexit,&wait);
diff --git a/kernel/fork.c b/kernel/fork.c
index 19908b2..df8fe06 100644  
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1124,8 +1124,8 @@ static struct task_struct *copy_process(
 	 */
 	p->group_leader = p;
 	INIT_LIST_HEAD(&p->thread_group);
-	INIT_LIST_HEAD(&p->ptrace_children);
-	INIT_LIST_HEAD(&p->ptrace_list);
+	INIT_LIST_HEAD(&p->ptrace_entry);
+	INIT_LIST_HEAD(&p->ptraced);
 
 	/* Now that the task is set up, run cgroup callbacks if
 	 * necessary. We need to run them before the task is visible
@@ -1197,7 +1197,7 @@ static struct task_struct *copy_process(
 	}
 
 	if (likely(p->pid)) {
-		add_parent(p);
+		list_add_tail(&p->sibling, &p->real_parent->children);
 		if (unlikely(p->ptrace & PT_PTRACED))
 			__ptrace_link(p, current->parent);
 
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 6c19e94..acf80a4 100644  
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -33,13 +33,9 @@
  */
 void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
 {
-	BUG_ON(!list_empty(&child->ptrace_list));
-	if (child->parent == new_parent)
-		return;
-	list_add(&child->ptrace_list, &child->parent->ptrace_children);
-	remove_parent(child);
+	BUG_ON(!list_empty(&child->ptrace_entry));
+	list_add(&child->ptrace_entry, &new_parent->ptraced);
 	child->parent = new_parent;
-	add_parent(child);
 }
  
 /*
@@ -73,12 +69,8 @@ void __ptrace_unlink(struct task_struct 
 	BUG_ON(!child->ptrace);
 
 	child->ptrace = 0;
-	if (ptrace_reparented(child)) {
-		list_del_init(&child->ptrace_list);
-		remove_parent(child);
-		child->parent = child->real_parent;
-		add_parent(child);
-	}
+	child->parent = child->real_parent;
+	list_del_init(&child->ptrace_entry);
 
 	if (task_is_traced(child))
 		ptrace_untrace(child);
@@ -492,14 +484,33 @@ int ptrace_traceme(void)
 	/*
 	 * Are we already being traced?
 	 */
+repeat:
 	task_lock(current);
 	if (!(current->ptrace & PT_PTRACED)) {
+		/*
+		 * See ptrace_attach() comments about the locking here.
+		 */
+		unsigned long flags;
+		if (!write_trylock_irqsave(&tasklist_lock, flags)) {
+			task_unlock(current);
+			do {
+				cpu_relax();
+			} while (!write_can_lock(&tasklist_lock));
+			goto repeat;
+		}
+
 		ret = security_ptrace(current->parent, current);
+
 		/*
 		 * Set the ptrace bit in the process ptrace flags.
+		 * Then link us on our parent's ptraced list.
 		 */
-		if (!ret)
+		if (!ret) {
 			current->ptrace |= PT_PTRACED;
+			__ptrace_link(current, current->real_parent);
+		}
+
+		write_unlock_irqrestore(&tasklist_lock, flags);
 	}
 	task_unlock(current);
 	return ret;

linux-2.6-tracehook.patch:

--- NEW FILE linux-2.6-tracehook.patch ---
 arch/Kconfig                      |   18 ++
 arch/ia64/Kconfig                 |    1 +
 arch/ia64/kernel/perfmon.c        |   11 +-
 arch/ia64/kernel/process.c        |   21 +-
 arch/ia64/kernel/ptrace.c         |   43 +--
 arch/ia64/kernel/signal.c         |    8 +
 arch/mips/kernel/irixelf.c        |    2 -
 arch/powerpc/Kconfig              |    1 +
 arch/powerpc/kernel/entry_32.S    |    4 +-
 arch/powerpc/kernel/entry_64.S    |    3 +-
 arch/powerpc/kernel/ptrace.c      |   33 +--
 arch/powerpc/kernel/signal.c      |   21 ++-
 arch/sparc64/Kconfig              |    1 +
 arch/sparc64/kernel/ptrace.c      |   24 +--
 arch/sparc64/kernel/rtrap.S       |    6 +-
 arch/sparc64/kernel/signal.c      |    5 +
 arch/x86/Kconfig                  |    1 +
 arch/x86/ia32/ia32_aout.c         |    6 -
 arch/x86/kernel/ptrace.c          |   31 +--
 arch/x86/kernel/signal_32.c       |   11 +-
 arch/x86/kernel/signal_64.c       |   49 +---
 fs/binfmt_aout.c                  |    6 -
 fs/binfmt_elf.c                   |    6 -
 fs/binfmt_elf_fdpic.c             |    7 -
 fs/binfmt_flat.c                  |    3 -
 fs/binfmt_som.c                   |    2 -
 fs/exec.c                         |   12 +-
 fs/proc/array.c                   |    9 +-
 fs/proc/base.c                    |   39 +++-
 include/asm-generic/syscall.h     |  137 +++++++++
 include/asm-ia64/thread_info.h    |    3 -
 include/asm-powerpc/ptrace.h      |    1 +
 include/asm-powerpc/signal.h      |    3 +-
 include/asm-powerpc/syscall.h     |   72 +++++
 include/asm-powerpc/thread_info.h |    5 +-
 include/asm-sparc64/ptrace.h      |    1 +
 include/asm-sparc64/syscall.h     |   75 +++++
 include/asm-sparc64/thread_info.h |    8 +-
 include/asm-x86/ptrace.h          |    5 +
 include/asm-x86/syscall.h         |  213 ++++++++++++++
 include/asm-x86/thread_info_32.h  |    2 +
 include/asm-x86/thread_info_64.h  |    5 +-
 include/linux/ptrace.h            |   72 +++++
 include/linux/sched.h             |   10 +-
 include/linux/tracehook.h         |  566 +++++++++++++++++++++++++++++++++++++
 kernel/exit.c                     |   53 ++---
 kernel/fork.c                     |   74 ++---
 kernel/kthread.c                  |    2 +-
 kernel/ptrace.c                   |    2 +-
 kernel/sched.c                    |   29 ++-
 kernel/signal.c                   |   99 ++++---
 lib/Makefile                      |    2 +
 lib/syscall.c                     |   75 +++++
 mm/nommu.c                        |    4 +-
 security/selinux/hooks.c          |   22 +--
 55 files changed, 1556 insertions(+), 368 deletions(-)

diff --git a/arch/Kconfig b/arch/Kconfig
index 3ea332b..1955fbd 100644  
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -39,3 +39,21 @@ config HAVE_KRETPROBES
 
 config HAVE_DMA_ATTRS
 	def_bool n
+
+#
+# An arch should select this if it provides all these things:
+#
+#	task_pt_regs()		in asm/processor.h or asm/ptrace.h
+#	arch_has_single_step()	if there is hardware single-step support
+#	arch_has_block_step()	if there is hardware block-step support
+#	arch_ptrace()		and not #define __ARCH_SYS_PTRACE
+#	compat_arch_ptrace()	and #define __ARCH_WANT_COMPAT_SYS_PTRACE
+#	asm/syscall.h		supplying asm-generic/syscall.h interface
+#	linux/regset.h		user_regset interfaces
+#	CORE_DUMP_USE_REGSET	#define'd in linux/elf.h
+#	TIF_SYSCALL_TRACE	calls tracehook_report_syscall_{entry,exit}
+#	TIF_NOTIFY_RESUME	calls tracehook_notify_resume()
+#	signal delivery		calls tracehook_signal_handler()
+#
+config HAVE_ARCH_TRACEHOOK
+	def_bool n
diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig
index 16be414..f23a54a 100644  
--- a/arch/ia64/Kconfig
+++ b/arch/ia64/Kconfig
@@ -19,6 +19,7 @@ config IA64
 	select HAVE_OPROFILE
 	select HAVE_KPROBES
 	select HAVE_KRETPROBES
+	select HAVE_ARCH_TRACEHOOK
 	select HAVE_DMA_ATTRS
 	select HAVE_KVM
 	default y
diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c
index 7714a97..189d525 100644  
--- a/arch/ia64/kernel/perfmon.c
+++ b/arch/ia64/kernel/perfmon.c
@@ -40,6 +40,7 @@
 #include <linux/capability.h>
 #include <linux/rcupdate.h>
 #include <linux/completion.h>
+#include <linux/tracehook.h>
 
 #include <asm/errno.h>
 #include <asm/intrinsics.h>
@@ -2626,7 +2627,7 @@ pfm_task_incompatible(pfm_context_t *ctx
 	/*
 	 * make sure the task is off any CPU
 	 */
-	wait_task_inactive(task);
+	wait_task_inactive(task, 0);
 
 	/* more to come... */
 
@@ -3684,7 +3685,7 @@ pfm_restart(pfm_context_t *ctx, void *ar
 
 		PFM_SET_WORK_PENDING(task, 1);
 
-		tsk_set_notify_resume(task);
+		set_notify_resume(task);
 
 		/*
 		 * XXX: send reschedule if task runs on another CPU
@@ -4774,7 +4775,7 @@ recheck:
 
 		UNPROTECT_CTX(ctx, flags);
 
-		wait_task_inactive(task);
+		wait_task_inactive(task, 0);
 
 		PROTECT_CTX(ctx, flags);
 
@@ -5044,8 +5045,6 @@ pfm_handle_work(void)
 
 	PFM_SET_WORK_PENDING(current, 0);
 
-	tsk_clear_notify_resume(current);
-
 	regs = task_pt_regs(current);
 
 	/*
@@ -5414,7 +5413,7 @@ pfm_overflow_handler(struct task_struct 
 			 * when coming from ctxsw, current still points to the
 			 * previous task, therefore we must work with task and not current.
 			 */
-			tsk_set_notify_resume(task);
+			set_notify_resume(task);
 		}
 		/*
 		 * defer until state is changed (shorten spin window). the context is locked
diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c
index a3a34b4..ff448fe 100644  
--- a/arch/ia64/kernel/process.c
+++ b/arch/ia64/kernel/process.c
@@ -156,21 +156,6 @@ show_regs (struct pt_regs *regs)
 		show_stack(NULL, NULL);
 }
 
-void tsk_clear_notify_resume(struct task_struct *tsk)
-{
-#ifdef CONFIG_PERFMON
-	if (tsk->thread.pfm_needs_checking)
-		return;
-#endif
-	if (test_ti_thread_flag(task_thread_info(tsk), TIF_RESTORE_RSE))
-		return;
-	clear_ti_thread_flag(task_thread_info(tsk), TIF_NOTIFY_RESUME);
-}
-
-/*
- * do_notify_resume_user():
- *	Called from notify_resume_user at entry.S, with interrupts disabled.
- */
 void
 do_notify_resume_user(sigset_t *unused, struct sigscratch *scr, long in_syscall)
 {
@@ -199,6 +184,11 @@ do_notify_resume_user(sigset_t *unused, 
 		ia64_do_signal(scr, in_syscall);
 	}
 
+	if (test_thread_flag(TIF_NOTIFY_RESUME)) {
+		clear_thread_flag(TIF_NOTIFY_RESUME);
+		tracehook_notify_resume(&scr->pt);
+	}
+
 	/* copy user rbs to kernel rbs */
 	if (unlikely(test_thread_flag(TIF_RESTORE_RSE))) {
 		local_irq_enable();	/* force interrupt enable */
diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c
index 2a9943b..3ba40a1 100644  
--- a/arch/ia64/kernel/ptrace.c
+++ b/arch/ia64/kernel/ptrace.c
@@ -22,6 +22,7 @@
 #include <linux/signal.h>
 #include <linux/regset.h>
 #include <linux/elf.h>
+#include <linux/tracehook.h>
[...2880 lines suppressed...]
+		do_group_exit(info->si_signo);
 		/* NOTREACHED */
 	}
 	spin_unlock_irq(&sighand->siglock);
@@ -1909,7 +1937,7 @@ void exit_signals(struct task_struct *ts
 out:
 	spin_unlock_irq(&tsk->sighand->siglock);
 
-	if (unlikely(group_stop)) {
+	if (unlikely(group_stop) && tracehook_notify_jctl(1, CLD_STOPPED)) {
 		read_lock(&tasklist_lock);
 		do_notify_parent_cldstop(tsk, CLD_STOPPED);
 		read_unlock(&tasklist_lock);
@@ -1921,7 +1949,6 @@ EXPORT_SYMBOL_GPL(dequeue_signal);
 EXPORT_SYMBOL(flush_signals);
 EXPORT_SYMBOL(force_sig);
 EXPORT_SYMBOL(kill_proc);
-EXPORT_SYMBOL(ptrace_notify);
 EXPORT_SYMBOL(send_sig);
 EXPORT_SYMBOL(send_sig_info);
 EXPORT_SYMBOL(sigprocmask);
@@ -2325,7 +2352,7 @@ int do_sigaction(int sig, struct k_sigac
 		 *   (for example, SIGCHLD), shall cause the pending signal to
 		 *   be discarded, whether or not it is blocked"
 		 */
-		if (__sig_ignored(t, sig)) {
+		if (sig_handler_ignored(sig_handler(t, sig), sig)) {
 			sigemptyset(&mask);
 			sigaddset(&mask, sig);
 			rm_from_queue_full(&mask, &t->signal->shared_pending);
diff --git a/lib/Makefile b/lib/Makefile
index 74b0cfb..62dfd16 100644  
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -72,6 +72,8 @@ lib-$(CONFIG_GENERIC_BUG) += bug.o
 
 obj-$(CONFIG_HAVE_LMB) += lmb.o
 
+obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o
+
 hostprogs-y	:= gen_crc32table
 clean-files	:= crc32table.h
 
diff --git a/lib/syscall.c b/lib/syscall.c
new file mode 100644
index ...a4f7067 100644  
--- /dev/null
+++ b/lib/syscall.c
@@ -0,0 +1,75 @@
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <asm/syscall.h>
+
+static int collect_syscall(struct task_struct *target, long *callno,
+			   unsigned long args[6], unsigned int maxargs,
+			   unsigned long *sp, unsigned long *pc)
+{
+	struct pt_regs *regs = task_pt_regs(target);
+	if (unlikely(!regs))
+		return -EAGAIN;
+
+	*sp = user_stack_pointer(regs);
+	*pc = instruction_pointer(regs);
+
+	*callno = syscall_get_nr(target, regs);
+	if (*callno != -1L && maxargs > 0)
+		syscall_get_arguments(target, regs, 0, maxargs, args);
+
+	return 0;
+}
+
+/**
+ * task_current_syscall - Discover what a blocked task is doing.
+ * @target:		thread to examine
+ * @callno:		filled with system call number or -1
+ * @args:		filled with @maxargs system call arguments
+ * @maxargs:		number of elements in @args to fill
+ * @sp:			filled with user stack pointer
+ * @pc:			filled with user PC
+ *
+ * If @target is blocked in a system call, returns zero with *@callno
+ * set to the the call's number and @args filled in with its arguments.
+ * Registers not used for system call arguments may not be available and
+ * it is not kosher to use &struct user_regset calls while the system
+ * call is still in progress.  Note we may get this result if @target
+ * has finished its system call but not yet returned to user mode, such
+ * as when it's stopped for signal handling or syscall exit tracing.
+ *
+ * If @target is blocked in the kernel during a fault or exception,
+ * returns zero with *@callno set to -1 and does not fill in @args.
+ * If so, it's now safe to examine @target using &struct user_regset
+ * get() calls as long as we're sure @target won't return to user mode.
+ *
+ * Returns -%EAGAIN if @target does not remain blocked.
+ *
+ * Returns -%EINVAL if @maxargs is too large (maximum is six).
+ */
+int task_current_syscall(struct task_struct *target, long *callno,
+			 unsigned long args[6], unsigned int maxargs,
+			 unsigned long *sp, unsigned long *pc)
+{
+	long state;
+	unsigned long ncsw;
+
+	if (unlikely(maxargs > 6))
+		return -EINVAL;
+
+	if (target == current)
+		return collect_syscall(target, callno, args, maxargs, sp, pc);
+
+	state = target->state;
+	if (unlikely(!state))
+		return -EAGAIN;
+
+	ncsw = wait_task_inactive(target, state);
+	if (unlikely(!ncsw) ||
+	    unlikely(collect_syscall(target, callno, args, maxargs, sp, pc)) ||
+	    unlikely(wait_task_inactive(target, state) != ncsw))
+		return -EAGAIN;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(task_current_syscall);
diff --git a/mm/nommu.c b/mm/nommu.c
index 4462b6a..5edccd9 100644  
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -22,7 +22,7 @@
 #include <linux/pagemap.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
-#include <linux/ptrace.h>
+#include <linux/tracehook.h>
 #include <linux/blkdev.h>
 #include <linux/backing-dev.h>
 #include <linux/mount.h>
@@ -745,7 +745,7 @@ static unsigned long determine_vm_flags(
 	 * it's being traced - otherwise breakpoints set in it may interfere
 	 * with another untraced process
 	 */
-	if ((flags & MAP_PRIVATE) && (current->ptrace & PT_PTRACED))
+	if ((flags & MAP_PRIVATE) && tracehook_expect_breakpoints(current))
 		vm_flags &= ~VM_MAYSHARE;
 
 	return vm_flags;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 1c864c0..f92c6af 100644  
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -24,7 +24,7 @@
 
 #include <linux/init.h>
 #include <linux/kernel.h>
-#include <linux/ptrace.h>
+#include <linux/tracehook.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/security.h>
@@ -1910,22 +1910,6 @@ static int selinux_vm_enough_memory(stru
 	return __vm_enough_memory(mm, pages, cap_sys_admin);
 }
 
-/**
- * task_tracer_task - return the task that is tracing the given task
- * @task:		task to consider
- *
- * Returns NULL if noone is tracing @task, or the &struct task_struct
- * pointer to its tracer.
- *
- * Must be called under rcu_read_lock().
- */
-static struct task_struct *task_tracer_task(struct task_struct *task)
-{
-	if (task->ptrace & PT_PTRACED)
-		return rcu_dereference(task->parent);
-	return NULL;
-}
-
 /* binprm security operations */
 
 static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
@@ -2177,7 +2161,7 @@ static void selinux_bprm_apply_creds(str
 			u32 ptsid = 0;
 
 			rcu_read_lock();
-			tracer = task_tracer_task(current);
+			tracer = tracehook_tracer_task(current);
 			if (likely(tracer != NULL)) {
 				sec = tracer->security;
 				ptsid = sec->sid;
@@ -5205,7 +5189,7 @@ static int selinux_setprocattr(struct ta
 		   Otherwise, leave SID unchanged and fail. */
 		task_lock(p);
 		rcu_read_lock();
-		tracer = task_tracer_task(p);
+		tracer = tracehook_tracer_task(p);
 		if (tracer != NULL) {
 			struct task_security_struct *ptsec = tracer->security;
 			u32 ptsid = ptsec->sid;


Index: config-generic
===================================================================
RCS file: /cvs/pkgs/rpms/kernel/devel/config-generic,v
retrieving revision 1.123
retrieving revision 1.124
diff -u -r1.123 -r1.124
--- config-generic	8 Jul 2008 20:33:41 -0000	1.123
+++ config-generic	9 Jul 2008 03:37:06 -0000	1.124
@@ -3432,7 +3432,7 @@
 CONFIG_UNUSED_SYMBOLS=y
 
 CONFIG_UTRACE=y
-CONFIG_PTRACE=y
+CONFIG_UTRACE_PTRACE=y
 
 CONFIG_KPROBES=y
 


Index: config-ia64
===================================================================
RCS file: /cvs/pkgs/rpms/kernel/devel/config-ia64,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- config-ia64	12 Jul 2007 19:15:37 -0000	1.1
+++ config-ia64	9 Jul 2008 03:37:06 -0000	1.2
@@ -1,3 +1,4 @@
 CONFIG_CRASH_DUMP=y
 CONFIG_PROC_VMCORE=y
 
+CONFIG_UTRACE=n


Index: kernel.spec
===================================================================
RCS file: /cvs/pkgs/rpms/kernel/devel/kernel.spec,v
retrieving revision 1.744
retrieving revision 1.745
diff -u -r1.744 -r1.745
--- kernel.spec	8 Jul 2008 21:29:35 -0000	1.744
+++ kernel.spec	9 Jul 2008 03:37:06 -0000	1.745
@@ -571,7 +571,9 @@
 
 Patch10: linux-2.6-hotfixes.patch
 
-Patch21: linux-2.6-utrace.patch
+Patch21: linux-2.6-ptrace-cleanup.patch
+Patch22: linux-2.6-tracehook.patch
+Patch23: linux-2.6-utrace.patch
 
 Patch41: linux-2.6-sysrq-c.patch
 Patch42: linux-2.6-x86-tune-generic.patch
@@ -1004,9 +1006,9 @@
 ApplyPatch linux-2.6-hotfixes.patch
 
 # Roland's utrace ptrace replacement.
-%ifnarch ia64
-#ApplyPatch linux-2.6-utrace.patch
-%endif
+ApplyPatch linux-2.6-ptrace-cleanup.patch
+ApplyPatch linux-2.6-tracehook.patch
+ApplyPatch linux-2.6-utrace.patch
 
 # enable sysrq-c on all kernels, not only kexec
 ApplyPatch linux-2.6-sysrq-c.patch
@@ -1792,6 +1794,9 @@
 %kernel_variant_files -a /%{image_install_path}/xen*-%{KVERREL}.xen -e /etc/ld.so.conf.d/kernelcap-%{KVERREL}.xen.conf %{with_xen} xen
 
 %changelog
+* Tue Jul  8 2008 Roland McGrath <roland at redhat.com>
+- new bleeding-edge utrace code
+
 * Tue Jul 08 2008 Eric Sandeen <sandeen at redhat.com>
 - Fix reiserfs list corruption (#453699)
 

linux-2.6-utrace.patch:

View full diff with command:
/usr/bin/cvs -f diff  -kk -u -N -r 1.78 -r 1.79 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.78
retrieving revision 1.79
diff -u -r1.78 -r1.79
--- linux-2.6-utrace.patch	16 May 2008 21:06:38 -0000	1.78
+++ linux-2.6-utrace.patch	9 Jul 2008 03:37:06 -0000	1.79
@@ -1,88 +1,20 @@
- Documentation/DocBook/Makefile      |    2 +-
- Documentation/DocBook/utrace.tmpl   |   23 +
- Documentation/utrace.txt            |  579 +++++++++
- arch/alpha/kernel/ptrace.c          |   15 +-
- arch/arm/kernel/ptrace.c            |    5 +-
- arch/avr32/kernel/ptrace.c          |    5 +-
- arch/blackfin/kernel/ptrace.c       |    5 +-
- arch/frv/kernel/ptrace.c            |    3 +-
- arch/h8300/kernel/ptrace.c          |    3 +-
- arch/ia64/kernel/process.c          |    2 +-
- arch/ia64/kernel/ptrace.c           |   17 +-
- arch/m32r/kernel/ptrace.c           |    7 +-
- arch/m68k/kernel/ptrace.c           |    5 +-
- arch/m68knommu/kernel/ptrace.c      |    3 +-
- arch/mips/kernel/irixelf.c          |    2 -
- arch/mips/kernel/ptrace.c           |    5 +-
- arch/mn10300/kernel/ptrace.c        |    3 +-
- arch/parisc/kernel/ptrace.c         |    7 +-
- arch/powerpc/kernel/entry_32.S      |    4 +-
- arch/powerpc/kernel/process.c       |    5 -
- arch/powerpc/kernel/ptrace.c        |   38 +-
- arch/powerpc/kernel/ptrace32.c      |    8 +-
- arch/powerpc/kernel/signal.c        |   20 +-
- arch/powerpc/kernel/signal_32.c     |   21 +-
- arch/powerpc/kernel/sys_ppc32.c     |    5 -
- arch/s390/kernel/entry.S            |   14 +-
- arch/s390/kernel/entry64.S          |   12 +-
- arch/sh/kernel/ptrace_32.c          |    5 +-
- arch/sh/kernel/ptrace_64.c          |    5 +-
- arch/sparc/kernel/ptrace.c          |    5 +-
- arch/sparc64/kernel/binfmt_aout32.c |    2 -
- arch/sparc64/kernel/ptrace.c        |   35 +-
- arch/um/kernel/ptrace.c             |    3 +-
- arch/v850/kernel/ptrace.c           |    3 +-
- arch/x86/ia32/ia32_aout.c           |    6 -
- arch/x86/ia32/ia32_signal.c         |   12 +-
- arch/x86/ia32/ia32entry.S           |   14 +-
- arch/x86/ia32/sys_ia32.c            |    5 -
- arch/x86/kernel/entry_64.S          |    8 +-
- arch/x86/kernel/ptrace.c            |  159 +--
- arch/x86/kernel/signal_32.c         |   56 +-
- arch/x86/kernel/signal_64.c         |   49 +-
- arch/x86/kernel/step.c              |   22 +-
- arch/x86/kernel/vm86_32.c           |    9 +-
- arch/xtensa/kernel/ptrace.c         |    6 +-
- fs/binfmt_aout.c                    |    6 -
- fs/binfmt_elf.c                     |    6 -
- fs/binfmt_elf_fdpic.c               |    7 -
- fs/binfmt_flat.c                    |    3 -
- fs/binfmt_som.c                     |    2 -
- fs/compat.c                         |   14 +-
- fs/eventpoll.c                      |    7 +-
- fs/exec.c                           |   12 +-
- fs/proc/array.c                     |    9 +-
- fs/proc/base.c                      |   43 +-
- fs/select.c                         |   12 +-
- include/asm-ia64/thread_info.h      |    5 +-
- include/asm-powerpc/thread_info.h   |   17 +-
- include/asm-s390/thread_info.h      |    2 +-
- include/asm-x86/ptrace.h            |    2 +
- include/asm-x86/thread_info_32.h    |   13 +-
- include/asm-x86/thread_info_64.h    |   13 +-
- include/linux/compat.h              |    5 +-
- include/linux/init_task.h           |    3 -
- include/linux/ptrace.h              |   70 +-
- include/linux/sched.h               |   38 +-
- include/linux/thread_info.h         |   27 +-
- include/linux/tracehook.h           |  507 ++++++++
- include/linux/utrace.h              |  544 ++++++++
- init/Kconfig                        |   29 +
- kernel/Makefile                     |    1 +
- kernel/compat.c                     |    3 +-
- kernel/exit.c                       |  403 +++----
- kernel/fork.c                       |   72 +-
- kernel/ptrace.c                     | 2129 +++++++++++++++++++++++++-------
- kernel/signal.c                     |  285 +----
- kernel/sys_ni.c                     |    4 +
- kernel/utrace.c                     | 2377 +++++++++++++++++++++++++++++++++++
- mm/nommu.c                          |    4 +-
- security/selinux/hooks.c            |   59 +-
- security/selinux/include/objsec.h   |    1 -
- 81 files changed, 6535 insertions(+), 1421 deletions(-)
+ Documentation/DocBook/Makefile    |    2 +-
+ Documentation/DocBook/utrace.tmpl |   21 +
+ Documentation/utrace.txt          |  579 ++++++++++
+ fs/proc/array.c                   |    3 +
+ include/linux/ptrace.h            |   15 +
+ include/linux/sched.h             |    6 +
+ include/linux/tracehook.h         |   64 ++
+ include/linux/utrace.h            |  504 +++++++++
+ init/Kconfig                      |   28 +
+ kernel/Makefile                   |    1 +
+ kernel/ptrace.c                   |  438 ++++++++-
+ kernel/signal.c                   |   14 +-
+ kernel/utrace.c                   | 2156 +++++++++++++++++++++++++++++++++++++
+ 13 files changed, 3827 insertions(+), 4 deletions(-)
 
 diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile
-index 300e170..7cf3afc 100644  
+index 0eb0d02..49a78b7 100644  
 --- a/Documentation/DocBook/Makefile
 +++ b/Documentation/DocBook/Makefile
 @@ -7,7 +7,7 @@
@@ -92,14 +24,14 @@
 -	    kernel-hacking.xml kernel-locking.xml deviceiobook.xml \
 +	    kernel-hacking.xml kernel-locking.xml deviceiobook.xml utrace.xml \
  	    procfs-guide.xml writing_usb_driver.xml networking.xml \
- 	    kernel-api.xml filesystems.xml lsm.xml usb.xml \
+ 	    kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \
  	    gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
 diff --git a/Documentation/DocBook/utrace.tmpl b/Documentation/DocBook/utrace.tmpl
 new file mode 100644
-index ...f185043 100644  
+index ...8dfaa91 100644  
 --- /dev/null
 +++ b/Documentation/DocBook/utrace.tmpl
-@@ -0,0 +1,23 @@
+@@ -0,0 +1,21 @@
 +<?xml version="1.0" encoding="UTF-8"?>
 +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
 +	"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
@@ -117,9 +49,7 @@
 +    </chapter>
 +
 +<chapter><title>Machine state access via utrace</title>
-+!Finclude/linux/regset.h struct user_regset
-+!Finclude/linux/regset.h struct user_regset_view
-+!Finclude/linux/regset.h task_user_regset_view
++!Finclude/linux/regset.h
 +    </chapter>
 +
 +</book>
@@ -708,6935 +638,1404 @@
 +sure which event callbacks it will or won't see after utrace_set_flags
 +has returned.  By checking for errors, it can know whether to clean up
 +its data structures immediately or to let its callbacks do the work.
-diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c
-index 1e9ad52..7dc30f5 100644  
---- a/arch/alpha/kernel/ptrace.c
-+++ b/arch/alpha/kernel/ptrace.c
-@@ -260,11 +260,12 @@ void ptrace_disable(struct task_struct *
- 	ptrace_cancel_bpt(child);
- }
- 
--long arch_ptrace(struct task_struct *child, long request, long addr, long data)
-+int arch_ptrace(struct task_struct *child, long request, long addr, long data,
-+		long *retval)
- {
- 	unsigned long tmp;
- 	size_t copied;
--	long ret;
-+	int ret;
- 
- 	switch (request) {
- 	/* When I and D space are separate, these will need to be fixed.  */
-@@ -275,14 +276,14 @@ long arch_ptrace(struct task_struct *chi
- 		if (copied != sizeof(tmp))
- 			break;
- 		
--		force_successful_syscall_return();
--		ret = tmp;
-+		ret = 0;
-+		*retval = tmp;
- 		break;
- 
- 	/* Read register number ADDR. */
- 	case PTRACE_PEEKUSR:
--		force_successful_syscall_return();
--		ret = get_reg(child, addr);
-+		ret = 0;
-+		*retval = get_reg(child, addr);
- 		DBG(DBG_MEM, ("peek $%ld->%#lx\n", addr, ret));
- 		break;
- 
-@@ -343,7 +344,7 @@ long arch_ptrace(struct task_struct *chi
- 		break;
- 
- 	default:
--		ret = ptrace_request(child, request, addr, data);
-+		ret = 1;
- 		break;
- 	}
- 	return ret;
-diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
-index 4b05dc5..938be29 100644  
---- a/arch/arm/kernel/ptrace.c
-+++ b/arch/arm/kernel/ptrace.c
-@@ -655,7 +655,8 @@ static int ptrace_setcrunchregs(struct t
- }
- #endif
- 
[...11835 lines suppressed...]
 +{
-+	struct utrace *utrace = tsk->utrace;
-+	struct list_head *pos, *next;
-+	struct utrace_attached_engine *engine;
++	struct utrace *utrace = task->utrace;
++	struct utrace_attached_engine *engine, *next;
 +	const struct utrace_engine_ops *ops;
 +	int unsafe = 0;
 +
-+	/* XXX must change for sharing */
-+	list_for_each_safe_rcu(pos, next, &utrace->engines) {
-+		engine = list_entry(pos, struct utrace_attached_engine, entry);
++	list_for_each_entry_safe(engine, next, &utrace->engines, entry) {
 +		ops = rcu_dereference(engine->ops);
 +		if (ops->unsafe_exec)
-+			unsafe |= (*ops->unsafe_exec)(engine, tsk);
++			unsafe |= (*ops->unsafe_exec)(engine, task);
 +	}
 +
 +	return unsafe;
 +}
-diff --git a/mm/nommu.c b/mm/nommu.c
-index 5d8ae08..515beac 100644  
---- a/mm/nommu.c
-+++ b/mm/nommu.c
-@@ -22,7 +22,7 @@
- #include <linux/pagemap.h>
- #include <linux/slab.h>
- #include <linux/vmalloc.h>
--#include <linux/ptrace.h>
-+#include <linux/tracehook.h>
- #include <linux/blkdev.h>
- #include <linux/backing-dev.h>
- #include <linux/mount.h>
-@@ -734,7 +734,7 @@ static unsigned long determine_vm_flags(
- 	 * it's being traced - otherwise breakpoints set in it may interfere
- 	 * with another untraced process
- 	 */
--	if ((flags & MAP_PRIVATE) && (current->ptrace & PT_PTRACED))
-+	if ((flags & MAP_PRIVATE) && tracehook_expect_breakpoints(current))
- 		vm_flags &= ~VM_MAYSHARE;
- 
- 	return vm_flags;
-diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
-index d39b59c..43cb7c3 100644  
---- a/security/selinux/hooks.c
-+++ b/security/selinux/hooks.c
-@@ -24,7 +24,7 @@
- 
- #include <linux/init.h>
- #include <linux/kernel.h>
--#include <linux/ptrace.h>
-+#include <linux/tracehook.h>
- #include <linux/errno.h>
- #include <linux/sched.h>
- #include <linux/security.h>
-@@ -162,7 +162,7 @@ static int task_alloc_security(struct ta
- 		return -ENOMEM;
- 
- 	tsec->task = task;
--	tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED;
-+	tsec->osid = tsec->sid = SECINITSID_UNLABELED;
- 	task->security = tsec;
- 
- 	return 0;
-@@ -1645,19 +1645,13 @@ static inline u32 file_to_av(struct file
- 
- static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
- {
--	struct task_security_struct *psec = parent->security;
--	struct task_security_struct *csec = child->security;
- 	int rc;
- 
- 	rc = secondary_ops->ptrace(parent,child);
- 	if (rc)
- 		return rc;
- 
--	rc = task_has_perm(parent, child, PROCESS__PTRACE);
--	/* Save the SID of the tracing process for later use in apply_creds. */
--	if (!(child->ptrace & PT_PTRACED) && !rc)
--		csec->ptrace_sid = psec->sid;
--	return rc;
-+	return task_has_perm(parent, child, PROCESS__PTRACE);
- }
- 
- static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
-@@ -2126,12 +2120,25 @@ static void selinux_bprm_apply_creds(str
- 		/* Check for ptracing, and update the task SID if ok.
- 		   Otherwise, leave SID unchanged and kill. */
- 		if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
--			rc = avc_has_perm(tsec->ptrace_sid, sid,
--					  SECCLASS_PROCESS, PROCESS__PTRACE,
--					  NULL);
--			if (rc) {
--				bsec->unsafe = 1;
--				return;
-+			struct task_struct *tracer;
-+			struct task_security_struct *sec;
-+			u32 ptsid = 0;
-+
-+			rcu_read_lock();
-+			tracer = tracehook_tracer_task(current);
-+			if (likely(tracer != NULL)) {
-+				sec = tracer->security;
-+				ptsid = sec->sid;
-+			}
-+			rcu_read_unlock();
 +
-+			if (ptsid != 0) {
-+				rc = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
-+						  PROCESS__PTRACE, NULL);
-+				if (rc) {
-+					bsec->unsafe = 1;
-+					return;
-+				}
- 			}
- 		}
- 		tsec->sid = sid;
-@@ -2214,7 +2221,7 @@ static void selinux_bprm_post_apply_cred
- 
- 	/* Wake up the parent if it is waiting so that it can
- 	   recheck wait permission to the new task SID. */
--	wake_up_interruptible(&current->parent->signal->wait_chldexit);
-+	wake_up_interruptible(&current->real_parent->signal->wait_chldexit);
- }
- 
- /* superblock security operations */
-@@ -3087,11 +3094,6 @@ static int selinux_task_alloc_security(s
- 	tsec2->keycreate_sid = tsec1->keycreate_sid;
- 	tsec2->sockcreate_sid = tsec1->sockcreate_sid;
- 
--	/* Retain ptracer SID across fork, if any.
--	   This will be reset by the ptrace hook upon any
--	   subsequent ptrace_attach operations. */
--	tsec2->ptrace_sid = tsec1->ptrace_sid;
--
- 	return 0;
- }
- 
-@@ -5057,6 +5059,7 @@ static int selinux_setprocattr(struct ta
- 			       char *name, void *value, size_t size)
- {
- 	struct task_security_struct *tsec;
-+	struct task_struct *tracer;
- 	u32 sid = 0;
- 	int error;
- 	char *str = value;
-@@ -5145,18 +5148,24 @@ static int selinux_setprocattr(struct ta
- 		/* Check for ptracing, and update the task SID if ok.
- 		   Otherwise, leave SID unchanged and fail. */
- 		task_lock(p);
--		if (p->ptrace & PT_PTRACED) {
--			error = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
-+		rcu_read_lock();
-+		tracer = tracehook_tracer_task(p);
-+		if (tracer != NULL) {
-+			struct task_security_struct *ptsec = tracer->security;
-+			u32 ptsid = ptsec->sid;
-+			rcu_read_unlock();
-+			error = avc_has_perm_noaudit(ptsid, sid,
- 						     SECCLASS_PROCESS,
- 						     PROCESS__PTRACE, 0, &avd);
- 			if (!error)
- 				tsec->sid = sid;
- 			task_unlock(p);
--			avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
-+			avc_audit(ptsid, sid, SECCLASS_PROCESS,
- 				  PROCESS__PTRACE, &avd, error, NULL);
- 			if (error)
- 				return error;
- 		} else {
-+			rcu_read_unlock();
- 			tsec->sid = sid;
- 			task_unlock(p);
- 		}
-diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
-index c6c2bb4..1db31c2 100644  
---- a/security/selinux/include/objsec.h
-+++ b/security/selinux/include/objsec.h
-@@ -35,7 +35,6 @@ struct task_security_struct {
- 	u32 create_sid;      /* fscreate SID */
- 	u32 keycreate_sid;   /* keycreate SID */
- 	u32 sockcreate_sid;  /* fscreate SID */
--	u32 ptrace_sid;      /* SID of ptrace parent */
- };
- 
- struct inode_security_struct {
++/*
++ * Called with rcu_read_lock() held.
++ */
++void task_utrace_proc_status(struct seq_file *m, struct task_struct *p)
++{
++	struct utrace *utrace = rcu_dereference(p->utrace);
++	if (unlikely(utrace))
++		seq_printf(m, "Utrace: %lx%s%s%s\n",
++			   p->utrace_flags,
++			   utrace->stopped ? " (stopped)" : "",
++			   utrace->report ? " (report)" : "",
++			   utrace->interrupt ? " (interrupt)" : "");
++}




More information about the scm-commits mailing list