2019-08-25 12:49:18 +03:00
// SPDX-License-Identifier: GPL-2.0
2005-09-04 02:57:47 +04:00
/*
2007-10-16 12:27:00 +04:00
* Copyright ( C ) 2000 - 2007 Jeff Dike ( jdike @ { addtoit , linux . intel } . com )
2005-04-17 02:20:36 +04:00
*/
# include <stdio.h>
2005-09-17 06:27:50 +04:00
# include <stdlib.h>
2007-10-16 12:27:00 +04:00
# include <stdarg.h>
2005-04-17 02:20:36 +04:00
# include <unistd.h>
# include <errno.h>
2007-10-16 12:27:00 +04:00
# include <fcntl.h>
# include <sched.h>
# include <signal.h>
# include <string.h>
2005-04-17 02:20:36 +04:00
# include <sys/mman.h>
2007-10-16 12:27:00 +04:00
# include <sys/stat.h>
# include <sys/wait.h>
2012-12-30 02:37:30 +04:00
# include <sys/time.h>
# include <sys/resource.h>
2005-04-17 02:20:36 +04:00
# include <asm/unistd.h>
2012-10-08 06:27:32 +04:00
# include <init.h>
# include <os.h>
# include <mem_user.h>
# include <ptrace_user.h>
# include <registers.h>
# include <skas.h>
2005-04-17 02:20:36 +04:00
2008-04-28 13:13:53 +04:00
static void ptrace_child ( void )
2005-04-17 02:20:36 +04:00
{
int ret ;
2007-10-16 12:27:11 +04:00
/* Calling os_getpid because some libcs cached getpid incorrectly */
2005-04-17 02:20:36 +04:00
int pid = os_getpid ( ) , ppid = getppid ( ) ;
int sc_result ;
2008-04-28 13:13:53 +04:00
if ( change_sig ( SIGWINCH , 0 ) < 0 | |
ptrace ( PTRACE_TRACEME , 0 , 0 , 0 ) < 0 ) {
2005-04-17 02:20:36 +04:00
perror ( " ptrace " ) ;
2007-10-16 12:27:11 +04:00
kill ( pid , SIGKILL ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:44:20 +03:00
kill ( pid , SIGSTOP ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:00 +04:00
/*
* This syscall will be intercepted by the parent . Don ' t call more than
* once , please .
*/
2005-04-17 02:20:36 +04:00
sc_result = os_getpid ( ) ;
if ( sc_result = = pid )
2007-10-16 12:27:00 +04:00
/* Nothing modified by the parent, we are running normally. */
ret = 1 ;
2005-04-17 02:20:36 +04:00
else if ( sc_result = = ppid )
2007-10-16 12:27:00 +04:00
/*
* Expected in check_ptrace and check_sysemu when they succeed
* in modifying the stack frame
*/
ret = 0 ;
2005-04-17 02:20:36 +04:00
else
2007-10-16 12:27:00 +04:00
/* Serious trouble! This could be caused by a bug in host 2.6
* SKAS3 / 2.6 patch before release - V6 , together with a bug in
* the UML code itself .
*/
ret = 2 ;
2008-02-05 09:31:04 +03:00
exit ( ret ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-05 09:30:35 +03:00
static void fatal_perror ( const char * str )
2007-02-10 12:44:28 +03:00
{
perror ( str ) ;
exit ( 1 ) ;
}
static void fatal ( char * fmt , . . . )
{
va_list list ;
va_start ( list , fmt ) ;
2008-04-28 13:13:53 +04:00
vfprintf ( stderr , fmt , list ) ;
2007-02-10 12:44:28 +03:00
va_end ( list ) ;
exit ( 1 ) ;
}
static void non_fatal ( char * fmt , . . . )
{
va_list list ;
va_start ( list , fmt ) ;
2008-04-28 13:13:53 +04:00
vfprintf ( stderr , fmt , list ) ;
2007-02-10 12:44:28 +03:00
va_end ( list ) ;
}
2007-10-16 12:27:09 +04:00
static int start_ptraced_child ( void )
2005-04-17 02:20:36 +04:00
{
int pid , n , status ;
2005-09-04 02:57:47 +04:00
2015-12-18 23:28:53 +03:00
fflush ( stdout ) ;
2007-10-16 12:27:09 +04:00
pid = fork ( ) ;
if ( pid = = 0 )
ptrace_child ( ) ;
else if ( pid < 0 )
fatal_perror ( " start_ptraced_child : fork failed " ) ;
2007-10-16 12:27:00 +04:00
2005-04-17 02:20:36 +04:00
CATCH_EINTR ( n = waitpid ( pid , & status , WUNTRACED ) ) ;
2007-10-16 12:27:00 +04:00
if ( n < 0 )
2007-10-16 12:27:09 +04:00
fatal_perror ( " check_ptrace : waitpid failed " ) ;
2007-10-16 12:27:00 +04:00
if ( ! WIFSTOPPED ( status ) | | ( WSTOPSIG ( status ) ! = SIGSTOP ) )
2007-02-10 12:44:28 +03:00
fatal ( " check_ptrace : expected SIGSTOP, got status = %d " ,
2005-04-17 02:20:36 +04:00
status ) ;
2007-02-10 12:44:20 +03:00
return pid ;
2005-04-17 02:20:36 +04:00
}
2005-09-04 02:57:47 +04:00
/* When testing for SYSEMU support, if it is one of the broken versions, we
* must just avoid using sysemu , not panic , but only if SYSEMU features are
* broken .
2005-04-17 02:20:36 +04:00
* So only for SYSEMU features we test mustpanic , while normal host features
2005-09-04 02:57:47 +04:00
* must work anyway !
*/
2007-10-16 12:27:09 +04:00
static int stop_ptraced_child ( int pid , int exitcode , int mustexit )
2005-04-17 02:20:36 +04:00
{
int status , n , ret = 0 ;
2008-06-13 02:21:41 +04:00
if ( ptrace ( PTRACE_CONT , pid , 0 , 0 ) < 0 ) {
perror ( " stop_ptraced_child : ptrace failed " ) ;
return - 1 ;
}
2005-04-17 02:20:36 +04:00
CATCH_EINTR ( n = waitpid ( pid , & status , 0 ) ) ;
2007-10-16 12:27:00 +04:00
if ( ! WIFEXITED ( status ) | | ( WEXITSTATUS ( status ) ! = exitcode ) ) {
2005-04-17 02:20:36 +04:00
int exit_with = WEXITSTATUS ( status ) ;
if ( exit_with = = 2 )
2007-02-10 12:44:28 +03:00
non_fatal ( " check_ptrace : child exited with status 2. "
2007-05-24 00:57:40 +04:00
" \n Disabling SYSEMU support. \n " ) ;
2007-02-10 12:44:28 +03:00
non_fatal ( " check_ptrace : child exited with exitcode %d, while "
" expecting %d; status 0x%x \n " , exit_with ,
exitcode , status ) ;
if ( mustexit )
exit ( 1 ) ;
2005-04-17 02:20:36 +04:00
ret = - 1 ;
}
return ret ;
}
2007-02-10 12:44:19 +03:00
/* Changed only during early boot */
2005-09-04 02:57:47 +04:00
static int force_sysemu_disabled = 0 ;
2005-04-17 02:20:36 +04:00
static int __init nosysemu_cmd_param ( char * str , int * add )
{
force_sysemu_disabled = 1 ;
return 0 ;
}
__uml_setup ( " nosysemu " , nosysemu_cmd_param ,
2005-09-04 02:57:47 +04:00
" nosysemu \n "
2017-08-08 23:19:41 +03:00
" Turns off syscall emulation patch for ptrace (SYSEMU). \n "
2005-09-04 02:57:47 +04:00
" SYSEMU is a performance-patch introduced by Laurent Vivier. It changes \n "
2017-08-08 23:19:41 +03:00
" behaviour of ptrace() and helps reduce host context switch rates. \n "
" To make it work, you need a kernel patch for your host, too. \n "
2005-09-04 02:57:47 +04:00
" See http://perso.wanadoo.fr/laurent.vivier/UML/ for further \n "
" information. \n \n " ) ;
2005-04-17 02:20:36 +04:00
static void __init check_sysemu ( void )
{
2007-05-24 00:57:40 +04:00
unsigned long regs [ MAX_REG_NR ] ;
2007-02-10 12:44:20 +03:00
int pid , n , status , count = 0 ;
2005-04-17 02:20:36 +04:00
2017-05-17 20:17:14 +03:00
os_info ( " Checking syscall emulation patch for ptrace... " ) ;
2005-04-17 02:20:36 +04:00
sysemu_supported = 0 ;
2007-10-16 12:27:09 +04:00
pid = start_ptraced_child ( ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:00 +04:00
if ( ptrace ( PTRACE_SYSEMU , pid , 0 , 0 ) < 0 )
2005-04-17 02:20:36 +04:00
goto fail ;
CATCH_EINTR ( n = waitpid ( pid , & status , WUNTRACED ) ) ;
if ( n < 0 )
2007-02-10 12:44:28 +03:00
fatal_perror ( " check_sysemu : wait failed " ) ;
2007-10-16 12:27:00 +04:00
if ( ! WIFSTOPPED ( status ) | | ( WSTOPSIG ( status ) ! = SIGTRAP ) )
2008-06-13 02:21:41 +04:00
fatal ( " check_sysemu : expected SIGTRAP, got status = %d \n " ,
2007-02-10 12:44:28 +03:00
status ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:00 +04:00
if ( ptrace ( PTRACE_GETREGS , pid , 0 , regs ) < 0 )
2007-05-24 00:57:40 +04:00
fatal_perror ( " check_sysemu : PTRACE_GETREGS failed " ) ;
2007-10-16 12:27:00 +04:00
if ( PT_SYSCALL_NR ( regs ) ! = __NR_getpid ) {
2007-05-24 00:57:40 +04:00
non_fatal ( " check_sysemu got system call number %d, "
" expected %d... " , PT_SYSCALL_NR ( regs ) , __NR_getpid ) ;
goto fail ;
}
2011-08-18 23:12:19 +04:00
n = ptrace ( PTRACE_POKEUSER , pid , PT_SYSCALL_RET_OFFSET , os_getpid ( ) ) ;
2007-10-16 12:27:00 +04:00
if ( n < 0 ) {
2007-05-24 00:57:40 +04:00
non_fatal ( " check_sysemu : failed to modify system call "
" return " ) ;
goto fail ;
}
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:09 +04:00
if ( stop_ptraced_child ( pid , 0 , 0 ) < 0 )
2005-04-17 02:20:36 +04:00
goto fail_stopped ;
sysemu_supported = 1 ;
2017-05-17 20:17:14 +03:00
os_info ( " OK \n " ) ;
2005-04-17 02:20:36 +04:00
set_using_sysemu ( ! force_sysemu_disabled ) ;
2017-05-17 20:17:14 +03:00
os_info ( " Checking advanced syscall emulation patch for ptrace... " ) ;
2007-10-16 12:27:09 +04:00
pid = start_ptraced_child ( ) ;
2005-09-04 02:57:51 +04:00
2007-10-16 12:27:00 +04:00
if ( ( ptrace ( PTRACE_OLDSETOPTIONS , pid , 0 ,
2007-02-10 12:44:28 +03:00
( void * ) PTRACE_O_TRACESYSGOOD ) < 0 ) )
2009-04-01 02:23:41 +04:00
fatal_perror ( " check_sysemu: PTRACE_OLDSETOPTIONS failed " ) ;
2005-09-04 02:57:51 +04:00
2007-10-16 12:27:00 +04:00
while ( 1 ) {
2005-04-17 02:20:36 +04:00
count + + ;
2007-10-16 12:27:00 +04:00
if ( ptrace ( PTRACE_SYSEMU_SINGLESTEP , pid , 0 , 0 ) < 0 )
2005-04-17 02:20:36 +04:00
goto fail ;
CATCH_EINTR ( n = waitpid ( pid , & status , WUNTRACED ) ) ;
2007-10-16 12:27:00 +04:00
if ( n < 0 )
2009-04-01 02:23:41 +04:00
fatal_perror ( " check_sysemu: wait failed " ) ;
2007-02-10 12:44:28 +03:00
2007-10-16 12:27:00 +04:00
if ( WIFSTOPPED ( status ) & &
( WSTOPSIG ( status ) = = ( SIGTRAP | 0x80 ) ) ) {
2008-06-13 02:21:41 +04:00
if ( ! count ) {
2009-04-01 02:23:41 +04:00
non_fatal ( " check_sysemu: SYSEMU_SINGLESTEP "
2008-06-13 02:21:41 +04:00
" doesn't singlestep " ) ;
goto fail ;
}
2011-08-18 23:12:19 +04:00
n = ptrace ( PTRACE_POKEUSER , pid , PT_SYSCALL_RET_OFFSET ,
2005-04-17 02:20:36 +04:00
os_getpid ( ) ) ;
2007-10-16 12:27:00 +04:00
if ( n < 0 )
2007-02-10 12:44:28 +03:00
fatal_perror ( " check_sysemu : failed to modify "
" system call return " ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2007-10-16 12:27:00 +04:00
else if ( WIFSTOPPED ( status ) & & ( WSTOPSIG ( status ) = = SIGTRAP ) )
2005-09-04 02:57:51 +04:00
count + + ;
2008-06-13 02:21:41 +04:00
else {
2009-04-01 02:23:41 +04:00
non_fatal ( " check_sysemu: expected SIGTRAP or "
2008-06-13 02:21:41 +04:00
" (SIGTRAP | 0x80), got status = %d \n " ,
status ) ;
goto fail ;
}
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:27:09 +04:00
if ( stop_ptraced_child ( pid , 0 , 0 ) < 0 )
2005-04-17 02:20:36 +04:00
goto fail_stopped ;
sysemu_supported = 2 ;
2017-05-17 20:17:14 +03:00
os_info ( " OK \n " ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:00 +04:00
if ( ! force_sysemu_disabled )
2005-04-17 02:20:36 +04:00
set_using_sysemu ( sysemu_supported ) ;
return ;
fail :
2007-10-16 12:27:09 +04:00
stop_ptraced_child ( pid , 1 , 0 ) ;
2005-04-17 02:20:36 +04:00
fail_stopped :
2007-02-10 12:44:28 +03:00
non_fatal ( " missing \n " ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-04 02:57:47 +04:00
static void __init check_ptrace ( void )
2005-04-17 02:20:36 +04:00
{
int pid , syscall , n , status ;
2017-05-17 20:17:14 +03:00
os_info ( " Checking that ptrace can change system call numbers... " ) ;
2007-10-16 12:27:09 +04:00
pid = start_ptraced_child ( ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:00 +04:00
if ( ( ptrace ( PTRACE_OLDSETOPTIONS , pid , 0 ,
2007-02-10 12:44:28 +03:00
( void * ) PTRACE_O_TRACESYSGOOD ) < 0 ) )
fatal_perror ( " check_ptrace: PTRACE_OLDSETOPTIONS failed " ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:00 +04:00
while ( 1 ) {
if ( ptrace ( PTRACE_SYSCALL , pid , 0 , 0 ) < 0 )
2007-02-10 12:44:28 +03:00
fatal_perror ( " check_ptrace : ptrace failed " ) ;
2005-04-17 02:20:36 +04:00
CATCH_EINTR ( n = waitpid ( pid , & status , WUNTRACED ) ) ;
2007-10-16 12:27:00 +04:00
if ( n < 0 )
2007-02-10 12:44:28 +03:00
fatal_perror ( " check_ptrace : wait failed " ) ;
2007-10-16 12:27:00 +04:00
if ( ! WIFSTOPPED ( status ) | |
2007-02-10 12:44:28 +03:00
( WSTOPSIG ( status ) ! = ( SIGTRAP | 0x80 ) ) )
fatal ( " check_ptrace : expected (SIGTRAP|0x80), "
" got status = %d " , status ) ;
2005-09-04 02:57:47 +04:00
2011-08-18 23:12:19 +04:00
syscall = ptrace ( PTRACE_PEEKUSER , pid , PT_SYSCALL_NR_OFFSET ,
2005-04-17 02:20:36 +04:00
0 ) ;
2007-10-16 12:27:00 +04:00
if ( syscall = = __NR_getpid ) {
2011-08-18 23:12:19 +04:00
n = ptrace ( PTRACE_POKEUSER , pid , PT_SYSCALL_NR_OFFSET ,
2005-04-17 02:20:36 +04:00
__NR_getppid ) ;
2007-10-16 12:27:00 +04:00
if ( n < 0 )
2007-02-10 12:44:28 +03:00
fatal_perror ( " check_ptrace : failed to modify "
" system call " ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
2007-10-16 12:27:09 +04:00
stop_ptraced_child ( pid , 0 , 1 ) ;
2017-05-17 20:17:14 +03:00
os_info ( " OK \n " ) ;
2005-04-17 02:20:36 +04:00
check_sysemu ( ) ;
}
2006-04-19 09:21:43 +04:00
extern void check_tmpexec ( void ) ;
2005-09-17 06:27:50 +04:00
2007-05-07 01:51:11 +04:00
static void __init check_coredump_limit ( void )
2007-05-07 01:51:00 +04:00
{
struct rlimit lim ;
int err = getrlimit ( RLIMIT_CORE , & lim ) ;
2007-10-16 12:27:00 +04:00
if ( err ) {
2007-05-07 01:51:00 +04:00
perror ( " Getting core dump limit " ) ;
return ;
}
2017-05-17 20:17:14 +03:00
os_info ( " Core dump limits : \n \t soft - " ) ;
2007-10-16 12:27:00 +04:00
if ( lim . rlim_cur = = RLIM_INFINITY )
2017-05-17 20:17:14 +03:00
os_info ( " NONE \n " ) ;
else
os_info ( " %llu \n " , ( unsigned long long ) lim . rlim_cur ) ;
2007-05-07 01:51:00 +04:00
2017-05-17 20:17:14 +03:00
os_info ( " \t hard - " ) ;
2007-10-16 12:27:00 +04:00
if ( lim . rlim_max = = RLIM_INFINITY )
2017-05-17 20:17:14 +03:00
os_info ( " NONE \n " ) ;
else
os_info ( " %llu \n " , ( unsigned long long ) lim . rlim_max ) ;
2007-05-07 01:51:00 +04:00
}
2021-03-12 18:16:07 +03:00
void __init get_host_cpu_features (
void ( * flags_helper_func ) ( char * line ) ,
void ( * cache_helper_func ) ( char * line ) )
{
FILE * cpuinfo ;
char * line = NULL ;
size_t len = 0 ;
int done_parsing = 0 ;
cpuinfo = fopen ( " /proc/cpuinfo " , " r " ) ;
if ( cpuinfo = = NULL ) {
os_info ( " Failed to get host CPU features \n " ) ;
} else {
while ( ( getline ( & line , & len , cpuinfo ) ) ! = - 1 ) {
if ( strstr ( line , " flags " ) ) {
flags_helper_func ( line ) ;
done_parsing + + ;
}
if ( strstr ( line , " cache_alignment " ) ) {
cache_helper_func ( line ) ;
done_parsing + + ;
}
free ( line ) ;
line = NULL ;
if ( done_parsing > 1 )
break ;
}
fclose ( cpuinfo ) ;
}
}
2007-05-07 01:51:11 +04:00
void __init os_early_checks ( void )
2005-04-17 02:20:36 +04:00
{
2008-02-05 09:31:22 +03:00
int pid ;
2007-05-07 01:51:00 +04:00
/* Print out the core dump limits early */
check_coredump_limit ( ) ;
2005-09-04 02:57:47 +04:00
check_ptrace ( ) ;
2005-09-17 06:27:50 +04:00
/* Need to check this early because mmapping happens before the
* kernel is running .
*/
check_tmpexec ( ) ;
2008-02-05 09:31:22 +03:00
pid = start_ptraced_child ( ) ;
2021-09-13 09:12:52 +03:00
if ( init_pid_registers ( pid ) )
2008-02-05 09:31:22 +03:00
fatal ( " Failed to initialize default registers " ) ;
stop_ptraced_child ( pid , 1 , 1 ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-17 06:27:50 +04:00
int __init parse_iomem ( char * str , int * add )
{
struct iomem_region * new ;
2007-02-10 12:44:20 +03:00
struct stat64 buf ;
2005-09-17 06:27:50 +04:00
char * file , * driver ;
2007-02-10 12:44:20 +03:00
int fd , size ;
2005-09-17 06:27:50 +04:00
driver = str ;
file = strchr ( str , ' , ' ) ;
2007-10-16 12:27:00 +04:00
if ( file = = NULL ) {
2017-05-17 20:19:31 +03:00
os_warn ( " parse_iomem : failed to parse iomem \n " ) ;
2005-09-17 06:27:50 +04:00
goto out ;
}
* file = ' \0 ' ;
file + + ;
2007-02-10 12:44:20 +03:00
fd = open ( file , O_RDWR , 0 ) ;
2007-10-16 12:27:00 +04:00
if ( fd < 0 ) {
2007-10-16 12:27:11 +04:00
perror ( " parse_iomem - Couldn't open io file " ) ;
2005-09-17 06:27:50 +04:00
goto out ;
}
2007-10-16 12:27:00 +04:00
if ( fstat64 ( fd , & buf ) < 0 ) {
2007-02-10 12:44:20 +03:00
perror ( " parse_iomem - cannot stat_fd file " ) ;
2005-09-17 06:27:50 +04:00
goto out_close ;
}
new = malloc ( sizeof ( * new ) ) ;
2007-10-16 12:27:00 +04:00
if ( new = = NULL ) {
2005-09-17 06:27:50 +04:00
perror ( " Couldn't allocate iomem_region struct " ) ;
goto out_close ;
}
2007-02-10 12:44:20 +03:00
size = ( buf . st_size + UM_KERN_PAGE_SIZE ) & ~ ( UM_KERN_PAGE_SIZE - 1 ) ;
2005-09-17 06:27:50 +04:00
* new = ( ( struct iomem_region ) { . next = iomem_regions ,
. driver = driver ,
. fd = fd ,
. size = size ,
. phys = 0 ,
. virt = 0 } ) ;
iomem_regions = new ;
iomem_size + = new - > size + UM_KERN_PAGE_SIZE ;
2007-02-10 12:44:20 +03:00
return 0 ;
2005-09-17 06:27:50 +04:00
out_close :
2007-02-10 12:44:20 +03:00
close ( fd ) ;
2005-09-17 06:27:50 +04:00
out :
2007-02-10 12:44:20 +03:00
return 1 ;
2005-09-17 06:27:50 +04:00
}