linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH RESEND] ptrace: make ptrace() fail if the tracee changed its pid unexpectedly
@ 2021-04-26 15:45 Oleg Nesterov
  2021-04-26 21:04 ` Mathieu Desnoyers
  0 siblings, 1 reply; 4+ messages in thread
From: Oleg Nesterov @ 2021-04-26 15:45 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Eric W. Biederman, Eugene Syromiatnikov, Jan Kratochvil,
	Linus Torvalds, Mathieu Desnoyers, Michael Kerrisk, Pedro Alves,
	Simon Marchi, linux-kernel

Suppose we have 2 threads, the group-leader L and a sub-theread T,
both parked in ptrace_stop(). Debugger tries to resume both threads
and does

	ptrace(PTRACE_CONT, T);
	ptrace(PTRACE_CONT, L);

If the sub-thread T execs in between, the 2nd PTRACE_CONT doesn not
resume the old leader L, it resumes the post-exec thread T which was
actually now stopped in PTHREAD_EVENT_EXEC. In this case the
PTHREAD_EVENT_EXEC event is lost, and the tracer can't know that the
tracee changed its pid.

This patch makes ptrace() fail in this case until debugger does wait()
and consumes PTHREAD_EVENT_EXEC which reports old_pid. This affects all
ptrace requests except the "asynchronous" PTRACE_INTERRUPT/KILL.

The patch doesn't add the new PTRACE_ option to not complicate the API,
and I _hope_ this won't cause any noticeable regression:

	- If debugger uses PTRACE_O_TRACEEXEC and the thread did an exec
	  and the tracer does a ptrace request without having consumed
	  the exec event, it's 100% sure that the thread the ptracer
	  thinks it is targeting does not exist anymore, or isn't the
	  same as the one it thinks it is targeting.

	- To some degree this patch adds nothing new. In the scenario
	  above ptrace(L) can fail with -ESRCH if it is called after the
	  execing sub-thread wakes the leader up and before it "steals"
	  the leader's pid.

Test-case:

	#include <stdio.h>
	#include <unistd.h>
	#include <signal.h>
	#include <sys/ptrace.h>
	#include <sys/wait.h>
	#include <errno.h>
	#include <pthread.h>
	#include <assert.h>

	void *tf(void *arg)
	{
		execve("/usr/bin/true", NULL, NULL);
		assert(0);

		return NULL;
	}

	int main(void)
	{
		int leader = fork();
		if (!leader) {
			kill(getpid(), SIGSTOP);

			pthread_t th;
			pthread_create(&th, NULL, tf, NULL);
			for (;;)
				pause();

			return 0;
		}

		waitpid(leader, NULL, WSTOPPED);

		ptrace(PTRACE_SEIZE, leader, 0,
				PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC);
		waitpid(leader, NULL, 0);

		ptrace(PTRACE_CONT, leader, 0,0);
		waitpid(leader, NULL, 0);

		int status, thread = waitpid(-1, &status, 0);
		assert(thread > 0 && thread != leader);
		assert(status == 0x80137f);

		ptrace(PTRACE_CONT, thread, 0,0);
		/*
		 * waitid() because waitpid(leader, &status, WNOWAIT) does not
		 * report status. Why ????
		 *
		 * Why WEXITED? because we have another kernel problem connected
		 * to mt-exec.
		 */
		siginfo_t info;
		assert(waitid(P_PID, leader, &info, WSTOPPED|WEXITED|WNOWAIT) == 0);
		assert(info.si_pid == leader && info.si_status == 0x0405);

		/* OK, it sleeps in ptrace(PTRACE_EVENT_EXEC == 0x04) */
		assert(ptrace(PTRACE_CONT, leader, 0,0) == -1);
		assert(errno == ESRCH);

		assert(leader == waitpid(leader, &status, WNOHANG));
		assert(status == 0x04057f);

		assert(ptrace(PTRACE_CONT, leader, 0,0) == 0);

		return 0;
	}

Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Reported-by: Simon Marchi <simon.marchi@efficios.com>
Acked-by: "Eric W. Biederman" <ebiederm@xmission.com>
Acked-by: Pedro Alves <palves@redhat.com>
Acked-by: Simon Marchi <simon.marchi@efficios.com>
Acked-by: Jan Kratochvil <jan.kratochvil@redhat.com>
---
 kernel/ptrace.c | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 43d6179508d6..1037251ae4a5 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -169,6 +169,27 @@ void __ptrace_unlink(struct task_struct *child)
 	spin_unlock(&child->sighand->siglock);
 }
 
+static bool looks_like_a_spurious_pid(struct task_struct *task)
+{
+	int pid;
+
+	if (task->exit_code != ((PTRACE_EVENT_EXEC << 8) | SIGTRAP))
+		return false;
+
+	rcu_read_lock();
+	pid = task_pid_nr_ns(task, task_active_pid_ns(task->parent));
+	rcu_read_unlock();
+
+	if (pid == task->ptrace_message)
+		return false;
+	/*
+	 * The tracee changed its pid but the PTRACE_EVENT_EXEC event
+	 * was not wait()'ed, most probably debugger targets the old
+	 * leader which was destroyed in de_thread().
+	 */
+	return true;
+}
+
 /* Ensure that nothing can wake it up, even SIGKILL */
 static bool ptrace_freeze_traced(struct task_struct *task)
 {
@@ -179,7 +200,8 @@ static bool ptrace_freeze_traced(struct task_struct *task)
 		return ret;
 
 	spin_lock_irq(&task->sighand->siglock);
-	if (task_is_traced(task) && !__fatal_signal_pending(task)) {
+	if (task_is_traced(task) && !looks_like_a_spurious_pid(task) &&
+	    !__fatal_signal_pending(task)) {
 		task->state = __TASK_TRACED;
 		ret = true;
 	}
-- 
2.25.1.362.g51ebf55



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH RESEND] ptrace: make ptrace() fail if the tracee changed its pid unexpectedly
  2021-04-26 15:45 [PATCH RESEND] ptrace: make ptrace() fail if the tracee changed its pid unexpectedly Oleg Nesterov
@ 2021-04-26 21:04 ` Mathieu Desnoyers
  2021-04-27  6:26   ` Oleg Nesterov
  0 siblings, 1 reply; 4+ messages in thread
From: Mathieu Desnoyers @ 2021-04-26 21:04 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Andrew Morton, Eric W. Biederman, Eugene Syromiatnikov,
	Jan Kratochvil, Linus Torvalds, Michael Kerrisk, Pedro Alves,
	Simon Marchi, linux-kernel

----- On Apr 26, 2021, at 11:45 AM, Oleg Nesterov oleg@redhat.com wrote:

> Suppose we have 2 threads, the group-leader L and a sub-theread T,
> both parked in ptrace_stop(). Debugger tries to resume both threads
> and does
> 
>	ptrace(PTRACE_CONT, T);
>	ptrace(PTRACE_CONT, L);
> 
> If the sub-thread T execs in between, the 2nd PTRACE_CONT doesn not
> resume the old leader L, it resumes the post-exec thread T which was
> actually now stopped in PTHREAD_EVENT_EXEC. In this case the
> PTHREAD_EVENT_EXEC event is lost, and the tracer can't know that the
> tracee changed its pid.
> 
> This patch makes ptrace() fail in this case until debugger does wait()
> and consumes PTHREAD_EVENT_EXEC which reports old_pid. This affects all
> ptrace requests except the "asynchronous" PTRACE_INTERRUPT/KILL.
> 
> The patch doesn't add the new PTRACE_ option to not complicate the API,
> and I _hope_ this won't cause any noticeable regression:
> 
>	- If debugger uses PTRACE_O_TRACEEXEC and the thread did an exec
>	  and the tracer does a ptrace request without having consumed
>	  the exec event, it's 100% sure that the thread the ptracer
>	  thinks it is targeting does not exist anymore, or isn't the
>	  same as the one it thinks it is targeting.
> 
>	- To some degree this patch adds nothing new. In the scenario
>	  above ptrace(L) can fail with -ESRCH if it is called after the
>	  execing sub-thread wakes the leader up and before it "steals"
>	  the leader's pid.

Hi Oleg,

Is this something that should also target stable kernels ? AFAIU this change
won't break debuggers more that they are already in this scenario. Or maybe
it makes them fail in more obvious ways ?

Thanks,

Mathieu
 
-- 
Mathieu Desnoyers
EfficiOS Inc.
http://www.efficios.com

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH RESEND] ptrace: make ptrace() fail if the tracee changed its pid unexpectedly
  2021-04-26 21:04 ` Mathieu Desnoyers
