2008-01-30 15:30:50 +03:00
/*
* x86 single - step support code , common to 32 - bit and 64 - bit .
*/
# include <linux/sched.h>
# include <linux/mm.h>
# include <linux/ptrace.h>
2009-07-18 19:08:54 +04:00
# include <asm/desc.h>
2015-07-31 00:31:32 +03:00
# include <asm/mmu_context.h>
2008-01-30 15:30:50 +03:00
2008-01-30 15:33:12 +03:00
unsigned long convert_ip_to_linear ( struct task_struct * child , struct pt_regs * regs )
2008-01-30 15:30:50 +03:00
{
unsigned long addr , seg ;
2008-01-30 15:30:56 +03:00
addr = regs - > ip ;
2008-01-30 15:30:50 +03:00
seg = regs - > cs & 0xffff ;
2008-01-30 15:30:56 +03:00
if ( v8086_mode ( regs ) ) {
2008-01-30 15:30:50 +03:00
addr = ( addr & 0xffff ) + ( seg < < 4 ) ;
return addr ;
}
2008-01-30 15:30:50 +03:00
2015-07-31 00:31:34 +03:00
# ifdef CONFIG_MODIFY_LDT_SYSCALL
2008-01-30 15:30:50 +03:00
/*
* We ' ll assume that the code segments in the GDT
* are all zero - based . That is largely true : the
* TLS segments are used for data , and the PNPBIOS
* and APM bios ones we just ignore here .
*/
2008-01-30 15:30:50 +03:00
if ( ( seg & SEGMENT_TI_MASK ) = = SEGMENT_LDT ) {
2009-07-18 19:08:54 +04:00
struct desc_struct * desc ;
2008-01-30 15:30:50 +03:00
unsigned long base ;
2015-08-06 11:04:38 +03:00
seg > > = 3 ;
2008-01-30 15:30:50 +03:00
mutex_lock ( & child - > mm - > context . lock ) ;
2015-07-31 00:31:32 +03:00
if ( unlikely ( ! child - > mm - > context . ldt | |
2015-08-06 11:04:38 +03:00
seg > = child - > mm - > context . ldt - > size ) )
2008-01-30 15:30:50 +03:00
addr = - 1L ; /* bogus selector, access would fault */
else {
2015-07-31 00:31:32 +03:00
desc = & child - > mm - > context . ldt - > entries [ seg ] ;
2009-07-18 19:08:54 +04:00
base = get_desc_base ( desc ) ;
2008-01-30 15:30:50 +03:00
/* 16-bit code segment? */
2009-07-18 19:08:54 +04:00
if ( ! desc - > d )
2008-01-30 15:30:50 +03:00
addr & = 0xffff ;
addr + = base ;
}
mutex_unlock ( & child - > mm - > context . lock ) ;
}
2015-07-31 00:31:34 +03:00
# endif
2008-01-30 15:30:50 +03:00
return addr ;
}
static int is_setting_trap_flag ( struct task_struct * child , struct pt_regs * regs )
{
int i , copied ;
unsigned char opcode [ 15 ] ;
2008-01-30 15:33:12 +03:00
unsigned long addr = convert_ip_to_linear ( child , regs ) ;
2008-01-30 15:30:50 +03:00
copied = access_process_vm ( child , addr , opcode , sizeof ( opcode ) , 0 ) ;
for ( i = 0 ; i < copied ; i + + ) {
switch ( opcode [ i ] ) {
/* popf and iret */
case 0x9d : case 0xcf :
return 1 ;
/* CHECKME: 64 65 */
/* opcode and address size prefixes */
case 0x66 : case 0x67 :
continue ;
/* irrelevant prefixes (segment overrides and repeats) */
case 0x26 : case 0x2e :
case 0x36 : case 0x3e :
case 0x64 : case 0x65 :
2008-01-30 15:30:50 +03:00
case 0xf0 : case 0xf2 : case 0xf3 :
2008-01-30 15:30:50 +03:00
continue ;
2008-01-30 15:30:50 +03:00
# ifdef CONFIG_X86_64
2008-01-30 15:30:50 +03:00
case 0x40 . . . 0x4f :
2011-08-03 17:31:53 +04:00
if ( ! user_64bit_mode ( regs ) )
2008-01-30 15:30:50 +03:00
/* 32-bit mode: register increment */
return 0 ;
/* 64-bit mode: REX prefix */
continue ;
2008-01-30 15:30:50 +03:00
# endif
2008-01-30 15:30:50 +03:00
/* CHECKME: f2, f3 */
/*
* pushf : NOTE ! We should probably not let
* the user see the TF bit being set . But
* it ' s more pain than it ' s worth to avoid
* it , and a debugger could emulate this
* all in user space if it _really_ cares .
*/
case 0x9c :
default :
return 0 ;
}
}
return 0 ;
}
2008-01-30 15:30:54 +03:00
/*
* Enable single - stepping . Return nonzero if user mode is not using TF itself .
*/
static int enable_single_step ( struct task_struct * child )
2008-01-30 15:30:50 +03:00
{
struct pt_regs * regs = task_pt_regs ( child ) ;
2008-07-09 12:07:02 +04:00
unsigned long oflags ;
2008-01-30 15:30:50 +03:00
2008-07-09 13:39:29 +04:00
/*
* If we stepped into a sysenter / syscall insn , it trapped in
* kernel mode ; do_debug ( ) cleared TF and set TIF_SINGLESTEP .
* If user - mode had set TF itself , then it ' s still clear from
* do_debug ( ) and we need to set it again to restore the user
* state so we don ' t wrongly set TIF_FORCED_TF below .
* If enable_single_step ( ) was used last and that is what
* set TIF_SINGLESTEP , then both TF and TIF_FORCED_TF are
* already set and our bookkeeping is fine .
*/
if ( unlikely ( test_tsk_thread_flag ( child , TIF_SINGLESTEP ) ) )
regs - > flags | = X86_EFLAGS_TF ;
2008-01-30 15:30:50 +03:00
/*
* Always set TIF_SINGLESTEP - this guarantees that
* we single - step system calls etc . . This will also
* cause us to set TF when returning to user mode .
*/
set_tsk_thread_flag ( child , TIF_SINGLESTEP ) ;
2008-07-09 12:07:02 +04:00
oflags = regs - > flags ;
2008-01-30 15:30:50 +03:00
/* Set TF on the kernel stack.. */
2008-01-30 15:30:56 +03:00
regs - > flags | = X86_EFLAGS_TF ;
2008-01-30 15:30:50 +03:00
/*
* . . but if TF is changed by the instruction we will trace ,
* don ' t mark it as being " us " that set it , so that we
* won ' t clear it by hand later .
2008-07-09 12:07:02 +04:00
*
* 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 .
2008-01-30 15:30:50 +03:00
*/
2008-07-09 12:07:02 +04:00
if ( is_setting_trap_flag ( child , regs ) ) {
clear_tsk_thread_flag ( child , TIF_FORCED_TF ) ;
2008-01-30 15:30:54 +03:00
return 0 ;
2008-07-09 12:07:02 +04:00
}
/*
* 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 ) ;
2008-01-30 15:30:50 +03:00
2008-01-30 15:30:50 +03:00
set_tsk_thread_flag ( child , TIF_FORCED_TF ) ;
2008-01-30 15:30:54 +03:00
return 1 ;
}
2012-09-03 17:24:17 +04:00
void set_task_blockstep ( struct task_struct * task , bool on )
2012-08-03 19:31:46 +04:00
{
unsigned long debugctl ;
2012-08-11 20:06:42 +04:00
/*
* Ensure irq / preemption can ' t change debugctl in between .
* Note also that both TIF_BLOCKSTEP and debugctl should
* be changed atomically wrt preemption .
2013-01-21 23:48:00 +04:00
*
* 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 .
2012-08-11 20:06:42 +04:00
*/
local_irq_disable ( ) ;
2012-08-03 19:31:46 +04:00
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 ) ;
}
2012-08-11 20:06:42 +04:00
if ( task = = current )
update_debugctlmsr ( debugctl ) ;
local_irq_enable ( ) ;
2012-08-03 19:31:46 +04:00
}
2008-01-30 15:30:54 +03:00
/*
* Enable single or block step .
*/
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 .
2011-03-17 22:24:16 +03:00
* So no one should try to use debugger block stepping in a program
2008-01-30 15:30:54 +03:00
* that uses user - mode single stepping itself .
*/
2012-08-03 19:31:46 +04:00
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 ) ;
2008-01-30 15:30:54 +03:00
}
void user_enable_single_step ( struct task_struct * child )
{
enable_step ( child , 0 ) ;
}
void user_enable_block_step ( struct task_struct * child )
{
enable_step ( child , 1 ) ;
2008-01-30 15:30:50 +03:00
}
void user_disable_single_step ( struct task_struct * child )
{
2008-01-30 15:30:54 +03:00
/*
* Make sure block stepping ( BTF ) is disabled .
*/
2012-08-03 19:31:46 +04:00
if ( test_tsk_thread_flag ( child , TIF_BLOCKSTEP ) )
set_task_blockstep ( child , false ) ;
2008-01-30 15:30:54 +03:00
2008-01-30 15:30:50 +03:00
/* Always clear TIF_SINGLESTEP... */
clear_tsk_thread_flag ( child , TIF_SINGLESTEP ) ;
/* But touch TF only if it was set by us.. */
2008-01-30 15:30:50 +03:00
if ( test_and_clear_tsk_thread_flag ( child , TIF_FORCED_TF ) )
2008-01-30 15:30:56 +03:00
task_pt_regs ( child ) - > flags & = ~ X86_EFLAGS_TF ;
2008-01-30 15:30:50 +03:00
}