Kaynağa Gözat

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>
Roland McGrath 17 yıl önce
ebeveyn
işleme
6718d0d6da
1 değiştirilmiş dosya ile 16 ekleme ve 6 silme
  1. 16 6
      arch/x86/kernel/step.c

+ 16 - 6
arch/x86/kernel/step.c

@@ -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);
 
 
-	/*
-	 * 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.. */
 	/* 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);