@ 2021-04-27  6:26   ` Oleg Nesterov
  2021-04-27 13:31     ` Mathieu Desnoyers
  0 siblings, 1 reply; 4+ messages in thread
From: Oleg Nesterov @ 2021-04-27  6:26 UTC (permalink / raw)
  To: Mathieu Desnoyers
  Cc: Andrew Morton, Eric W. Biederman, Eugene Syromiatnikov,
	Jan Kratochvil, Linus Torvalds, Michael Kerrisk, Pedro Alves,
	Simon Marchi, linux-kernel

Hi Mathieu,

On 04/26, Mathieu Desnoyers wrote:
>
> > The patch doesn't add the new PTRACE_ option to not complicate the API,
> > and I _hope_ this won't cause any noticeable regression:
> >
> >	- If debugger uses PTRACE_O_TRACEEXEC and the thread did an exec
> >	  and the tracer does a ptrace request without having consumed
> >	  the exec event, it's 100% sure that the thread the ptracer
> >	  thinks it is targeting does not exist anymore, or isn't the
> >	  same as the one it thinks it is targeting.
> >
> >	- To some degree this patch adds nothing new. In the scenario
> >	  above ptrace(L) can fail with -ESRCH if it is called after the
> >	  execing sub-thread wakes the leader up and before it "steals"
> >	  the leader's pid.
>
> Hi Oleg,
>
> Is this something that should also target stable kernels ? AFAIU this change
> won't break debuggers more that they are already in this scenario. Or maybe
> it makes them fail in more obvious ways ?

