All of lore.kernel.org
 help / color / mirror / Atom feed
From: Oleg Nesterov <oleg@redhat.com>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>,
	Evan Teran <eteran@alum.rit.edu>,
	Jan Kratochvil <jan.kratochvil@redhat.com>,
	Pedro Alves <palves@redhat.com>,
	Roland McGrath <roland@hack.frob.com>,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH 1/1] ptrace/x86: fix the TIF_FORCED_TF logic in handle_signal()
Date: Fri, 28 Nov 2014 00:21:26 +0100	[thread overview]
Message-ID: <20141127232126.GA25773@redhat.com> (raw)
In-Reply-To: <20141103201317.GA5221@redhat.com>

ping ;)

Should I resend? This fixes the real (although not that serious) bug
and nobody objected.

On 11/03, Oleg Nesterov wrote:
>
> When the TIF_SINGLESTEP tracee dequeues a signal, handle_signal()
> clears TIF_FORCED_TF and X86_EFLAGS_TF but leaves TIF_SINGLESTEP set.
> 
> If the tracer does PTRACE_SINGLESTEP again, enable_single_step() sets
> X86_EFLAGS_TF but not TIF_FORCED_TF. This means that the subsequent
> PTRACE_CONT doesn't not clear X86_EFLAGS_TF, and the tracee gets the
> wrong SIGTRAP.
> 
> Test-case (needs -O2 to avoid prologue insns in signal handler):
> 
> 	#include <unistd.h>
> 	#include <stdio.h>
> 	#include <sys/ptrace.h>
> 	#include <sys/wait.h>
> 	#include <sys/user.h>
> 	#include <assert.h>
> 	#include <stddef.h>
> 
> 	void handler(int n)
> 	{
> 		asm("nop");
> 	}
> 
> 	int child(void)
> 	{
> 		assert(ptrace(PTRACE_TRACEME, 0,0,0) == 0);
> 		signal(SIGALRM, handler);
> 		kill(getpid(), SIGALRM);
> 		return 0x23;
> 	}
> 
> 	void *getip(int pid)
> 	{
> 		return (void*)ptrace(PTRACE_PEEKUSER, pid,
> 					offsetof(struct user, regs.rip), 0);
> 	}
> 
> 	int main(void)
> 	{
> 		int pid, status;
> 
> 		pid = fork();
> 		if (!pid)
> 			return child();
> 
> 		assert(wait(&status) == pid);
> 		assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGALRM);
> 
> 		assert(ptrace(PTRACE_SINGLESTEP, pid, 0, SIGALRM) == 0);
> 		assert(wait(&status) == pid);
> 		assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP);
> 		assert((getip(pid) - (void*)handler) == 0);
> 
> 		assert(ptrace(PTRACE_SINGLESTEP, pid, 0, SIGALRM) == 0);
> 		assert(wait(&status) == pid);
> 		assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP);
> 		assert((getip(pid) - (void*)handler) == 1);
> 
> 		assert(ptrace(PTRACE_CONT, pid, 0,0) == 0);
> 		assert(wait(&status) == pid);
> 		assert(WIFEXITED(status) && WEXITSTATUS(status) == 0x23);
> 
> 		return 0;
> 	}
> 
> The last assert() fails because PTRACE_CONT wrongly triggers another
> single-step and X86_EFLAGS_TF can't be cleared by debugger until the
> tracee does sys_rt_sigreturn().
> 
> Change handle_signal() to do user_disable_single_step() if stepping,
> we do not need to preserve TIF_SINGLESTEP because we are going to do
> ptrace_notify(), and it is simply wrong to leak this bit.
> 
> While at it, change the comment to explain why we also need to clear
> TF unconditionally after setup_rt_frame().
> 
> Note: in the longer term we should probably change setup_sigcontext()
> to use get_flags() and then just remove this user_disable_single_step().
> And, the state of TIF_FORCED_TF can be wrong after restore_sigcontext()
> which can set/clear TF, this needs another fix.
> 
> Reported-by: Evan Teran <eteran@alum.rit.edu>
> Reported-by: Pedro Alves <palves@redhat.com>
> Signed-off-by: Oleg Nesterov <oleg@redhat.com>
> ---
>  arch/x86/kernel/signal.c |   22 +++++++++++-----------
>  1 files changed, 11 insertions(+), 11 deletions(-)
> 
> diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
> index ed37a76..9d3a15b 100644
> --- a/arch/x86/kernel/signal.c
> +++ b/arch/x86/kernel/signal.c
> @@ -629,7 +629,8 @@ setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs)
>  static void
>  handle_signal(struct ksignal *ksig, struct pt_regs *regs)
>  {
> -	bool failed;
> +	bool stepping, failed;
> +
>  	/* Are we from a system call? */
>  	if (syscall_get_nr(current, regs) >= 0) {
>  		/* If so, check system call restarting.. */
> @@ -653,12 +654,13 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)
>  	}
>  
>  	/*
> -	 * If TF is set due to a debugger (TIF_FORCED_TF), clear the TF
> -	 * flag so that register information in the sigcontext is correct.
> +	 * If TF is set due to a debugger (TIF_FORCED_TF), clear TF now
> +	 * so that register information in the sigcontext is correct and
> +	 * then notify the tracer before entering the signal handler.
>  	 */
> -	if (unlikely(regs->flags & X86_EFLAGS_TF) &&
> -	    likely(test_and_clear_thread_flag(TIF_FORCED_TF)))
> -		regs->flags &= ~X86_EFLAGS_TF;
> +	stepping = test_thread_flag(TIF_SINGLESTEP);
> +	if (stepping)
> +		user_disable_single_step(current);
>  
>  	failed = (setup_rt_frame(ksig, regs) < 0);
>  	if (!failed) {
> @@ -669,10 +671,8 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)
>  		 * it might disable possible debug exception from the
>  		 * signal handler.
>  		 *
> -		 * Clear TF when entering the signal handler, but
> -		 * notify any tracer that was single-stepping it.
> -		 * The tracer may want to single-step inside the
> -		 * handler too.
> +		 * Clear TF for the case when it wasn't set by debugger to
> +		 * avoid the recursive send_sigtrap() in SIGTRAP handler.
>  		 */
>  		regs->flags &= ~(X86_EFLAGS_DF|X86_EFLAGS_RF|X86_EFLAGS_TF);
>  		/*
> @@ -681,7 +681,7 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)
>  		if (used_math())
>  			drop_init_fpu(current);
>  	}
> -	signal_setup_done(failed, ksig, test_thread_flag(TIF_SINGLESTEP));
> +	signal_setup_done(failed, ksig, stepping);
>  }
>  
>  #ifdef CONFIG_X86_32
> -- 
> 1.5.5.1
> 


  parent reply	other threads:[~2014-11-27 23:21 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-11-03 20:12 [PATCH 0/1] ptrace/x86: fix the TIF_FORCED_TF logic in handle_signal() Oleg Nesterov
2014-11-03 20:13 ` [PATCH 1/1] " Oleg Nesterov
2014-11-03 21:40   ` Pedro Alves
2014-11-04 23:55     ` Oleg Nesterov
2014-11-05  9:57       ` Pedro Alves
2014-11-27 23:21   ` Oleg Nesterov [this message]
2015-02-17  2:31     ` Andres Freund
2015-02-23 19:52       ` Oleg Nesterov
2015-01-06 16:18 ` [PATCH RESEND] " Oleg Nesterov
2015-02-23 19:53 ` [PATCH 2nd " Oleg Nesterov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20141127232126.GA25773@redhat.com \
    --to=oleg@redhat.com \
    --cc=akpm@linux-foundation.org \
    --cc=eteran@alum.rit.edu \
    --cc=jan.kratochvil@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=palves@redhat.com \
    --cc=roland@hack.frob.com \
    --cc=viro@zeniv.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.