2005-04-17 02:20:36 +04:00
/* By Ross Biro 1/23/92 */
/*
* Pentium III FXSR , SSE support
* Gareth Hughes < gareth @ valinux . com > , May 2000
2008-01-30 15:31:09 +03:00
*
* BTS tracing
* Markus Metzger < markus . t . metzger @ intel . com > , Dec 2007
2005-04-17 02:20:36 +04:00
*/
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/mm.h>
# include <linux/smp.h>
# include <linux/errno.h>
# include <linux/ptrace.h>
2008-01-30 15:31:52 +03:00
# include <linux/regset.h>
2008-03-17 09:36:28 +03:00
# include <linux/tracehook.h>
2005-04-17 02:20:36 +04:00
# include <linux/user.h>
2008-01-30 15:31:53 +03:00
# include <linux/elf.h>
2005-04-17 02:20:36 +04:00
# include <linux/security.h>
# include <linux/audit.h>
# include <linux/seccomp.h>
2005-05-01 19:59:14 +04:00
# include <linux/signal.h>
2009-04-03 18:43:35 +04:00
# include <linux/workqueue.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include <asm/pgtable.h>
# include <asm/system.h>
# include <asm/processor.h>
# include <asm/i387.h>
# include <asm/debugreg.h>
# include <asm/ldt.h>
# include <asm/desc.h>
2008-01-30 15:31:01 +03:00
# include <asm/prctl.h>
# include <asm/proto.h>
2008-01-30 15:31:09 +03:00
# include <asm/ds.h>
2009-06-01 22:15:48 +04:00
# include <asm/hw_breakpoint.h>
2008-01-30 15:31:09 +03:00
2009-04-08 22:40:59 +04:00
# include <trace/syscall.h>
2008-01-30 15:31:53 +03:00
# include "tls.h"
enum x86_regset {
REGSET_GENERAL ,
REGSET_FP ,
REGSET_XFP ,
2008-08-09 02:58:39 +04:00
REGSET_IOPERM64 = REGSET_XFP ,
2008-01-30 15:31:53 +03:00
REGSET_TLS ,
2008-08-09 02:58:39 +04:00
REGSET_IOPERM32 ,
2008-01-30 15:31:53 +03:00
} ;
2008-01-30 15:31:09 +03:00
2005-04-17 02:20:36 +04:00
/*
* does not yet catch signals sent when the child dies .
* in exit . c or in signal . c .
*/
2006-01-06 07:11:29 +03:00
/*
* Determines which flags the user has access to [ 1 = access , 0 = no access ] .
*/
2008-01-30 15:31:01 +03:00
# define FLAG_MASK_32 ((unsigned long) \
( X86_EFLAGS_CF | X86_EFLAGS_PF | \
X86_EFLAGS_AF | X86_EFLAGS_ZF | \
X86_EFLAGS_SF | X86_EFLAGS_TF | \
X86_EFLAGS_DF | X86_EFLAGS_OF | \
X86_EFLAGS_RF | X86_EFLAGS_AC ) )
2008-01-30 15:31:01 +03:00
/*
* Determines whether a value may be installed in a segment register .
*/
static inline bool invalid_selector ( u16 value )
{
return unlikely ( value ! = 0 & & ( value & SEGMENT_RPL_MASK ) ! = USER_RPL ) ;
}
# ifdef CONFIG_X86_32
2008-01-30 15:31:01 +03:00
# define FLAG_MASK FLAG_MASK_32
2005-04-17 02:20:36 +04:00
2008-07-25 08:49:27 +04:00
static unsigned long * pt_regs_access ( struct pt_regs * regs , unsigned long regno )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:30:56 +03:00
BUILD_BUG_ON ( offsetof ( struct pt_regs , bx ) ! = 0 ) ;
2009-02-09 16:17:40 +03:00
return & regs - > bx + ( regno > > 2 ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:01 +03:00
static u16 get_segment_reg ( struct task_struct * task , unsigned long offset )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:31:01 +03:00
/*
* Returning the value truncates it to 16 bits .
*/
unsigned int retval ;
if ( offset ! = offsetof ( struct user_regs_struct , gs ) )
retval = * pt_regs_access ( task_pt_regs ( task ) , offset ) ;
else {
if ( task = = current )
2009-02-09 16:17:40 +03:00
retval = get_user_gs ( task_pt_regs ( task ) ) ;
else
retval = task_user_gs ( task ) ;
2008-01-30 15:31:01 +03:00
}
return retval ;
}
static int set_segment_reg ( struct task_struct * task ,
unsigned long offset , u16 value )
{
/*
* The value argument was already truncated to 16 bits .
*/
2008-01-30 15:31:01 +03:00
if ( invalid_selector ( value ) )
2008-01-30 15:31:01 +03:00
return - EIO ;
2008-02-07 00:39:44 +03:00
/*
* For % cs and % ss we cannot permit a null selector .
* We can permit a bogus selector as long as it has USER_RPL .
* Null selectors are fine for other segment registers , but
* we will never get back to user mode with invalid % cs or % ss
* and will take the trap in iret instead . Much code relies
* on user_mode ( ) to distinguish a user trap frame ( which can
* safely use invalid selectors ) from a kernel trap frame .
*/
switch ( offset ) {
case offsetof ( struct user_regs_struct , cs ) :
case offsetof ( struct user_regs_struct , ss ) :
if ( unlikely ( value = = 0 ) )
return - EIO ;
default :
2008-01-30 15:31:01 +03:00
* pt_regs_access ( task_pt_regs ( task ) , offset ) = value ;
2008-02-07 00:39:44 +03:00
break ;
case offsetof ( struct user_regs_struct , gs ) :
2008-01-30 15:31:01 +03:00
if ( task = = current )
2009-02-09 16:17:40 +03:00
set_user_gs ( task_pt_regs ( task ) , value ) ;
else
task_user_gs ( task ) = value ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:01 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-01-30 15:31:01 +03:00
# else /* CONFIG_X86_64 */
# define FLAG_MASK (FLAG_MASK_32 | X86_EFLAGS_NT)
static unsigned long * pt_regs_access ( struct pt_regs * regs , unsigned long offset )
{
BUILD_BUG_ON ( offsetof ( struct pt_regs , r15 ) ! = 0 ) ;
return & regs - > r15 + ( offset / sizeof ( regs - > r15 ) ) ;
}
static u16 get_segment_reg ( struct task_struct * task , unsigned long offset )
{
/*
* Returning the value truncates it to 16 bits .
*/
unsigned int seg ;
switch ( offset ) {
case offsetof ( struct user_regs_struct , fs ) :
if ( task = = current ) {
/* Older gas can't assemble movq %?s,%r?? */
asm ( " movl %%fs,%0 " : " =r " ( seg ) ) ;
return seg ;
}
return task - > thread . fsindex ;
case offsetof ( struct user_regs_struct , gs ) :
if ( task = = current ) {
asm ( " movl %%gs,%0 " : " =r " ( seg ) ) ;
return seg ;
}
return task - > thread . gsindex ;
case offsetof ( struct user_regs_struct , ds ) :
if ( task = = current ) {
asm ( " movl %%ds,%0 " : " =r " ( seg ) ) ;
return seg ;
}
return task - > thread . ds ;
case offsetof ( struct user_regs_struct , es ) :
if ( task = = current ) {
asm ( " movl %%es,%0 " : " =r " ( seg ) ) ;
return seg ;
}
return task - > thread . es ;
case offsetof ( struct user_regs_struct , cs ) :
case offsetof ( struct user_regs_struct , ss ) :
break ;
}
return * pt_regs_access ( task_pt_regs ( task ) , offset ) ;
}
static int set_segment_reg ( struct task_struct * task ,
unsigned long offset , u16 value )
{
/*
* The value argument was already truncated to 16 bits .
*/
if ( invalid_selector ( value ) )
return - EIO ;
switch ( offset ) {
case offsetof ( struct user_regs_struct , fs ) :
/*
* If this is setting fs as for normal 64 - bit use but
* setting fs_base has implicitly changed it , leave it .
*/
if ( ( value = = FS_TLS_SEL & & task - > thread . fsindex = = 0 & &
task - > thread . fs ! = 0 ) | |
( value = = 0 & & task - > thread . fsindex = = FS_TLS_SEL & &
task - > thread . fs = = 0 ) )
break ;
task - > thread . fsindex = value ;
if ( task = = current )
loadsegment ( fs , task - > thread . fsindex ) ;
break ;
case offsetof ( struct user_regs_struct , gs ) :
/*
* If this is setting gs as for normal 64 - bit use but
* setting gs_base has implicitly changed it , leave it .
*/
if ( ( value = = GS_TLS_SEL & & task - > thread . gsindex = = 0 & &
task - > thread . gs ! = 0 ) | |
( value = = 0 & & task - > thread . gsindex = = GS_TLS_SEL & &
task - > thread . gs = = 0 ) )
break ;
task - > thread . gsindex = value ;
if ( task = = current )
load_gs_index ( task - > thread . gsindex ) ;
break ;
case offsetof ( struct user_regs_struct , ds ) :
task - > thread . ds = value ;
if ( task = = current )
loadsegment ( ds , task - > thread . ds ) ;
break ;
case offsetof ( struct user_regs_struct , es ) :
task - > thread . es = value ;
if ( task = = current )
loadsegment ( es , task - > thread . es ) ;
break ;
/*
* Can ' t actually change these in 64 - bit mode .
*/
case offsetof ( struct user_regs_struct , cs ) :
2008-02-07 00:39:44 +03:00
if ( unlikely ( value = = 0 ) )
return - EIO ;
2008-01-30 15:31:01 +03:00
# ifdef CONFIG_IA32_EMULATION
if ( test_tsk_thread_flag ( task , TIF_IA32 ) )
task_pt_regs ( task ) - > cs = value ;
# endif
2008-01-30 15:31:01 +03:00
break ;
2008-01-30 15:31:01 +03:00
case offsetof ( struct user_regs_struct , ss ) :
2008-02-07 00:39:44 +03:00
if ( unlikely ( value = = 0 ) )
return - EIO ;
2008-01-30 15:31:01 +03:00
# ifdef CONFIG_IA32_EMULATION
if ( test_tsk_thread_flag ( task , TIF_IA32 ) )
task_pt_regs ( task ) - > ss = value ;
# endif
2008-01-30 15:31:01 +03:00
break ;
2008-01-30 15:31:01 +03:00
}
return 0 ;
}
# endif /* CONFIG_X86_32 */
2008-01-30 15:31:01 +03:00
static unsigned long get_flags ( struct task_struct * task )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:31:01 +03:00
unsigned long retval = task_pt_regs ( task ) - > flags ;
/*
* If the debugger set TF , hide it from the readout .
*/
if ( test_tsk_thread_flag ( task , TIF_FORCED_TF ) )
retval & = ~ X86_EFLAGS_TF ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2008-01-30 15:31:01 +03:00
static int set_flags ( struct task_struct * task , unsigned long value )
{
struct pt_regs * regs = task_pt_regs ( task ) ;
/*
* If the user value contains TF , mark that
* it was not " us " ( the debugger ) that set it .
* If not , make sure it stays set if we had .
*/
if ( value & X86_EFLAGS_TF )
clear_tsk_thread_flag ( task , TIF_FORCED_TF ) ;
else if ( test_tsk_thread_flag ( task , TIF_FORCED_TF ) )
value | = X86_EFLAGS_TF ;
regs - > flags = ( regs - > flags & ~ FLAG_MASK ) | ( value & FLAG_MASK ) ;
return 0 ;
}
static int putreg ( struct task_struct * child ,
unsigned long offset , unsigned long value )
{
switch ( offset ) {
case offsetof ( struct user_regs_struct , cs ) :
case offsetof ( struct user_regs_struct , ds ) :
case offsetof ( struct user_regs_struct , es ) :
case offsetof ( struct user_regs_struct , fs ) :
case offsetof ( struct user_regs_struct , gs ) :
case offsetof ( struct user_regs_struct , ss ) :
return set_segment_reg ( child , offset , value ) ;
case offsetof ( struct user_regs_struct , flags ) :
return set_flags ( child , value ) ;
2008-01-30 15:31:01 +03:00
# ifdef CONFIG_X86_64
2008-03-08 01:56:02 +03:00
/*
* Orig_ax is really just a flag with small positive and
* negative values , so make sure to always sign - extend it
* from 32 bits so that it works correctly regardless of
* whether we come from a 32 - bit environment or not .
*/
case offsetof ( struct user_regs_struct , orig_ax ) :
value = ( long ) ( s32 ) value ;
break ;
2008-01-30 15:31:01 +03:00
case offsetof ( struct user_regs_struct , fs_base ) :
if ( value > = TASK_SIZE_OF ( child ) )
return - EIO ;
/*
* When changing the segment base , use do_arch_prctl
* to set either thread . fs or thread . fsindex and the
* corresponding GDT slot .
*/
if ( child - > thread . fs ! = value )
return do_arch_prctl ( child , ARCH_SET_FS , value ) ;
return 0 ;
case offsetof ( struct user_regs_struct , gs_base ) :
/*
* Exactly the same here as the % fs handling above .
*/
if ( value > = TASK_SIZE_OF ( child ) )
return - EIO ;
if ( child - > thread . gs ! = value )
return do_arch_prctl ( child , ARCH_SET_GS , value ) ;
return 0 ;
# endif
2008-01-30 15:31:01 +03:00
}
* pt_regs_access ( task_pt_regs ( child ) , offset ) = value ;
return 0 ;
}
static unsigned long getreg ( struct task_struct * task , unsigned long offset )
{
switch ( offset ) {
case offsetof ( struct user_regs_struct , cs ) :
case offsetof ( struct user_regs_struct , ds ) :
case offsetof ( struct user_regs_struct , es ) :
case offsetof ( struct user_regs_struct , fs ) :
case offsetof ( struct user_regs_struct , gs ) :
case offsetof ( struct user_regs_struct , ss ) :
return get_segment_reg ( task , offset ) ;
case offsetof ( struct user_regs_struct , flags ) :
return get_flags ( task ) ;
2008-01-30 15:31:01 +03:00
# ifdef CONFIG_X86_64
case offsetof ( struct user_regs_struct , fs_base ) : {
/*
* do_arch_prctl may have used a GDT slot instead of
* the MSR . To userland , it appears the same either
* way , except the % fs segment selector might not be 0.
*/
unsigned int seg = task - > thread . fsindex ;
if ( task - > thread . fs ! = 0 )
return task - > thread . fs ;
if ( task = = current )
asm ( " movl %%fs,%0 " : " =r " ( seg ) ) ;
if ( seg ! = FS_TLS_SEL )
return 0 ;
return get_desc_base ( & task - > thread . tls_array [ FS_TLS ] ) ;
}
case offsetof ( struct user_regs_struct , gs_base ) : {
/*
* Exactly the same here as the % fs handling above .
*/
unsigned int seg = task - > thread . gsindex ;
if ( task - > thread . gs ! = 0 )
return task - > thread . gs ;
if ( task = = current )
asm ( " movl %%gs,%0 " : " =r " ( seg ) ) ;
if ( seg ! = GS_TLS_SEL )
return 0 ;
return get_desc_base ( & task - > thread . tls_array [ GS_TLS ] ) ;
}
# endif
2008-01-30 15:31:01 +03:00
}
return * pt_regs_access ( task_pt_regs ( task ) , offset ) ;
}
2008-01-30 15:31:52 +03:00
static int genregs_get ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
void * kbuf , void __user * ubuf )
{
if ( kbuf ) {
unsigned long * k = kbuf ;
while ( count > 0 ) {
* k + + = getreg ( target , pos ) ;
count - = sizeof ( * k ) ;
pos + = sizeof ( * k ) ;
}
} else {
unsigned long __user * u = ubuf ;
while ( count > 0 ) {
if ( __put_user ( getreg ( target , pos ) , u + + ) )
return - EFAULT ;
count - = sizeof ( * u ) ;
pos + = sizeof ( * u ) ;
}
}
return 0 ;
}
static int genregs_set ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
const void * kbuf , const void __user * ubuf )
{
int ret = 0 ;
if ( kbuf ) {
const unsigned long * k = kbuf ;
while ( count > 0 & & ! ret ) {
ret = putreg ( target , pos , * k + + ) ;
count - = sizeof ( * k ) ;
pos + = sizeof ( * k ) ;
}
} else {
const unsigned long __user * u = ubuf ;
while ( count > 0 & & ! ret ) {
unsigned long word ;
ret = __get_user ( word , u + + ) ;
if ( ret )
break ;
ret = putreg ( target , pos , word ) ;
count - = sizeof ( * u ) ;
pos + = sizeof ( * u ) ;
}
}
return ret ;
}
2008-01-30 15:30:52 +03:00
/*
2009-06-01 22:15:48 +04:00
* Decode the length and type bits for a particular breakpoint as
* stored in debug register 7. Return the " enabled " status .
2008-01-30 15:30:52 +03:00
*/
2009-06-01 22:15:48 +04:00
static int decode_dr7 ( unsigned long dr7 , int bpnum , unsigned * len ,
unsigned * type )
2008-01-30 15:30:52 +03:00
{
2009-06-01 22:15:48 +04:00
int bp_info = dr7 > > ( DR_CONTROL_SHIFT + bpnum * DR_CONTROL_SIZE ) ;
* len = ( bp_info & 0xc ) | 0x40 ;
* type = ( bp_info & 0x3 ) | 0x80 ;
return ( dr7 > > ( bpnum * DR_ENABLE_SIZE ) ) & 0x3 ;
2008-01-30 15:30:52 +03:00
}
2009-06-01 22:15:48 +04:00
static void ptrace_triggered ( struct hw_breakpoint * bp , struct pt_regs * regs )
2008-01-30 15:30:52 +03:00
{
2009-06-01 22:15:48 +04:00
struct thread_struct * thread = & ( current - > thread ) ;
2008-01-30 15:30:59 +03:00
int i ;
2009-06-01 22:15:48 +04:00
/*
* Store in the virtual DR6 register the fact that the breakpoint
* was hit so the thread ' s debugger will see it .
*/
for ( i = 0 ; i < hbp_kernel_pos ; i + + )
/*
* We will check bp - > info . address against the address stored in
* thread ' s hbp structure and not debugreg [ i ] . This is to ensure
* that the corresponding bit for ' i ' in DR7 register is enabled
*/
if ( bp - > info . address = = thread - > hbp [ i ] - > info . address )
break ;
2008-01-30 15:30:52 +03:00
2009-06-01 22:15:48 +04:00
thread - > debugreg6 | = ( DR_TRAP0 < < i ) ;
}
2008-01-30 15:30:52 +03:00
2009-06-01 22:15:48 +04:00
/*
* Handle ptrace writes to debug register 7.
*/
static int ptrace_write_dr7 ( struct task_struct * tsk , unsigned long data )
{
struct thread_struct * thread = & ( tsk - > thread ) ;
unsigned long old_dr7 = thread - > debugreg7 ;
int i , orig_ret = 0 , rc = 0 ;
int enabled , second_pass = 0 ;
unsigned len , type ;
struct hw_breakpoint * bp ;
data & = ~ DR_CONTROL_RESERVED ;
restore :
/*
* Loop through all the hardware breakpoints , making the
* appropriate changes to each .
*/
for ( i = 0 ; i < HBP_NUM ; i + + ) {
enabled = decode_dr7 ( data , i , & len , & type ) ;
bp = thread - > hbp [ i ] ;
if ( ! enabled ) {
if ( bp ) {
/* Don't unregister the breakpoints right-away,
* unless all register_user_hw_breakpoint ( )
* requests have succeeded . This prevents
* any window of opportunity for debug
* register grabbing by other users .
*/
if ( ! second_pass )
continue ;
unregister_user_hw_breakpoint ( tsk , bp ) ;
kfree ( bp ) ;
}
continue ;
}
if ( ! bp ) {
rc = - ENOMEM ;
bp = kzalloc ( sizeof ( struct hw_breakpoint ) , GFP_KERNEL ) ;
if ( bp ) {
bp - > info . address = thread - > debugreg [ i ] ;
bp - > triggered = ptrace_triggered ;
bp - > info . len = len ;
bp - > info . type = type ;
rc = register_user_hw_breakpoint ( tsk , bp ) ;
if ( rc )
kfree ( bp ) ;
}
} else
rc = modify_user_hw_breakpoint ( tsk , bp ) ;
if ( rc )
break ;
}
/*
* Make a second pass to free the remaining unused breakpoints
* or to restore the original breakpoints if an error occurred .
*/
if ( ! second_pass ) {
second_pass = 1 ;
if ( rc < 0 ) {
orig_ret = rc ;
data = old_dr7 ;
}
goto restore ;
}
return ( ( orig_ret < 0 ) ? orig_ret : rc ) ;
}
2008-01-30 15:30:59 +03:00
2009-06-01 22:15:48 +04:00
/*
* Handle PTRACE_PEEKUSR calls for the debug register area .
*/
unsigned long ptrace_get_debugreg ( struct task_struct * tsk , int n )
{
struct thread_struct * thread = & ( tsk - > thread ) ;
unsigned long val = 0 ;
if ( n < HBP_NUM )
val = thread - > debugreg [ n ] ;
else if ( n = = 6 )
val = thread - > debugreg6 ;
else if ( n = = 7 )
val = thread - > debugreg7 ;
return val ;
}
2008-01-30 15:30:59 +03:00
2009-06-01 22:15:48 +04:00
/*
* Handle PTRACE_POKEUSR calls for the debug register area .
*/
int ptrace_set_debugreg ( struct task_struct * tsk , int n , unsigned long val )
{
struct thread_struct * thread = & ( tsk - > thread ) ;
int rc = 0 ;
/* There are no DR4 or DR5 registers */
if ( n = = 4 | | n = = 5 )
return - EIO ;
if ( n = = 6 ) {
tsk - > thread . debugreg6 = val ;
goto ret_path ;
2008-01-30 15:30:52 +03:00
}
2009-06-01 22:15:48 +04:00
if ( n < HBP_NUM ) {
if ( thread - > hbp [ n ] ) {
if ( arch_check_va_in_userspace ( val ,
thread - > hbp [ n ] - > info . len ) = = 0 ) {
rc = - EIO ;
goto ret_path ;
}
thread - > hbp [ n ] - > info . address = val ;
}
thread - > debugreg [ n ] = val ;
}
/* All that's left is DR7 */
if ( n = = 7 )
rc = ptrace_write_dr7 ( tsk , val ) ;
2008-01-30 15:30:52 +03:00
2009-06-01 22:15:48 +04:00
ret_path :
return rc ;
2008-01-30 15:30:52 +03:00
}
2008-08-09 02:58:39 +04:00
/*
* These access the current or another ( stopped ) task ' s io permission
* bitmap for debugging or core dump .
*/
static int ioperm_active ( struct task_struct * target ,
const struct user_regset * regset )
{
return target - > thread . io_bitmap_max / regset - > size ;
}
2008-02-26 11:40:27 +03:00
2008-08-09 02:58:39 +04:00
static int ioperm_get ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
void * kbuf , void __user * ubuf )
2008-01-30 15:31:09 +03:00
{
2008-08-09 02:58:39 +04:00
if ( ! target - > thread . io_bitmap_ptr )
2008-01-30 15:31:09 +03:00
return - ENXIO ;
2008-08-09 02:58:39 +04:00
return user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
target - > thread . io_bitmap_ptr ,
0 , IO_BITMAP_BYTES ) ;
}
2008-04-08 13:01:58 +04:00
# ifdef CONFIG_X86_PTRACE_BTS
2009-04-03 18:43:35 +04:00
/*
* A branch trace store context .
*
* Contexts may only be installed by ptrace_bts_config ( ) and only for
* ptraced tasks .
*
* Contexts are destroyed when the tracee is detached from the tracer .
* The actual destruction work requires interrupts enabled , so the
* work is deferred and will be scheduled during __ptrace_unlink ( ) .
*
* Contexts hold an additional task_struct reference on the traced
* task , as well as a reference on the tracer ' s mm .
*
* Ptrace already holds a task_struct for the duration of ptrace operations ,
* but since destruction is deferred , it may be executed after both
* tracer and tracee exited .
*/
struct bts_context {
/* The branch trace handle. */
struct bts_tracer * tracer ;
/* The buffer used to store the branch trace and its size. */
void * buffer ;
unsigned int size ;
/* The mm that paid for the above buffer. */
struct mm_struct * mm ;
/* The task this context belongs to. */
struct task_struct * task ;
/* The signal to send on a bts buffer overflow. */
unsigned int bts_ovfl_signal ;
/* The work struct to destroy a context. */
struct work_struct work ;
} ;
2009-04-24 11:51:43 +04:00
static int alloc_bts_buffer ( struct bts_context * context , unsigned int size )
2009-04-03 18:43:35 +04:00
{
2009-04-24 11:51:43 +04:00
void * buffer = NULL ;
int err = - ENOMEM ;
2009-04-03 18:43:35 +04:00
2009-04-24 11:51:43 +04:00
err = account_locked_memory ( current - > mm , current - > signal - > rlim , size ) ;
if ( err < 0 )
return err ;
buffer = kzalloc ( size , GFP_KERNEL ) ;
if ( ! buffer )
goto out_refund ;
context - > buffer = buffer ;
context - > size = size ;
context - > mm = get_task_mm ( current ) ;
return 0 ;
out_refund :
refund_locked_memory ( current - > mm , size ) ;
return err ;
2009-04-03 18:43:35 +04:00
}
static inline void free_bts_buffer ( struct bts_context * context )
{
if ( ! context - > buffer )
return ;
kfree ( context - > buffer ) ;
context - > buffer = NULL ;
2009-04-24 11:51:43 +04:00
refund_locked_memory ( context - > mm , context - > size ) ;
2009-04-03 18:43:35 +04:00
context - > size = 0 ;
mmput ( context - > mm ) ;
context - > mm = NULL ;
}
static void free_bts_context_work ( struct work_struct * w )
{
struct bts_context * context ;
context = container_of ( w , struct bts_context , work ) ;
ds_release_bts ( context - > tracer ) ;
put_task_struct ( context - > task ) ;
free_bts_buffer ( context ) ;
kfree ( context ) ;
}
static inline void free_bts_context ( struct bts_context * context )
{
INIT_WORK ( & context - > work , free_bts_context_work ) ;
schedule_work ( & context - > work ) ;
}
static inline struct bts_context * alloc_bts_context ( struct task_struct * task )
{
struct bts_context * context = kzalloc ( sizeof ( * context ) , GFP_KERNEL ) ;
if ( context ) {
context - > task = task ;
task - > bts = context ;
get_task_struct ( task ) ;
}
return context ;
}
2008-04-08 13:01:58 +04:00
static int ptrace_bts_read_record ( struct task_struct * child , size_t index ,
2008-01-30 15:31:09 +03:00
struct bts_struct __user * out )
{
2009-04-03 18:43:35 +04:00
struct bts_context * context ;
2008-12-11 15:49:59 +03:00
const struct bts_trace * trace ;
struct bts_struct bts ;
const unsigned char * at ;
2008-04-08 13:01:58 +04:00
int error ;
2008-01-30 15:31:09 +03:00
2009-04-03 18:43:35 +04:00
context = child - > bts ;
if ( ! context )
return - ESRCH ;
trace = ds_read_bts ( context - > tracer ) ;
2008-12-11 15:49:59 +03:00
if ( ! trace )
2009-04-03 18:43:35 +04:00
return - ESRCH ;
2008-01-30 15:31:20 +03:00
2008-12-11 15:49:59 +03:00
at = trace - > ds . top - ( ( index + 1 ) * trace - > ds . size ) ;
if ( ( void * ) at < trace - > ds . begin )
at + = ( trace - > ds . n * trace - > ds . size ) ;
2008-04-08 13:01:58 +04:00
2008-12-11 15:49:59 +03:00
if ( ! trace - > read )
return - EOPNOTSUPP ;
2008-04-08 13:01:58 +04:00
2009-04-03 18:43:35 +04:00
error = trace - > read ( context - > tracer , at , & bts ) ;
2008-04-08 13:01:58 +04:00
if ( error < 0 )
return error ;
2008-01-30 15:31:20 +03:00
2008-12-11 15:49:59 +03:00
if ( copy_to_user ( out , & bts , sizeof ( bts ) ) )
2008-01-30 15:31:09 +03:00
return - EFAULT ;
2008-12-11 15:49:59 +03:00
return sizeof ( bts ) ;
2008-01-30 15:31:09 +03:00
}
2008-01-30 15:31:20 +03:00
static int ptrace_bts_drain ( struct task_struct * child ,
2008-01-30 15:32:03 +03:00
long size ,
2008-01-30 15:31:20 +03:00
struct bts_struct __user * out )
{
2009-04-03 18:43:35 +04:00
struct bts_context * context ;
2008-12-11 15:49:59 +03:00
const struct bts_trace * trace ;
const unsigned char * at ;
int error , drained = 0 ;
2008-01-30 15:31:09 +03:00
2009-04-03 18:43:35 +04:00
context = child - > bts ;
if ( ! context )
return - ESRCH ;
trace = ds_read_bts ( context - > tracer ) ;
2008-12-11 15:49:59 +03:00
if ( ! trace )
2009-04-03 18:43:35 +04:00
return - ESRCH ;
2008-01-30 15:31:20 +03:00
2008-12-11 15:49:59 +03:00
if ( ! trace - > read )
return - EOPNOTSUPP ;
if ( size < ( trace - > ds . top - trace - > ds . begin ) )
2008-01-30 15:32:03 +03:00
return - EIO ;
2008-12-11 15:49:59 +03:00
for ( at = trace - > ds . begin ; ( void * ) at < trace - > ds . top ;
out + + , drained + + , at + = trace - > ds . size ) {
struct bts_struct bts ;
2008-01-30 15:31:20 +03:00
2009-04-03 18:43:35 +04:00
error = trace - > read ( context - > tracer , at , & bts ) ;
2008-12-11 15:49:59 +03:00
if ( error < 0 )
return error ;
2008-01-30 15:31:20 +03:00
2008-12-11 15:49:59 +03:00
if ( copy_to_user ( out , & bts , sizeof ( bts ) ) )
2008-01-30 15:31:20 +03:00
return - EFAULT ;
}
2008-12-11 15:49:59 +03:00
memset ( trace - > ds . begin , 0 , trace - > ds . n * trace - > ds . size ) ;
2009-04-03 18:43:35 +04:00
error = ds_reset_bts ( context - > tracer ) ;
2008-04-08 13:01:58 +04:00
if ( error < 0 )
return error ;
2008-01-30 15:31:20 +03:00
2008-12-11 15:49:59 +03:00
return drained ;
2008-01-30 15:31:20 +03:00
}
static int ptrace_bts_config ( struct task_struct * child ,
2008-01-30 15:32:03 +03:00
long cfg_size ,
2008-01-30 15:31:20 +03:00
const struct ptrace_bts_config __user * ucfg )
{
2009-04-03 18:43:35 +04:00
struct bts_context * context ;
2008-01-30 15:31:20 +03:00
struct ptrace_bts_config cfg ;
2008-12-11 15:49:59 +03:00
unsigned int flags = 0 ;
2008-01-30 15:31:20 +03:00
2008-01-30 15:32:03 +03:00
if ( cfg_size < sizeof ( cfg ) )
2008-12-11 15:49:59 +03:00
return - EIO ;
2008-01-30 15:32:03 +03:00
2008-01-30 15:31:20 +03:00
if ( copy_from_user ( & cfg , ucfg , sizeof ( cfg ) ) )
2008-12-11 15:49:59 +03:00
return - EFAULT ;
2008-11-25 11:05:27 +03:00
2009-04-03 18:43:35 +04:00
context = child - > bts ;
if ( ! context )
context = alloc_bts_context ( child ) ;
if ( ! context )
return - ENOMEM ;
2008-04-08 13:01:58 +04:00
2008-12-11 15:49:59 +03:00
if ( cfg . flags & PTRACE_BTS_O_SIGNAL ) {
if ( ! cfg . signal )
return - EINVAL ;
2008-11-25 11:01:25 +03:00
2009-03-13 10:56:58 +03:00
return - EOPNOTSUPP ;
2009-04-03 18:43:35 +04:00
context - > bts_ovfl_signal = cfg . signal ;
2008-12-11 15:49:59 +03:00
}
2008-11-25 11:05:27 +03:00
2009-04-03 18:43:35 +04:00
ds_release_bts ( context - > tracer ) ;
context - > tracer = NULL ;
2008-11-25 11:05:27 +03:00
2009-04-03 18:43:35 +04:00
if ( ( cfg . flags & PTRACE_BTS_O_ALLOC ) & & ( cfg . size ! = context - > size ) ) {
2009-04-24 11:51:43 +04:00
int err ;
2009-04-03 18:43:35 +04:00
free_bts_buffer ( context ) ;
if ( ! cfg . size )
return 0 ;
2008-12-19 17:17:02 +03:00
2009-04-24 11:51:43 +04:00
err = alloc_bts_buffer ( context , cfg . size ) ;
if ( err < 0 )
return err ;
2008-01-30 15:31:20 +03:00
}
2008-01-30 15:32:03 +03:00
if ( cfg . flags & PTRACE_BTS_O_TRACE )
2008-12-11 15:49:59 +03:00
flags | = BTS_USER ;
2008-01-30 15:31:09 +03:00
2008-01-30 15:32:03 +03:00
if ( cfg . flags & PTRACE_BTS_O_SCHED )
2008-12-11 15:49:59 +03:00
flags | = BTS_TIMESTAMPS ;
2008-01-30 15:31:09 +03:00
2009-04-03 18:43:40 +04:00
context - > tracer =
ds_request_bts_task ( child , context - > buffer , context - > size ,
NULL , ( size_t ) - 1 , flags ) ;
2009-04-03 18:43:35 +04:00
if ( unlikely ( IS_ERR ( context - > tracer ) ) ) {
int error = PTR_ERR ( context - > tracer ) ;
2008-01-30 15:32:03 +03:00
2009-04-03 18:43:35 +04:00
free_bts_buffer ( context ) ;
context - > tracer = NULL ;
2008-12-11 15:49:59 +03:00
return error ;
}
2008-01-30 15:32:03 +03:00
2008-12-11 15:49:59 +03:00
return sizeof ( cfg ) ;
2008-01-30 15:31:09 +03:00
}
2008-01-30 15:31:20 +03:00
static int ptrace_bts_status ( struct task_struct * child ,
2008-01-30 15:32:03 +03:00
long cfg_size ,
2008-01-30 15:31:20 +03:00
struct ptrace_bts_config __user * ucfg )
2008-01-30 15:31:09 +03:00
{
2009-04-03 18:43:35 +04:00
struct bts_context * context ;
2008-12-11 15:49:59 +03:00
const struct bts_trace * trace ;
2008-01-30 15:31:20 +03:00
struct ptrace_bts_config cfg ;
2008-01-30 15:31:09 +03:00
2009-04-03 18:43:35 +04:00
context = child - > bts ;
if ( ! context )
return - ESRCH ;
2008-01-30 15:32:03 +03:00
if ( cfg_size < sizeof ( cfg ) )
return - EIO ;
2009-04-03 18:43:35 +04:00
trace = ds_read_bts ( context - > tracer ) ;
2008-12-11 15:49:59 +03:00
if ( ! trace )
2009-04-03 18:43:35 +04:00
return - ESRCH ;
2008-01-30 15:31:09 +03:00
2008-04-08 13:01:58 +04:00
memset ( & cfg , 0 , sizeof ( cfg ) ) ;
2009-04-03 18:43:35 +04:00
cfg . size = trace - > ds . end - trace - > ds . begin ;
cfg . signal = context - > bts_ovfl_signal ;
cfg . bts_size = sizeof ( struct bts_struct ) ;
2008-01-30 15:31:09 +03:00
2008-04-08 13:01:58 +04:00
if ( cfg . signal )
cfg . flags | = PTRACE_BTS_O_SIGNAL ;
2008-01-30 15:31:09 +03:00
2008-12-11 15:49:59 +03:00
if ( trace - > ds . flags & BTS_USER )
2008-04-08 13:01:58 +04:00
cfg . flags | = PTRACE_BTS_O_TRACE ;
2008-12-11 15:49:59 +03:00
if ( trace - > ds . flags & BTS_TIMESTAMPS )
2008-04-08 13:01:58 +04:00
cfg . flags | = PTRACE_BTS_O_SCHED ;
2008-01-30 15:32:54 +03:00
2008-01-30 15:31:20 +03:00
if ( copy_to_user ( ucfg , & cfg , sizeof ( cfg ) ) )
return - EFAULT ;
2008-01-30 15:31:09 +03:00
2008-01-30 15:31:20 +03:00
return sizeof ( cfg ) ;
2008-01-30 15:31:09 +03:00
}
2008-12-11 15:49:59 +03:00
static int ptrace_bts_clear ( struct task_struct * child )
2008-03-05 02:05:39 +03:00
{
2009-04-03 18:43:35 +04:00
struct bts_context * context ;
2008-12-11 15:49:59 +03:00
const struct bts_trace * trace ;
2008-03-05 02:05:39 +03:00
2009-04-03 18:43:35 +04:00
context = child - > bts ;
if ( ! context )
return - ESRCH ;
trace = ds_read_bts ( context - > tracer ) ;
2008-12-11 15:49:59 +03:00
if ( ! trace )
2009-04-03 18:43:35 +04:00
return - ESRCH ;
2008-03-05 02:05:39 +03:00
2008-12-11 15:49:59 +03:00
memset ( trace - > ds . begin , 0 , trace - > ds . n * trace - > ds . size ) ;
2008-03-05 02:05:39 +03:00
2009-04-03 18:43:35 +04:00
return ds_reset_bts ( context - > tracer ) ;
2008-03-05 02:05:39 +03:00
}
2008-12-11 15:49:59 +03:00
static int ptrace_bts_size ( struct task_struct * child )
2008-01-30 15:31:09 +03:00
{
2009-04-03 18:43:35 +04:00
struct bts_context * context ;
2008-12-11 15:49:59 +03:00
const struct bts_trace * trace ;
2008-04-08 13:01:58 +04:00
2009-04-03 18:43:35 +04:00
context = child - > bts ;
if ( ! context )
return - ESRCH ;
trace = ds_read_bts ( context - > tracer ) ;
2008-12-11 15:49:59 +03:00
if ( ! trace )
2009-04-03 18:43:35 +04:00
return - ESRCH ;
2008-04-08 13:01:58 +04:00
2008-12-11 15:49:59 +03:00
return ( trace - > ds . top - trace - > ds . begin ) / trace - > ds . size ;
2008-04-08 13:01:58 +04:00
}
2008-12-19 17:10:24 +03:00
2009-04-03 18:43:35 +04:00
/*
* Called from __ptrace_unlink ( ) after the child has been moved back
* to its original parent .
*/
2009-04-03 18:43:48 +04:00
void ptrace_bts_untrace ( struct task_struct * child )
2008-12-19 17:10:24 +03:00
{
if ( unlikely ( child - > bts ) ) {
2009-04-03 18:43:35 +04:00
free_bts_context ( child - > bts ) ;
2008-12-19 17:10:24 +03:00
child - > bts = NULL ;
}
}
2008-04-08 13:01:58 +04:00
# endif /* CONFIG_X86_PTRACE_BTS */
2008-01-30 15:31:09 +03:00
2005-04-17 02:20:36 +04:00
/*
* Called by kernel / ptrace . c when detaching . .
*
* Make sure the single step bit is not set .
*/
void ptrace_disable ( struct task_struct * child )
2008-01-30 15:30:58 +03:00
{
2008-01-30 15:30:48 +03:00
user_disable_single_step ( child ) ;
2008-01-30 15:31:01 +03:00
# ifdef TIF_SYSCALL_EMU
2005-09-04 02:57:21 +04:00
clear_tsk_thread_flag ( child , TIF_SYSCALL_EMU ) ;
2008-01-30 15:31:01 +03:00
# endif
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:54 +03:00
# if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
static const struct user_regset_view user_x86_32_view ; /* Initialized below. */
# endif
2005-11-07 11:59:47 +03:00
long arch_ptrace ( struct task_struct * child , long request , long addr , long data )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:31:54 +03:00
int ret ;
2005-04-17 02:20:36 +04:00
unsigned long __user * datap = ( unsigned long __user * ) data ;
switch ( request ) {
/* read the word at location addr in the USER area. */
case PTRACE_PEEKUSR : {
unsigned long tmp ;
ret = - EIO ;
2008-01-30 15:31:01 +03:00
if ( ( addr & ( sizeof ( data ) - 1 ) ) | | addr < 0 | |
addr > = sizeof ( struct user ) )
2005-04-17 02:20:36 +04:00
break ;
tmp = 0 ; /* Default return condition */
2008-01-30 15:31:01 +03:00
if ( addr < sizeof ( struct user_regs_struct ) )
2005-04-17 02:20:36 +04:00
tmp = getreg ( child , addr ) ;
2008-01-30 15:31:01 +03:00
else if ( addr > = offsetof ( struct user , u_debugreg [ 0 ] ) & &
addr < = offsetof ( struct user , u_debugreg [ 7 ] ) ) {
addr - = offsetof ( struct user , u_debugreg [ 0 ] ) ;
tmp = ptrace_get_debugreg ( child , addr / sizeof ( data ) ) ;
2005-04-17 02:20:36 +04:00
}
ret = put_user ( tmp , datap ) ;
break ;
}
case PTRACE_POKEUSR : /* write the word at location addr in the USER area */
ret = - EIO ;
2008-01-30 15:31:01 +03:00
if ( ( addr & ( sizeof ( data ) - 1 ) ) | | addr < 0 | |
addr > = sizeof ( struct user ) )
2005-04-17 02:20:36 +04:00
break ;
2008-01-30 15:31:01 +03:00
if ( addr < sizeof ( struct user_regs_struct ) )
2005-04-17 02:20:36 +04:00
ret = putreg ( child , addr , data ) ;
2008-01-30 15:31:01 +03:00
else if ( addr > = offsetof ( struct user , u_debugreg [ 0 ] ) & &
addr < = offsetof ( struct user , u_debugreg [ 7 ] ) ) {
addr - = offsetof ( struct user , u_debugreg [ 0 ] ) ;
ret = ptrace_set_debugreg ( child ,
addr / sizeof ( data ) , data ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:31:01 +03:00
break ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:54 +03:00
case PTRACE_GETREGS : /* Get all gp regs from the child. */
return copy_regset_to_user ( child ,
task_user_regset_view ( current ) ,
REGSET_GENERAL ,
0 , sizeof ( struct user_regs_struct ) ,
datap ) ;
case PTRACE_SETREGS : /* Set all gp regs in the child. */
return copy_regset_from_user ( child ,
task_user_regset_view ( current ) ,
REGSET_GENERAL ,
0 , sizeof ( struct user_regs_struct ) ,
datap ) ;
case PTRACE_GETFPREGS : /* Get the child FPU state. */
return copy_regset_to_user ( child ,
task_user_regset_view ( current ) ,
REGSET_FP ,
0 , sizeof ( struct user_i387_struct ) ,
datap ) ;
case PTRACE_SETFPREGS : /* Set the child FPU state. */
return copy_regset_from_user ( child ,
task_user_regset_view ( current ) ,
REGSET_FP ,
0 , sizeof ( struct user_i387_struct ) ,
datap ) ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:01 +03:00
# ifdef CONFIG_X86_32
2008-01-30 15:31:54 +03:00
case PTRACE_GETFPXREGS : /* Get the child extended FPU state. */
return copy_regset_to_user ( child , & user_x86_32_view ,
REGSET_XFP ,
0 , sizeof ( struct user_fxsr_struct ) ,
2008-07-01 01:02:41 +04:00
datap ) ? - EIO : 0 ;
2008-01-30 15:31:54 +03:00
case PTRACE_SETFPXREGS : /* Set the child extended FPU state. */
return copy_regset_from_user ( child , & user_x86_32_view ,
REGSET_XFP ,
0 , sizeof ( struct user_fxsr_struct ) ,
2008-07-01 01:02:41 +04:00
datap ) ? - EIO : 0 ;
2008-01-30 15:31:01 +03:00
# endif
2005-04-17 02:20:36 +04:00
2008-01-30 15:31:01 +03:00
# if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
2005-04-17 02:20:36 +04:00
case PTRACE_GET_THREAD_AREA :
2008-01-30 15:30:46 +03:00
if ( addr < 0 )
return - EIO ;
ret = do_get_thread_area ( child , addr ,
( struct user_desc __user * ) data ) ;
2005-04-17 02:20:36 +04:00
break ;
case PTRACE_SET_THREAD_AREA :
2008-01-30 15:30:46 +03:00
if ( addr < 0 )
return - EIO ;
ret = do_set_thread_area ( child , addr ,
( struct user_desc __user * ) data , 0 ) ;
2005-04-17 02:20:36 +04:00
break ;
2008-01-30 15:31:01 +03:00
# endif
# ifdef CONFIG_X86_64
/* normal 64bit interface to access TLS data.
Works just like arch_prctl , except that the arguments
are reversed . */
case PTRACE_ARCH_PRCTL :
ret = do_arch_prctl ( child , data , addr ) ;
break ;
# endif
2005-04-17 02:20:36 +04:00
2008-02-26 11:40:27 +03:00
/*
* These bits need more cooking - not enabled yet :
*/
2008-04-08 13:01:58 +04:00
# ifdef CONFIG_X86_PTRACE_BTS
2008-01-30 15:31:20 +03:00
case PTRACE_BTS_CONFIG :
ret = ptrace_bts_config
2008-01-30 15:32:03 +03:00
( child , data , ( struct ptrace_bts_config __user * ) addr ) ;
2008-01-30 15:31:09 +03:00
break ;
2008-01-30 15:31:20 +03:00
case PTRACE_BTS_STATUS :
ret = ptrace_bts_status
2008-01-30 15:32:03 +03:00
( child , data , ( struct ptrace_bts_config __user * ) addr ) ;
2008-01-30 15:31:09 +03:00
break ;
2008-12-11 15:49:59 +03:00
case PTRACE_BTS_SIZE :
ret = ptrace_bts_size ( child ) ;
2008-01-30 15:31:09 +03:00
break ;
2008-01-30 15:31:20 +03:00
case PTRACE_BTS_GET :
2008-01-30 15:31:09 +03:00
ret = ptrace_bts_read_record
2008-01-30 15:31:20 +03:00
( child , data , ( struct bts_struct __user * ) addr ) ;
2008-01-30 15:31:09 +03:00
break ;
2008-01-30 15:31:20 +03:00
case PTRACE_BTS_CLEAR :
2008-12-11 15:49:59 +03:00
ret = ptrace_bts_clear ( child ) ;
2008-01-30 15:31:09 +03:00
break ;
2008-01-30 15:31:20 +03:00
case PTRACE_BTS_DRAIN :
ret = ptrace_bts_drain
2008-01-30 15:32:03 +03:00
( child , data , ( struct bts_struct __user * ) addr ) ;
2008-01-30 15:31:09 +03:00
break ;
2008-04-08 13:01:58 +04:00
# endif /* CONFIG_X86_PTRACE_BTS */
2008-01-30 15:31:09 +03:00
2005-04-17 02:20:36 +04:00
default :
ret = ptrace_request ( child , request , addr , data ) ;
break ;
}
2008-01-30 15:30:52 +03:00
2005-04-17 02:20:36 +04:00
return ret ;
}
2008-01-30 15:31:01 +03:00
# ifdef CONFIG_IA32_EMULATION
2008-01-30 15:31:01 +03:00
# include <linux/compat.h>
# include <linux/syscalls.h>
# include <asm/ia32.h>
2008-01-30 15:31:01 +03:00
# include <asm/user32.h>
# define R32(l,q) \
case offsetof ( struct user32 , regs . l ) : \
regs - > q = value ; break
# define SEG32(rs) \
case offsetof ( struct user32 , regs . rs ) : \
return set_segment_reg ( child , \
offsetof ( struct user_regs_struct , rs ) , \
value ) ; \
break
static int putreg32 ( struct task_struct * child , unsigned regno , u32 value )
{
struct pt_regs * regs = task_pt_regs ( child ) ;
switch ( regno ) {
SEG32 ( cs ) ;
SEG32 ( ds ) ;
SEG32 ( es ) ;
SEG32 ( fs ) ;
SEG32 ( gs ) ;
SEG32 ( ss ) ;
R32 ( ebx , bx ) ;
R32 ( ecx , cx ) ;
R32 ( edx , dx ) ;
R32 ( edi , di ) ;
R32 ( esi , si ) ;
R32 ( ebp , bp ) ;
R32 ( eax , ax ) ;
R32 ( eip , ip ) ;
R32 ( esp , sp ) ;
2008-02-29 06:57:07 +03:00
case offsetof ( struct user32 , regs . orig_eax ) :
/*
* Sign - extend the value so that orig_eax = - 1
* causes ( long ) orig_ax < 0 tests to fire correctly .
*/
regs - > orig_ax = ( long ) ( s32 ) value ;
break ;
2008-01-30 15:31:01 +03:00
case offsetof ( struct user32 , regs . eflags ) :
return set_flags ( child , value ) ;
case offsetof ( struct user32 , u_debugreg [ 0 ] ) . . .
offsetof ( struct user32 , u_debugreg [ 7 ] ) :
regno - = offsetof ( struct user32 , u_debugreg [ 0 ] ) ;
return ptrace_set_debugreg ( child , regno / 4 , value ) ;
default :
if ( regno > sizeof ( struct user32 ) | | ( regno & 3 ) )
return - EIO ;
/*
* Other dummy fields in the virtual user structure
* are ignored
*/
break ;
}
return 0 ;
}
# undef R32
# undef SEG32
# define R32(l,q) \
case offsetof ( struct user32 , regs . l ) : \
* val = regs - > q ; break
# define SEG32(rs) \
case offsetof ( struct user32 , regs . rs ) : \
* val = get_segment_reg ( child , \
offsetof ( struct user_regs_struct , rs ) ) ; \
break
static int getreg32 ( struct task_struct * child , unsigned regno , u32 * val )
{
struct pt_regs * regs = task_pt_regs ( child ) ;
switch ( regno ) {
SEG32 ( ds ) ;
SEG32 ( es ) ;
SEG32 ( fs ) ;
SEG32 ( gs ) ;
R32 ( cs , cs ) ;
R32 ( ss , ss ) ;
R32 ( ebx , bx ) ;
R32 ( ecx , cx ) ;
R32 ( edx , dx ) ;
R32 ( edi , di ) ;
R32 ( esi , si ) ;
R32 ( ebp , bp ) ;
R32 ( eax , ax ) ;
R32 ( orig_eax , orig_ax ) ;
R32 ( eip , ip ) ;
R32 ( esp , sp ) ;
case offsetof ( struct user32 , regs . eflags ) :
* val = get_flags ( child ) ;
break ;
case offsetof ( struct user32 , u_debugreg [ 0 ] ) . . .
offsetof ( struct user32 , u_debugreg [ 7 ] ) :
regno - = offsetof ( struct user32 , u_debugreg [ 0 ] ) ;
* val = ptrace_get_debugreg ( child , regno / 4 ) ;
break ;
default :
if ( regno > sizeof ( struct user32 ) | | ( regno & 3 ) )
return - EIO ;
/*
* Other dummy fields in the virtual user structure
* are ignored
*/
* val = 0 ;
break ;
}
return 0 ;
}
# undef R32
# undef SEG32
2008-01-30 15:31:52 +03:00
static int genregs32_get ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
void * kbuf , void __user * ubuf )
{
if ( kbuf ) {
compat_ulong_t * k = kbuf ;
while ( count > 0 ) {
getreg32 ( target , pos , k + + ) ;
count - = sizeof ( * k ) ;
pos + = sizeof ( * k ) ;
}
} else {
compat_ulong_t __user * u = ubuf ;
while ( count > 0 ) {
compat_ulong_t word ;
getreg32 ( target , pos , & word ) ;
if ( __put_user ( word , u + + ) )
return - EFAULT ;
count - = sizeof ( * u ) ;
pos + = sizeof ( * u ) ;
}
}
return 0 ;
}
static int genregs32_set ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
const void * kbuf , const void __user * ubuf )
{
int ret = 0 ;
if ( kbuf ) {
const compat_ulong_t * k = kbuf ;
while ( count > 0 & & ! ret ) {
2008-02-22 07:37:24 +03:00
ret = putreg32 ( target , pos , * k + + ) ;
2008-01-30 15:31:52 +03:00
count - = sizeof ( * k ) ;
pos + = sizeof ( * k ) ;
}
} else {
const compat_ulong_t __user * u = ubuf ;
while ( count > 0 & & ! ret ) {
compat_ulong_t word ;
ret = __get_user ( word , u + + ) ;
if ( ret )
break ;
2008-02-22 07:37:24 +03:00
ret = putreg32 ( target , pos , word ) ;
2008-01-30 15:31:52 +03:00
count - = sizeof ( * u ) ;
pos + = sizeof ( * u ) ;
}
}
return ret ;
}
2008-04-22 23:21:25 +04:00
long compat_arch_ptrace ( struct task_struct * child , compat_long_t request ,
compat_ulong_t caddr , compat_ulong_t cdata )
2008-01-30 15:31:01 +03:00
{
2008-04-22 23:21:25 +04:00
unsigned long addr = caddr ;
unsigned long data = cdata ;
2008-01-30 15:31:01 +03:00
void __user * datap = compat_ptr ( data ) ;
int ret ;
__u32 val ;
switch ( request ) {
case PTRACE_PEEKUSR :
ret = getreg32 ( child , addr , & val ) ;
if ( ret = = 0 )
ret = put_user ( val , ( __u32 __user * ) datap ) ;
break ;
case PTRACE_POKEUSR :
ret = putreg32 ( child , addr , data ) ;
break ;
2008-01-30 15:31:54 +03:00
case PTRACE_GETREGS : /* Get all gp regs from the child. */
return copy_regset_to_user ( child , & user_x86_32_view ,
REGSET_GENERAL ,
0 , sizeof ( struct user_regs_struct32 ) ,
datap ) ;
case PTRACE_SETREGS : /* Set all gp regs in the child. */
return copy_regset_from_user ( child , & user_x86_32_view ,
REGSET_GENERAL , 0 ,
sizeof ( struct user_regs_struct32 ) ,
datap ) ;
case PTRACE_GETFPREGS : /* Get the child FPU state. */
return copy_regset_to_user ( child , & user_x86_32_view ,
REGSET_FP , 0 ,
sizeof ( struct user_i387_ia32_struct ) ,
datap ) ;
case PTRACE_SETFPREGS : /* Set the child FPU state. */
return copy_regset_from_user (
child , & user_x86_32_view , REGSET_FP ,
0 , sizeof ( struct user_i387_ia32_struct ) , datap ) ;
case PTRACE_GETFPXREGS : /* Get the child extended FPU state. */
return copy_regset_to_user ( child , & user_x86_32_view ,
REGSET_XFP , 0 ,
sizeof ( struct user32_fxsr_struct ) ,
datap ) ;
case PTRACE_SETFPXREGS : /* Set the child extended FPU state. */
return copy_regset_from_user ( child , & user_x86_32_view ,
REGSET_XFP , 0 ,
sizeof ( struct user32_fxsr_struct ) ,
datap ) ;
2008-01-30 15:31:01 +03:00
2008-04-22 23:21:25 +04:00
case PTRACE_GET_THREAD_AREA :
case PTRACE_SET_THREAD_AREA :
2008-12-11 15:49:59 +03:00
# ifdef CONFIG_X86_PTRACE_BTS
case PTRACE_BTS_CONFIG :
case PTRACE_BTS_STATUS :
case PTRACE_BTS_SIZE :
case PTRACE_BTS_GET :
case PTRACE_BTS_CLEAR :
case PTRACE_BTS_DRAIN :
# endif /* CONFIG_X86_PTRACE_BTS */
2008-04-22 23:21:25 +04:00
return arch_ptrace ( child , request , addr , data ) ;
2008-01-30 15:31:01 +03:00
default :
2008-01-30 15:31:56 +03:00
return compat_ptrace_request ( child , request , addr , data ) ;
2008-01-30 15:31:01 +03:00
}
return ret ;
}
2008-01-30 15:31:01 +03:00
# endif /* CONFIG_IA32_EMULATION */
2008-01-30 15:31:53 +03:00
# ifdef CONFIG_X86_64
static const struct user_regset x86_64_regsets [ ] = {
[ REGSET_GENERAL ] = {
. core_note_type = NT_PRSTATUS ,
. n = sizeof ( struct user_regs_struct ) / sizeof ( long ) ,
. size = sizeof ( long ) , . align = sizeof ( long ) ,
. get = genregs_get , . set = genregs_set
} ,
[ REGSET_FP ] = {
. core_note_type = NT_PRFPREG ,
. n = sizeof ( struct user_i387_struct ) / sizeof ( long ) ,
. size = sizeof ( long ) , . align = sizeof ( long ) ,
. active = xfpregs_active , . get = xfpregs_get , . set = xfpregs_set
} ,
2008-08-09 02:58:39 +04:00
[ REGSET_IOPERM64 ] = {
. core_note_type = NT_386_IOPERM ,
. n = IO_BITMAP_LONGS ,
. size = sizeof ( long ) , . align = sizeof ( long ) ,
. active = ioperm_active , . get = ioperm_get
} ,
2008-01-30 15:31:53 +03:00
} ;
static const struct user_regset_view user_x86_64_view = {
. name = " x86_64 " , . e_machine = EM_X86_64 ,
. regsets = x86_64_regsets , . n = ARRAY_SIZE ( x86_64_regsets )
} ;
# else /* CONFIG_X86_32 */
# define user_regs_struct32 user_regs_struct
# define genregs32_get genregs_get
# define genregs32_set genregs_set
2008-05-10 02:43:44 +04:00
# define user_i387_ia32_struct user_i387_struct
# define user32_fxsr_struct user_fxsr_struct
2008-01-30 15:31:53 +03:00
# endif /* CONFIG_X86_64 */
# if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
static const struct user_regset x86_32_regsets [ ] = {
[ REGSET_GENERAL ] = {
. core_note_type = NT_PRSTATUS ,
. n = sizeof ( struct user_regs_struct32 ) / sizeof ( u32 ) ,
. size = sizeof ( u32 ) , . align = sizeof ( u32 ) ,
. get = genregs32_get , . set = genregs32_set
} ,
[ REGSET_FP ] = {
. core_note_type = NT_PRFPREG ,
2008-05-10 02:43:44 +04:00
. n = sizeof ( struct user_i387_ia32_struct ) / sizeof ( u32 ) ,
2008-01-30 15:31:53 +03:00
. size = sizeof ( u32 ) , . align = sizeof ( u32 ) ,
. active = fpregs_active , . get = fpregs_get , . set = fpregs_set
} ,
[ REGSET_XFP ] = {
. core_note_type = NT_PRXFPREG ,
2008-05-10 02:43:44 +04:00
. n = sizeof ( struct user32_fxsr_struct ) / sizeof ( u32 ) ,
2008-01-30 15:31:53 +03:00
. size = sizeof ( u32 ) , . align = sizeof ( u32 ) ,
. active = xfpregs_active , . get = xfpregs_get , . set = xfpregs_set
} ,
[ REGSET_TLS ] = {
2008-01-30 15:31:56 +03:00
. core_note_type = NT_386_TLS ,
2008-01-30 15:31:53 +03:00
. n = GDT_ENTRY_TLS_ENTRIES , . bias = GDT_ENTRY_TLS_MIN ,
. size = sizeof ( struct user_desc ) ,
. align = sizeof ( struct user_desc ) ,
. active = regset_tls_active ,
. get = regset_tls_get , . set = regset_tls_set
} ,
2008-08-09 02:58:39 +04:00
[ REGSET_IOPERM32 ] = {
. core_note_type = NT_386_IOPERM ,
. n = IO_BITMAP_BYTES / sizeof ( u32 ) ,
. size = sizeof ( u32 ) , . align = sizeof ( u32 ) ,
. active = ioperm_active , . get = ioperm_get
} ,
2008-01-30 15:31:53 +03:00
} ;
static const struct user_regset_view user_x86_32_view = {
. name = " i386 " , . e_machine = EM_386 ,
. regsets = x86_32_regsets , . n = ARRAY_SIZE ( x86_32_regsets )
} ;
# endif
const struct user_regset_view * task_user_regset_view ( struct task_struct * task )
{
# ifdef CONFIG_IA32_EMULATION
if ( test_tsk_thread_flag ( task , TIF_IA32 ) )
# endif
# if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
return & user_x86_32_view ;
# endif
# ifdef CONFIG_X86_64
return & user_x86_64_view ;
# endif
}
2008-09-23 13:53:52 +04:00
void send_sigtrap ( struct task_struct * tsk , struct pt_regs * regs ,
int error_code , int si_code )
2005-04-17 02:20:36 +04:00
{
struct siginfo info ;
tsk - > thread . trap_no = 1 ;
tsk - > thread . error_code = error_code ;
memset ( & info , 0 , sizeof ( info ) ) ;
info . si_signo = SIGTRAP ;
2008-09-23 13:53:52 +04:00
info . si_code = si_code ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:30:56 +03:00
/* User-mode ip? */
info . si_addr = user_mode_vm ( regs ) ? ( void __user * ) regs - > ip : NULL ;
2005-04-17 02:20:36 +04:00
2007-10-20 03:13:56 +04:00
/* Send us the fake SIGTRAP */
2005-04-17 02:20:36 +04:00
force_sig_info ( SIGTRAP , & info , tsk ) ;
}
2008-01-30 15:31:01 +03:00
2008-07-09 13:38:07 +04:00
# ifdef CONFIG_X86_32
# define IS_IA32 1
# elif defined CONFIG_IA32_EMULATION
2009-02-28 06:03:24 +03:00
# define IS_IA32 is_compat_task()
2008-07-09 13:38:07 +04:00
# else
# define IS_IA32 0
# endif
/*
* We must return the syscall number to actually look up in the table .
* This can be - 1L to skip running any syscall at all .
*/
asmregparm long syscall_trace_enter ( struct pt_regs * regs )
2008-01-30 15:31:01 +03:00
{
2008-07-09 13:38:07 +04:00
long ret = 0 ;
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 . If we entered on the slow path , TF was already set .
*/
if ( test_thread_flag ( TIF_SINGLESTEP ) )
regs - > flags | = X86_EFLAGS_TF ;
2008-01-30 15:31:01 +03:00
/* do the secure computing check first */
secure_computing ( regs - > orig_ax ) ;
2008-07-09 13:38:07 +04:00
if ( unlikely ( test_thread_flag ( TIF_SYSCALL_EMU ) ) )
ret = - 1L ;
2008-03-17 09:36:28 +03:00
if ( ( ret | | test_thread_flag ( TIF_SYSCALL_TRACE ) ) & &
tracehook_report_syscall_entry ( regs ) )
ret = - 1L ;
2008-01-30 15:31:01 +03:00
2009-03-07 07:53:00 +03:00
if ( unlikely ( test_thread_flag ( TIF_SYSCALL_FTRACE ) ) )
ftrace_syscall_enter ( regs ) ;
2008-01-30 15:31:01 +03:00
if ( unlikely ( current - > audit_context ) ) {
2008-07-09 13:38:07 +04:00
if ( IS_IA32 )
2008-01-30 15:31:01 +03:00
audit_syscall_entry ( AUDIT_ARCH_I386 ,
regs - > orig_ax ,
regs - > bx , regs - > cx ,
regs - > dx , regs - > si ) ;
2008-07-09 13:38:07 +04:00
# ifdef CONFIG_X86_64
else
2008-01-30 15:31:01 +03:00
audit_syscall_entry ( AUDIT_ARCH_X86_64 ,
regs - > orig_ax ,
regs - > di , regs - > si ,
regs - > dx , regs - > r10 ) ;
2008-07-09 13:38:07 +04:00
# endif
2008-01-30 15:31:01 +03:00
}
2008-07-09 13:38:07 +04:00
return ret ? : regs - > orig_ax ;
2008-01-30 15:31:01 +03:00
}
2008-07-09 13:38:07 +04:00
asmregparm void syscall_trace_leave ( struct pt_regs * regs )
2008-01-30 15:31:01 +03:00
{
if ( unlikely ( current - > audit_context ) )
audit_syscall_exit ( AUDITSC_RESULT ( regs - > ax ) , regs - > ax ) ;
2009-03-07 07:53:00 +03:00
if ( unlikely ( test_thread_flag ( TIF_SYSCALL_FTRACE ) ) )
ftrace_syscall_exit ( regs ) ;
2008-07-09 13:38:07 +04:00
if ( test_thread_flag ( TIF_SYSCALL_TRACE ) )
2008-03-17 09:36:28 +03:00
tracehook_report_syscall_exit ( regs , 0 ) ;
2008-01-30 15:31:01 +03:00
2008-07-09 13:38:07 +04:00
/*
* If TIF_SYSCALL_EMU is set , we only get here because of
* TIF_SINGLESTEP ( i . e . this is PTRACE_SYSEMU_SINGLESTEP ) .
* We already reported this syscall instruction in
* syscall_trace_enter ( ) , so don ' t do any more now .
*/
if ( unlikely ( test_thread_flag ( TIF_SYSCALL_EMU ) ) )
return ;
/*
* If we are single - stepping , synthesize a trap to follow the
* system call instruction .
*/
if ( test_thread_flag ( TIF_SINGLESTEP ) & &
2009-04-03 03:58:00 +04:00
tracehook_consider_fatal_signal ( current , SIGTRAP ) )
2008-09-23 13:53:52 +04:00
send_sigtrap ( current , regs , 0 , TRAP_BRKPT ) ;
2008-07-09 13:38:07 +04:00
}