diff options
Diffstat (limited to 'arch/x86/kernel/step.c')
| -rw-r--r-- | arch/x86/kernel/step.c | 58 | 
1 files changed, 36 insertions, 22 deletions
diff --git a/arch/x86/kernel/step.c b/arch/x86/kernel/step.c index 58de45ee08b..9b4d51d0c0d 100644 --- a/arch/x86/kernel/step.c +++ b/arch/x86/kernel/step.c @@ -74,7 +74,7 @@ static int is_setting_trap_flag(struct task_struct *child, struct pt_regs *regs)  #ifdef CONFIG_X86_64  		case 0x40 ... 0x4f: -			if (regs->cs != __USER_CS) +			if (!user_64bit_mode(regs))  				/* 32-bit mode: register increment */  				return 0;  			/* 64-bit mode: REX prefix */ @@ -157,6 +157,34 @@ static int enable_single_step(struct task_struct *child)  	return 1;  } +void set_task_blockstep(struct task_struct *task, bool on) +{ +	unsigned long debugctl; + +	/* +	 * Ensure irq/preemption can't change debugctl in between. +	 * Note also that both TIF_BLOCKSTEP and debugctl should +	 * be changed atomically wrt preemption. +	 * +	 * NOTE: this means that set/clear TIF_BLOCKSTEP is only safe if +	 * task is current or it can't be running, otherwise we can race +	 * with __switch_to_xtra(). We rely on ptrace_freeze_traced() but +	 * PTRACE_KILL is not safe. +	 */ +	local_irq_disable(); +	debugctl = get_debugctlmsr(); +	if (on) { +		debugctl |= DEBUGCTLMSR_BTF; +		set_tsk_thread_flag(task, TIF_BLOCKSTEP); +	} else { +		debugctl &= ~DEBUGCTLMSR_BTF; +		clear_tsk_thread_flag(task, TIF_BLOCKSTEP); +	} +	if (task == current) +		update_debugctlmsr(debugctl); +	local_irq_enable(); +} +  /*   * Enable single or block step.   */ @@ -166,22 +194,13 @@ static void enable_step(struct task_struct *child, bool block)  	 * Make sure block stepping (BTF) is not enabled unless it should be.  	 * Note that we don't try to worry about any is_setting_trap_flag()  	 * instructions after the first when using block stepping. -	 * So noone should try to use debugger block stepping in a program +	 * So no one should try to use debugger block stepping in a program  	 * that uses user-mode single stepping itself.  	 */ -	if (enable_single_step(child) && block) { -		unsigned long debugctl = get_debugctlmsr(); - -		debugctl |= DEBUGCTLMSR_BTF; -		update_debugctlmsr(debugctl); -		set_tsk_thread_flag(child, TIF_BLOCKSTEP); -	} else if (test_tsk_thread_flag(child, TIF_BLOCKSTEP)) { -		unsigned long debugctl = get_debugctlmsr(); - -		debugctl &= ~DEBUGCTLMSR_BTF; -		update_debugctlmsr(debugctl); -		clear_tsk_thread_flag(child, TIF_BLOCKSTEP); -	} +	if (enable_single_step(child) && block) +		set_task_blockstep(child, true); +	else if (test_tsk_thread_flag(child, TIF_BLOCKSTEP)) +		set_task_blockstep(child, false);  }  void user_enable_single_step(struct task_struct *child) @@ -199,13 +218,8 @@ void user_disable_single_step(struct task_struct *child)  	/*  	 * Make sure block stepping (BTF) is disabled.  	 */ -	if (test_tsk_thread_flag(child, TIF_BLOCKSTEP)) { -		unsigned long debugctl = get_debugctlmsr(); - -		debugctl &= ~DEBUGCTLMSR_BTF; -		update_debugctlmsr(debugctl); -		clear_tsk_thread_flag(child, TIF_BLOCKSTEP); -	} +	if (test_tsk_thread_flag(child, TIF_BLOCKSTEP)) +		set_task_blockstep(child, false);  	/* Always clear TIF_SINGLESTEP... */  	clear_tsk_thread_flag(child, TIF_SINGLESTEP);  | 
