diff options
Diffstat (limited to 'tests/tst-dup.c')
| -rw-r--r-- | tests/tst-dup.c | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/tests/tst-dup.c b/tests/tst-dup.c new file mode 100644 index 0000000..3941378 --- /dev/null +++ b/tests/tst-dup.c @@ -0,0 +1,187 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> + +#include <libmcpu.h> +#include <sys/asm/errno.h> +#include <sys/asm/fcntl.h> +#include <sys/asm/syscall.h> + +#include "mcpu-asm.h" + + +#define CHECK(cond,msg) \ + do { \ + if( !(cond) ) \ + { \ + fprintf( stderr, "FAIL: %s\n", (msg) ); \ + return( 1 ); \ + } \ + } while( 0 ) + + +static void +put32( __mpu_uint32_t address, __mpu_uint32_t value ) +{ + __cpu_data_put_u32( __cpu_reg_ptr( 32, address ), value ); +} + + +static void +put64( __mpu_uint32_t address, __mpu_uint64_t value ) +{ + __cpu_data_put_u64( __cpu_reg_ptr( 64, address ), value ); +} + + +static __mpu_uint64_t +reg64( __mpu_uint32_t address ) +{ + return( __cpu_data_get_u64( __cpu_reg_ptr( 64, address ) ) ); +} + + +static int +run_syscall( __mpu_uint32_t nargs, __mpu_uint32_t sysno, + __mpu_uint64_t arg0, __mpu_uint64_t arg1, + __mpu_uint64_t arg2, __mpu_uint64_t *ret ) +{ + int rc; + + put32( CPU_SYSCALL_NARGS_ADDR, nargs ); + put32( CPU_SYSCALL_SYSNO_ADDR, sysno ); + put64( CPU_SYSCALL_ARG0_ADDR, arg0 ); + put64( CPU_SYSCALL_ARG1_ADDR, arg1 ); + put64( CPU_SYSCALL_ARG2_ADDR, arg2 ); + + __cpu_pc = 0; + rc = __cpu_run(); + if( rc != CPU_EXEC_SUCCESS ) return( rc ); + + *ret = reg64( CPU_SYSCALL_RET_ADDR ); + return( 0 ); + +} /* End of run_syscall() */ + + +static __mpu_int64_t +signed_result( __mpu_uint64_t value ) +{ + __mpu_int64_t ret; + + memcpy( &ret, &value, sizeof( ret ) ); + return( ret ); + +} /* End of signed_result() */ + + +int +main( void ) +{ + static const char source[] = + ".text\n" + "SYSCALL\n" + "HALT\n"; + cpu_asm_image image; + char err[256]; + __mpu_uint64_t ret; + + memset( &image, 0, sizeof( image ) ); + + CHECK( __NR_dup == 0x126, "public __NR_dup ABI number" ); + CHECK( __NR_dup2 == 0x127, "public __NR_dup2 ABI number" ); + + __cpu_init(); + + CHECK( cpu_assemble_text( source, &image, err, sizeof( err ) ) == 0, err ); + CHECK( __cpu_program_set( image.code, image.code_size ) == 0, + "program load" ); + + /* dup() returns the lowest free guest descriptor. */ + CHECK( run_syscall( 1, __NR_dup, 1, 0, 0, &ret ) == 0, "dup stdout" ); + CHECK( ret == 3, "dup returns lowest free guest fd 3" ); + + /* Descriptor flags are not copied by dup(). */ + CHECK( run_syscall( 3, __NR_fcntl, 1, F_SETFD, FD_CLOEXEC, &ret ) == 0, + "set FD_CLOEXEC on source" ); + CHECK( ret == 0, "F_SETFD source result" ); + CHECK( run_syscall( 3, __NR_fcntl, 3, F_GETFD, 0, &ret ) == 0, + "F_GETFD duplicated descriptor" ); + CHECK( ret == 0, "dup clears descriptor flags" ); + + /* dup2() installs exactly the requested guest descriptor. */ + CHECK( run_syscall( 2, __NR_dup2, 1, 10, 0, &ret ) == 0, + "dup2 stdout to guest fd 10" ); + CHECK( ret == 10, "dup2 returns requested guest fd" ); + + CHECK( run_syscall( 3, __NR_fcntl, 10, F_SETFD, FD_CLOEXEC, &ret ) == 0, + "set FD_CLOEXEC on dup2 target" ); + CHECK( ret == 0, "set target flags" ); + + /* Replacing an occupied target clears descriptor flags. */ + CHECK( run_syscall( 2, __NR_dup2, 3, 10, 0, &ret ) == 0, + "dup2 replaces occupied target" ); + CHECK( ret == 10, "dup2 replacement return" ); + CHECK( run_syscall( 3, __NR_fcntl, 10, F_GETFD, 0, &ret ) == 0, + "F_GETFD replaced target" ); + CHECK( ret == 0, "dup2 replacement clears descriptor flags" ); + + /* oldfd == newfd is a no-op and preserves descriptor flags. */ + CHECK( run_syscall( 3, __NR_fcntl, 1, F_GETFD, 0, &ret ) == 0, + "F_GETFD source before dup2 self" ); + CHECK( ret == FD_CLOEXEC, "source flag before dup2 self" ); + CHECK( run_syscall( 2, __NR_dup2, 1, 1, 0, &ret ) == 0, + "dup2 oldfd equals newfd" ); + CHECK( ret == 1, "dup2 self return" ); + CHECK( run_syscall( 3, __NR_fcntl, 1, F_GETFD, 0, &ret ) == 0, + "F_GETFD source after dup2 self" ); + CHECK( ret == FD_CLOEXEC, "dup2 self preserves descriptor flags" ); + + /* Closing a borrowed standard guest fd does not close HOST stdio, and dup() + may subsequently reuse that lowest free guest slot. */ + CHECK( run_syscall( 1, __NR_close, 0, 0, 0, &ret ) == 0, + "close borrowed guest stdin" ); + CHECK( ret == 0, "close borrowed guest stdin result" ); + CHECK( run_syscall( 1, __NR_dup, 1, 0, 0, &ret ) == 0, + "dup reuses closed guest fd 0" ); + CHECK( ret == 0, "dup returns lowest free guest fd 0" ); + + /* Invalid source and target descriptors use the stable MCPU EBADF value. */ + CHECK( run_syscall( 1, __NR_dup, 255, 0, 0, &ret ) == 0, + "dup invalid source" ); + CHECK( signed_result( ret ) == -(__mpu_int64_t)EBADF, + "dup invalid source returns -EBADF" ); + + CHECK( run_syscall( 2, __NR_dup2, 1, 256, 0, &ret ) == 0, + "dup2 invalid target" ); + CHECK( signed_result( ret ) == -(__mpu_int64_t)EBADF, + "dup2 invalid target returns -EBADF" ); + + CHECK( run_syscall( 3, __NR_fcntl, 10, F_SETFD, FD_CLOEXEC, &ret ) == 0, + "set target flag before invalid-source dup2" ); + CHECK( run_syscall( 2, __NR_dup2, 255, 10, 0, &ret ) == 0, + "dup2 invalid source with occupied target" ); + CHECK( signed_result( ret ) == -(__mpu_int64_t)EBADF, + "dup2 invalid source returns -EBADF" ); + CHECK( run_syscall( 3, __NR_fcntl, 10, F_GETFD, 0, &ret ) == 0, + "target survives invalid-source dup2" ); + CHECK( ret == FD_CLOEXEC, + "invalid-source dup2 leaves target unchanged" ); + + CHECK( run_syscall( 1, __NR_close, 0, 0, 0, &ret ) == 0, + "close reused guest fd 0" ); + CHECK( run_syscall( 1, __NR_close, 3, 0, 0, &ret ) == 0, + "close dup guest fd 3" ); + CHECK( run_syscall( 1, __NR_close, 10, 0, 0, &ret ) == 0, + "close dup2 guest fd 10" ); + + cpu_asm_image_free( &image ); + __cpu_free_context(); + + puts( "libmcpu dup/dup2 regression: PASS" ); + return( 0 ); + +} /* End of main() */ |
