2020-01-18 11:38:21 +03:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
*
2021-04-22 11:24:48 +03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 11:38:21 +03:00
*/
2021-05-15 13:34:40 +03:00
# include <AK/Assertions.h>
2021-02-12 00:43:18 +03:00
# include <AK/Debug.h>
2021-02-12 00:01:49 +03:00
# include <AK/Iterator.h>
# include <AK/Vector.h>
2021-02-11 23:08:46 +03:00
# include <LibCore/ArgsParser.h>
2021-09-12 14:29:28 +03:00
# include <errno_codes.h>
2019-09-12 17:26:54 +03:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
2021-05-14 18:38:33 +03:00
# include <sys/mman.h>
2021-02-05 14:16:30 +03:00
# include <syscall.h>
2019-09-12 17:26:54 +03:00
# define SC_NARG 4
2020-03-08 12:36:51 +03:00
FlatPtr arg [ SC_NARG ] ;
2021-02-12 00:01:49 +03:00
char outbuf [ BUFSIZ ] ;
2019-09-12 17:26:54 +03:00
2021-02-12 00:01:49 +03:00
using Arguments = Vector < const char * > ;
using ArgIter = Arguments : : Iterator ;
static FlatPtr parse_from ( ArgIter & ) ;
2019-09-12 17:26:54 +03:00
2021-04-23 12:11:09 +03:00
template < >
struct AK : : Formatter < Syscall : : Function > : Formatter < StringView > {
2021-11-16 03:15:21 +03:00
ErrorOr < void > format ( FormatBuilder & builder , Syscall : : Function function )
2021-04-23 12:11:09 +03:00
{
return Formatter < StringView > : : format ( builder , to_string ( function ) ) ;
}
} ;
2019-09-12 17:26:54 +03:00
int main ( int argc , char * * argv )
{
2021-02-11 23:08:46 +03:00
bool output_buffer = false ;
bool list_syscalls = false ;
Vector < const char * > arguments ;
Core : : ArgsParser args_parser ;
2021-02-12 00:43:18 +03:00
args_parser . set_general_help (
" Enables you to do a direct syscall, even those that use a 'SC_*_params' buffer. \n "
" Arguments can be literal strings, numbers, the output buffer, or parameter buffers: \n "
" - Arguments that begin with a comma are stripped of the comma and treated as string arguments, for example ',0x0' or ',['. \n "
" - 'buf' is replaced by a pointer to the output buffer. \n "
" - Numbers can be written like 1234 or 0xDEADC0DE. \n "
" - Parameter buffer (e.g. SC_realpath_params) can be passed by wrapping them in '[' and ']'. Note that '[' and ']' must be separate arguments to syscall(1). Buffers can be used recursively. \n "
" - The first argument may also be any syscall function name. Run 'syscall -l' to see the list. \n "
" - Arguments that cannot be interpreted are treated as string arguments, for example 'Hello, friends!'. \n "
" \n "
" Full example: syscall -o realpath [ /usr/share/man/man2/getgid.md 1024 buf 1024 ] " ) ;
2021-04-23 12:11:09 +03:00
args_parser . add_option ( list_syscalls , " List all existing syscalls, and exit " , " list-syscalls " , ' l ' ) ;
2021-02-11 23:08:46 +03:00
args_parser . add_option ( output_buffer , " Output the contents of the buffer (beware of stray zero bytes!) " , " output-buffer " , ' o ' ) ;
2021-04-23 12:11:09 +03:00
args_parser . add_positional_argument ( arguments , " Syscall arguments; see general help. " , " syscall-arguments " , Core : : ArgsParser : : Required : : No ) ;
2021-02-11 23:08:46 +03:00
args_parser . parse ( argc , argv ) ;
2021-04-23 12:11:09 +03:00
if ( list_syscalls ) {
outln ( " syscall list: " ) ;
for ( int sc = 0 ; sc < Syscall : : Function : : __Count ; + + sc ) {
outln ( " \033 [33;1m{} \033 [0m - {} " , sc , static_cast < Syscall : : Function > ( sc ) ) ;
}
exit ( 0 ) ;
}
if ( arguments . is_empty ( ) ) {
args_parser . print_usage ( stderr , argv [ 0 ] ) ;
exit ( 1 ) ;
}
2021-02-12 00:01:49 +03:00
ArgIter iter = arguments . begin ( ) ;
for ( size_t i = 0 ; i < SC_NARG & & ! iter . is_end ( ) ; i + + ) {
arg [ i ] = parse_from ( iter ) ;
}
if ( ! iter . is_end ( ) ) {
2021-02-12 00:43:18 +03:00
warnln ( " Too many arguments (did you want to use '[ parameter buffers ]'?) " ) ;
2021-02-12 00:01:49 +03:00
return - 1 ;
2019-09-12 17:26:54 +03:00
}
2021-02-12 00:01:49 +03:00
if ( arg [ 0 ] > Syscall : : Function : : __Count ) {
for ( int sc = 0 ; sc < Syscall : : Function : : __Count ; + + sc ) {
2021-08-05 21:41:44 +03:00
if ( Syscall : : to_string ( ( Syscall : : Function ) sc ) = = ( char const * ) arg [ 0 ] ) {
2021-02-12 00:01:49 +03:00
arg [ 0 ] = sc ;
break ;
2019-09-12 17:26:54 +03:00
}
2021-02-12 00:01:49 +03:00
}
if ( arg [ 0 ] > Syscall : : Function : : __Count ) {
2021-02-12 00:43:18 +03:00
warnln ( " Invalid syscall entry {} " , ( char * ) arg [ 0 ] ) ;
2021-02-12 00:01:49 +03:00
return - 1 ;
2019-09-12 17:26:54 +03:00
}
}
2021-02-12 00:43:18 +03:00
dbgln_if ( SYSCALL_1_DEBUG , " Calling {} {:p} {:p} {:p} \n " , arg [ 0 ] , arg [ 1 ] , arg [ 2 ] , arg [ 3 ] ) ;
2021-02-12 00:01:49 +03:00
int rc = syscall ( arg [ 0 ] , arg [ 1 ] , arg [ 2 ] , arg [ 3 ] ) ;
if ( output_buffer )
fwrite ( outbuf , 1 , sizeof ( outbuf ) , stdout ) ;
2021-11-07 01:25:25 +03:00
if ( - rc > = 0 & & - rc < EMAXERRNO ) {
warnln ( " Syscall return: {} ({}) " , rc , strerror ( - rc ) ) ;
} else {
warnln ( " Syscall return: {} (?) " , rc ) ;
}
2021-02-12 00:01:49 +03:00
return 0 ;
}
static FlatPtr as_buf ( Vector < FlatPtr > params_vec )
{
size_t params_size = sizeof ( FlatPtr ) * params_vec . size ( ) ;
size_t buf_size = round_up_to_power_of_two ( params_size + 1 , PAGE_SIZE ) ;
void * buf = mmap ( nullptr , buf_size , PROT_READ | PROT_WRITE , MAP_ANON | MAP_PRIVATE , 0 , 0 ) ;
if ( buf = = MAP_FAILED ) {
fprintf ( stderr , " Warning: Could not allocate buffer of size %zu (low memory?) \n " , buf_size ) ;
exit ( 1 ) ;
}
// It's probably good to ensure zero-initialization.
memset ( buf , 0 , buf_size ) ;
memcpy ( buf , params_vec . data ( ) , params_size ) ;
2021-02-12 00:43:18 +03:00
if constexpr ( SYSCALL_1_DEBUG ) {
StringBuilder builder ;
builder . append ( " Prepared [ " ) ;
for ( size_t i = 0 ; i < params_vec . size ( ) ; + + i ) {
builder . appendff ( " {:p} " , params_vec [ i ] ) ;
}
builder . appendff ( " ] at {:p} " , ( FlatPtr ) buf ) ;
2021-02-10 22:42:41 +03:00
dbgln ( " {} " , builder . to_string ( ) ) ;
2021-02-12 00:43:18 +03:00
}
2021-02-12 00:01:49 +03:00
// Leak the buffer here. We need to keep it until the special syscall happens,
// and we terminate immediately afterwards anyway.
return ( FlatPtr ) buf ;
2019-09-12 17:26:54 +03:00
}
2021-02-12 00:01:49 +03:00
static FlatPtr parse_parameter_buffer ( ArgIter & iter )
2019-09-12 17:26:54 +03:00
{
2021-02-12 00:01:49 +03:00
Vector < FlatPtr > params_vec ;
while ( ! iter . is_end ( ) ) {
if ( strcmp ( * iter , " ] " ) = = 0 ) {
+ + iter ;
return as_buf ( params_vec ) ;
}
2019-09-12 17:26:54 +03:00
2021-02-12 00:01:49 +03:00
params_vec . append ( parse_from ( iter ) ) ;
2019-09-12 17:26:54 +03:00
}
2021-02-13 12:55:51 +03:00
fprintf ( stderr , " Error: Unmatched '['?! \n " ) ;
2021-02-12 00:01:49 +03:00
exit ( 1 ) ;
2021-02-23 22:42:32 +03:00
VERIFY_NOT_REACHED ( ) ;
2021-02-12 00:01:49 +03:00
}
static FlatPtr parse_from ( ArgIter & iter )
{
const char * this_arg = * iter ;
+ + iter ;
// Is it a forced literal?
2021-02-12 00:43:18 +03:00
if ( this_arg [ 0 ] = = ' , ' ) {
this_arg + = 1 ;
dbgln_if ( SYSCALL_1_DEBUG , " Using (forced) string >>{}<< at {:p} " , this_arg , ( FlatPtr ) this_arg ) ;
return ( FlatPtr ) this_arg ;
}
2021-02-12 00:01:49 +03:00
// Is it the output buffer?
if ( strcmp ( this_arg , " buf " ) = = 0 )
return ( FlatPtr ) outbuf ;
// Is it a parameter buffer?
if ( strcmp ( this_arg , " [ " ) = = 0 )
return parse_parameter_buffer ( iter ) ;
// Is it a number?
char * endptr = nullptr ;
FlatPtr l = strtoul ( this_arg , & endptr , 0 ) ;
if ( * endptr = = 0 ) {
2019-09-12 17:26:54 +03:00
return l ;
}
2021-02-12 00:01:49 +03:00
// Then it must be a string:
if ( strcmp ( this_arg , " ] " ) = = 0 )
fprintf ( stderr , " Warning: Treating unmatched ']' as literal string \n " ) ;
2021-02-12 00:43:18 +03:00
dbgln_if ( SYSCALL_1_DEBUG , " Using (detected) string >>{}<< at {:p} " , this_arg , ( FlatPtr ) this_arg ) ;
2021-02-12 00:01:49 +03:00
return ( FlatPtr ) this_arg ;
2019-09-12 17:26:54 +03:00
}