ptrace: fix race between ptrace_resume() and wait_task_stopped()
ptrace_resume() is called when the tracee is still __TASK_TRACED.  We set
tracee->exit_code and then wake_up_state() changes tracee->state.  If the
tracer's sub-thread does wait() in between, task_stopped_code(ptrace => T)
wrongly looks like another report from tracee.
This confuses debugger, and since wait_task_stopped() clears ->exit_code
the tracee can miss a signal.
Test-case:
	#include <stdio.h>
	#include <unistd.h>
	#include <sys/wait.h>
	#include <sys/ptrace.h>
	#include <pthread.h>
	#include <assert.h>
	int pid;
	void *waiter(void *arg)
	{
		int stat;
		for (;;) {
			assert(pid == wait(&stat));
			assert(WIFSTOPPED(stat));
			if (WSTOPSIG(stat) == SIGHUP)
				continue;
			assert(WSTOPSIG(stat) == SIGCONT);
			printf("ERR! extra/wrong report:%x\n", stat);
		}
	}
	int main(void)
	{
		pthread_t thread;
		pid = fork();
		if (!pid) {
			assert(ptrace(PTRACE_TRACEME, 0,0,0) == 0);
			for (;;)
				kill(getpid(), SIGHUP);
		}
		assert(pthread_create(&thread, NULL, waiter, NULL) == 0);
		for (;;)
			ptrace(PTRACE_CONT, pid, 0, SIGCONT);
		return 0;
	}
Note for stable: the bug is very old, but without 9899d11f "ptrace:
ensure arch_ptrace/ptrace_request can never race with SIGKILL" the fix
should use lock_task_sighand(child).
Signed-off-by:  Oleg Nesterov <oleg@redhat.com>
Reported-by:
Oleg Nesterov <oleg@redhat.com>
Reported-by:  Pavel Labath <labath@google.com>
Tested-by:
Pavel Labath <labath@google.com>
Tested-by:  Pavel Labath <labath@google.com>
Cc: <stable@vger.kernel.org>
Signed-off-by:
Pavel Labath <labath@google.com>
Cc: <stable@vger.kernel.org>
Signed-off-by:  Andrew Morton <akpm@linux-foundation.org>
Signed-off-by:
Andrew Morton <akpm@linux-foundation.org>
Signed-off-by:  Linus Torvalds <torvalds@linux-foundation.org>
Linus Torvalds <torvalds@linux-foundation.org>
Loading
Please register or sign in to comment
