diff options
Diffstat (limited to 'arch/x86/kernel/step.c')
| -rw-r--r-- | arch/x86/kernel/step.c | 35 |
1 files changed, 29 insertions, 6 deletions
diff --git a/arch/x86/kernel/step.c b/arch/x86/kernel/step.c index 92c20fee6781..e8b9863ef8c4 100644 --- a/arch/x86/kernel/step.c +++ b/arch/x86/kernel/step.c | |||
| @@ -105,6 +105,20 @@ 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; | ||
| 109 | |||
| 110 | /* | ||
| 111 | * If we stepped into a sysenter/syscall insn, it trapped in | ||
| 112 | * kernel mode; do_debug() cleared TF and set TIF_SINGLESTEP. | ||
| 113 | * If user-mode had set TF itself, then it's still clear from | ||
| 114 | * do_debug() and we need to set it again to restore the user | ||
| 115 | * state so we don't wrongly set TIF_FORCED_TF below. | ||
| 116 | * If enable_single_step() was used last and that is what | ||
| 117 | * set TIF_SINGLESTEP, then both TF and TIF_FORCED_TF are | ||
| 118 | * already set and our bookkeeping is fine. | ||
| 119 | */ | ||
| 120 | if (unlikely(test_tsk_thread_flag(child, TIF_SINGLESTEP))) | ||
| 121 | regs->flags |= X86_EFLAGS_TF; | ||
| 108 | 122 | ||
| 109 | /* | 123 | /* |
| 110 | * Always set TIF_SINGLESTEP - this guarantees that | 124 | * Always set TIF_SINGLESTEP - this guarantees that |
| @@ -113,11 +127,7 @@ static int enable_single_step(struct task_struct *child) | |||
| 113 | */ | 127 | */ |
| 114 | set_tsk_thread_flag(child, TIF_SINGLESTEP); | 128 | set_tsk_thread_flag(child, TIF_SINGLESTEP); |
| 115 | 129 | ||
| 116 | /* | 130 | 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 | 131 | ||
| 122 | /* Set TF on the kernel stack.. */ | 132 | /* Set TF on the kernel stack.. */ |
| 123 | regs->flags |= X86_EFLAGS_TF; | 133 | regs->flags |= X86_EFLAGS_TF; |
| @@ -126,9 +136,22 @@ static int enable_single_step(struct task_struct *child) | |||
| 126 | * ..but if TF is changed by the instruction we will trace, | 136 | * ..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 | 137 | * don't mark it as being "us" that set it, so that we |
| 128 | * won't clear it by hand later. | 138 | * won't clear it by hand later. |
| 139 | * | ||
| 140 | * Note that if we don't actually execute the popf because | ||
| 141 | * of a signal arriving right now or suchlike, we will lose | ||
| 142 | * track of the fact that it really was "us" that set it. | ||
| 129 | */ | 143 | */ |
| 130 | if (is_setting_trap_flag(child, regs)) | 144 | if (is_setting_trap_flag(child, regs)) { |
| 145 | clear_tsk_thread_flag(child, TIF_FORCED_TF); | ||
| 131 | return 0; | 146 | return 0; |
| 147 | } | ||
| 148 | |||
| 149 | /* | ||
| 150 | * If TF was already set, check whether it was us who set it. | ||
| 151 | * If not, we should never attempt a block step. | ||
| 152 | */ | ||
| 153 | if (oflags & X86_EFLAGS_TF) | ||
| 154 | return test_tsk_thread_flag(child, TIF_FORCED_TF); | ||
| 132 | 155 | ||
| 133 | set_tsk_thread_flag(child, TIF_FORCED_TF); | 156 | set_tsk_thread_flag(child, TIF_FORCED_TF); |
| 134 | 157 | ||
