2018-08-27 11:12:24 +02:00
// SPDX-License-Identifier: GPL-2.0
# include <linux/compiler.h>
# include <sys/types.h>
# include <sys/wait.h>
# include <sys/user.h>
# include <syscall.h>
# include <unistd.h>
# include <stdio.h>
# include <stdlib.h>
2019-08-29 16:18:59 -03:00
# include <string.h>
2018-08-27 11:12:24 +02:00
# include <sys/ptrace.h>
# include <asm/ptrace.h>
# include <errno.h>
# include "debug.h"
# include "tests/tests.h"
# include "arch-tests.h"
static noinline int bp_1 ( void )
{
pr_debug ( " in %s \n " , __func__ ) ;
return 0 ;
}
static noinline int bp_2 ( void )
{
pr_debug ( " in %s \n " , __func__ ) ;
return 0 ;
}
static int spawn_child ( void )
{
int child = fork ( ) ;
if ( child = = 0 ) {
/*
* The child sets itself for as tracee and
* waits in signal for parent to trace it ,
* then it calls bp_1 and quits .
*/
int err = ptrace ( PTRACE_TRACEME , 0 , NULL , NULL ) ;
if ( err ) {
pr_debug ( " failed to PTRACE_TRACEME \n " ) ;
exit ( 1 ) ;
}
raise ( SIGCONT ) ;
bp_1 ( ) ;
exit ( 0 ) ;
}
return child ;
}
/*
* This tests creates HW breakpoint , tries to
* change it and checks it was properly changed .
*/
static int bp_modify1 ( void )
{
pid_t child ;
int status ;
unsigned long rip = 0 , dr7 = 1 ;
child = spawn_child ( ) ;
waitpid ( child , & status , 0 ) ;
if ( WIFEXITED ( status ) ) {
pr_debug ( " tracee exited prematurely 1 \n " ) ;
return TEST_FAIL ;
}
/*
* The parent does following steps :
* - creates a new breakpoint ( id 0 ) for bp_2 function
* - changes that breakponit to bp_1 function
* - waits for the breakpoint to hit and checks
* it has proper rip of bp_1 function
* - detaches the child
*/
if ( ptrace ( PTRACE_POKEUSER , child ,
offsetof ( struct user , u_debugreg [ 0 ] ) , bp_2 ) ) {
pr_debug ( " failed to set breakpoint, 1st time: %s \n " ,
strerror ( errno ) ) ;
goto out ;
}
if ( ptrace ( PTRACE_POKEUSER , child ,
offsetof ( struct user , u_debugreg [ 0 ] ) , bp_1 ) ) {
pr_debug ( " failed to set breakpoint, 2nd time: %s \n " ,
strerror ( errno ) ) ;
goto out ;
}
if ( ptrace ( PTRACE_POKEUSER , child ,
offsetof ( struct user , u_debugreg [ 7 ] ) , dr7 ) ) {
pr_debug ( " failed to set dr7: %s \n " , strerror ( errno ) ) ;
goto out ;
}
if ( ptrace ( PTRACE_CONT , child , NULL , NULL ) ) {
pr_debug ( " failed to PTRACE_CONT: %s \n " , strerror ( errno ) ) ;
goto out ;
}
waitpid ( child , & status , 0 ) ;
if ( WIFEXITED ( status ) ) {
pr_debug ( " tracee exited prematurely 2 \n " ) ;
return TEST_FAIL ;
}
rip = ptrace ( PTRACE_PEEKUSER , child ,
offsetof ( struct user_regs_struct , rip ) , NULL ) ;
if ( rip = = ( unsigned long ) - 1 ) {
pr_debug ( " failed to PTRACE_PEEKUSER: %s \n " ,
strerror ( errno ) ) ;
goto out ;
}
pr_debug ( " rip %lx, bp_1 %p \n " , rip , bp_1 ) ;
out :
if ( ptrace ( PTRACE_DETACH , child , NULL , NULL ) ) {
pr_debug ( " failed to PTRACE_DETACH: %s " , strerror ( errno ) ) ;
return TEST_FAIL ;
}
return rip = = ( unsigned long ) bp_1 ? TEST_OK : TEST_FAIL ;
}
/*
* This tests creates HW breakpoint , tries to
* change it to bogus value and checks the original
* breakpoint is hit .
*/
static int bp_modify2 ( void )
{
pid_t child ;
int status ;
unsigned long rip = 0 , dr7 = 1 ;
child = spawn_child ( ) ;
waitpid ( child , & status , 0 ) ;
if ( WIFEXITED ( status ) ) {
pr_debug ( " tracee exited prematurely 1 \n " ) ;
return TEST_FAIL ;
}
/*
* The parent does following steps :
* - creates a new breakpoint ( id 0 ) for bp_1 function
* - tries to change that breakpoint to ( - 1 ) address
* - waits for the breakpoint to hit and checks
* it has proper rip of bp_1 function
* - detaches the child
*/
if ( ptrace ( PTRACE_POKEUSER , child ,
offsetof ( struct user , u_debugreg [ 0 ] ) , bp_1 ) ) {
pr_debug ( " failed to set breakpoint: %s \n " ,
strerror ( errno ) ) ;
goto out ;
}
if ( ptrace ( PTRACE_POKEUSER , child ,
offsetof ( struct user , u_debugreg [ 7 ] ) , dr7 ) ) {
pr_debug ( " failed to set dr7: %s \n " , strerror ( errno ) ) ;
goto out ;
}
if ( ! ptrace ( PTRACE_POKEUSER , child ,
offsetof ( struct user , u_debugreg [ 0 ] ) , ( unsigned long ) ( - 1 ) ) ) {
pr_debug ( " failed, breakpoint set to bogus address \n " ) ;
goto out ;
}
if ( ptrace ( PTRACE_CONT , child , NULL , NULL ) ) {
pr_debug ( " failed to PTRACE_CONT: %s \n " , strerror ( errno ) ) ;
goto out ;
}
waitpid ( child , & status , 0 ) ;
if ( WIFEXITED ( status ) ) {
pr_debug ( " tracee exited prematurely 2 \n " ) ;
return TEST_FAIL ;
}
rip = ptrace ( PTRACE_PEEKUSER , child ,
offsetof ( struct user_regs_struct , rip ) , NULL ) ;
if ( rip = = ( unsigned long ) - 1 ) {
pr_debug ( " failed to PTRACE_PEEKUSER: %s \n " ,
strerror ( errno ) ) ;
goto out ;
}
pr_debug ( " rip %lx, bp_1 %p \n " , rip , bp_1 ) ;
out :
if ( ptrace ( PTRACE_DETACH , child , NULL , NULL ) ) {
pr_debug ( " failed to PTRACE_DETACH: %s " , strerror ( errno ) ) ;
return TEST_FAIL ;
}
return rip = = ( unsigned long ) bp_1 ? TEST_OK : TEST_FAIL ;
}
int test__bp_modify ( struct test * test __maybe_unused ,
int subtest __maybe_unused )
{
TEST_ASSERT_VAL ( " modify test 1 failed \n " , ! bp_modify1 ( ) ) ;
TEST_ASSERT_VAL ( " modify test 2 failed \n " , ! bp_modify2 ( ) ) ;
return 0 ;
}