diff options
author | Roland McGrath <roland@redhat.com> | 2008-07-09 04:07:02 -0400 |
---|---|---|
committer | Roland McGrath <roland@redhat.com> | 2008-07-16 15:15:16 -0400 |
commit | 6718d0d6da2749d3bff522e6057e97e6aa85e4d1 (patch) | |
tree | a069a92fd5011eb32bc461f3a23f0fc32d72f8ec /arch/x86/kernel/step.c | |
parent | a3cf859321486f69506326146ab3e2fd15c05c3f (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.c | 22 |
1 files changed, 16 insertions, 6 deletions
diff --git a/arch/x86/kernel/step.c b/arch/x86/kernel/step.c index 92c20fee678..0d2cb363ea7 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) | |||
105 | static int enable_single_step(struct task_struct *child) | 105 | static 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 | ||