Well, I am not sure this is stable material...

To me the problem is minor, and the patch adds the user-visible change.
I think it would be safer to not add stable tag.

Oleg.


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH RESEND] ptrace: make ptrace() fail if the tracee changed its pid unexpectedly
  2021-04-27  6:26   ` Oleg Nesterov
@ 2021-04-27 13:31     ` Mathieu Desnoyers
  0 siblings, 0 replies; 4+ messages in thread
From: Mathieu Desnoyers @ 2021-04-27 13:31 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Andrew Morton, Eric W. Biederman, Eugene Syromiatnikov,
	Jan Kratochvil, Linus Torvalds, Michael Kerrisk, Pedro Alves,
	Simon Marchi, linux-kernel, Greg KH

----- On Apr 27, 2021, at 2:26 AM, Oleg Nesterov oleg@redhat.com wrote:
[...]
>> Is this something that should also target stable kernels ? AFAIU this change
>> won't break debuggers more that they are already in this scenario. Or maybe
>> it makes them fail in more obvious ways ?
> 
> Well, I am not sure this is stable material...
> 
> To me the problem is minor, and the patch adds the user-visible change.
> I think it would be safer to not add stable tag.

I'm fine either way. So given the relatively small impact of this problem
(not critical), this ptrace fix may not be worthy of a stable tag.

I just find it odd that a patch fixing an ABI design flaw ends up not being
CC'd to stable, but also does not expose any way for user-space to discover
this altered ABI behavior. It's a rather weird middle-ground between a fix
and a new feature.

That being said, there was no prior way for user-space to achieve a correct
behavior before this patch, so making it discoverable is kind of pointless.

Thanks,

Mathieu

-- 
Mathieu Desnoyers
EfficiOS Inc.
http://www.efficios.com

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-04-27 13:32 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-26 15:45 [PATCH RESEND] ptrace: make ptrace() fail if the tracee changed its pid unexpectedly Oleg Nesterov
2021-04-26 21:04 ` Mathieu Desnoyers
2021-04-27  6:26   ` Oleg Nesterov
2021-04-27 13:31     ` Mathieu Desnoyers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).