aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/step.c
diff options
context:
space:
mode:
authorRoland McGrath <roland@redhat.com>2008-07-09 04:07:02 -0400
committerRoland McGrath <roland@redhat.com>2008-07-16 15:15:16 -0400
commit6718d0d6da2749d3bff522e6057e97e6aa85e4d1 (patch)
treea069a92fd5011eb32bc461f3a23f0fc32d72f8ec /arch/x86/kernel/step.c
parenta3cf859321486f69506326146ab3e2fd15c05c3f (diff)
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>
Diffstat (limited to 'arch/x86/kernel/step.c')
-rw-r--r--arch/x86/kernel/step.c22
1 files changed, 16 insertions, 6 deletions
diff --git a/arch/x86/kernel/step.c b/arch/x86/kernel/step.c
index 92c20fee6781..0d2cb363ea75 100644
--- a/arch/x86/kernel/step.c
+++ b/arch/x86/kernel/step.c
@@ -105,6 +105,7 @@ static int is_setting_trap_flag(struct task_struct *child, struct pt_regs *regs)
105static int enable_single_step(struct task_struct *child) 105static int enable_single_step(struct task_struct *child)
106{ 106{
107 struct pt_regs *regs = task_pt_regs(child); 107 struct pt_regs *regs = task_pt_regs(child);
108 unsigned long oflags;
108 109
109 /* 110 /*
110 * Always set TIF_SINGLESTEP - this guarantees that 111 * Always set TIF_SINGLESTEP - this guarantees that
@@ -113,11 +114,7 @@ static int enable_single_step(struct task_struct *child)
113 */ 114 */
114 set_tsk_thread_flag(child, TIF_SINGLESTEP); 115 set_tsk_thread_flag(child, TIF_SINGLESTEP);
115 116
116 /* 117 oflags = regs->flags;
117 * If TF was already set, don't do anything else
118 */
119 if (regs->flags & X86_EFLAGS_TF)
120 return 0;
121 118
122 /* Set TF on the kernel stack.. */ 119 /* Set TF on the kernel stack.. */
123 regs->flags |= X86_EFLAGS_TF; 120 regs->flags |= X86_EFLAGS_TF;
@@ -126,9 +123,22 @@ static int enable_single_step(struct task_struct *child)
126 * ..but if TF is changed by the instruction we will trace, 123 * ..but if TF is changed by the instruction we will trace,
127 * don't mark it as being "us" that set it, so that we 124 * don't mark it as being "us" that set it, so that we
128 * won't clear it by hand later. 125 * won't clear it by hand later.
126 *
127 * Note that if we don't actually execute the popf because
128 * of a signal arriving right now or suchlike, we will lose
129 * track of the fact that it really was "us" that set it.
129 */ 130 */
130 if (is_setting_trap_flag(child, regs)) 131 if (is_setting_trap_flag(child, regs)) {
132 clear_tsk_thread_flag(child, TIF_FORCED_TF);
131 return 0; 133 return 0;
134 }
135
136 /*
137 * If TF was already set, check whether it was us who set it.
138 * If not, we should never attempt a block step.
139 */
140 if (oflags & X86_EFLAGS_TF)
141 return test_tsk_thread_flag(child, TIF_FORCED_TF);
132 142
133 set_tsk_thread_flag(child, TIF_FORCED_TF); 143 set_tsk_thread_flag(child, TIF_FORCED_TF);
134 144