x86 ptrace: block-step fix
The enable_single_step() logic bails out early if TF is already set. That skips some of the bookkeeping that keeps things straight. This makes PTRACE_SINGLEBLOCK break the behavior of a user task that was already setting TF itself in user mode. Fix the bookkeeping to notice the old TF setting as it should. Test case at: http://sources.redhat.com/cgi-bin/cvsweb.cgi/~checkout~/tests/ptrace-tests/tests/step-jump-cont-strict.c?cvsroot=systemtap Signed-off-by: Roland McGrath <roland@redhat.com>
This commit is contained in:
parent
a3cf859321
commit
6718d0d6da
|
@ -105,6 +105,7 @@ static int is_setting_trap_flag(struct task_struct *child, struct pt_regs *regs)
|
||||||
static int enable_single_step(struct task_struct *child)
|
static int enable_single_step(struct task_struct *child)
|
||||||
{
|
{
|
||||||
struct pt_regs *regs = task_pt_regs(child);
|
struct pt_regs *regs = task_pt_regs(child);
|
||||||
|
unsigned long oflags;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Always set TIF_SINGLESTEP - this guarantees that
|
* Always set TIF_SINGLESTEP - this guarantees that
|
||||||
|
@ -113,11 +114,7 @@ static int enable_single_step(struct task_struct *child)
|
||||||
*/
|
*/
|
||||||
set_tsk_thread_flag(child, TIF_SINGLESTEP);
|
set_tsk_thread_flag(child, TIF_SINGLESTEP);
|
||||||
|
|
||||||
/*
|
oflags = regs->flags;
|
||||||
* If TF was already set, don't do anything else
|
|
||||||
*/
|
|
||||||
if (regs->flags & X86_EFLAGS_TF)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Set TF on the kernel stack.. */
|
/* Set TF on the kernel stack.. */
|
||||||
regs->flags |= X86_EFLAGS_TF;
|
regs->flags |= X86_EFLAGS_TF;
|
||||||
|
@ -126,9 +123,22 @@ static int enable_single_step(struct task_struct *child)
|
||||||
* ..but if TF is changed by the instruction we will trace,
|
* ..but if TF is changed by the instruction we will trace,
|
||||||
* don't mark it as being "us" that set it, so that we
|
* don't mark it as being "us" that set it, so that we
|
||||||
* won't clear it by hand later.
|
* 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;
|
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);
|
set_tsk_thread_flag(child, TIF_FORCED_TF);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue