diff options
Diffstat (limited to 'tests/tst-indirect-control.c')
| -rw-r--r-- | tests/tst-indirect-control.c | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/tests/tst-indirect-control.c b/tests/tst-indirect-control.c new file mode 100644 index 0000000..acb0a7b --- /dev/null +++ b/tests/tst-indirect-control.c @@ -0,0 +1,184 @@ +#include <stdio.h> +#include <string.h> +#include <libmcpu.h> +#include "../utils/mcpu-asm.h" + +#define CHECK(c,m) do{if(!(c)){fprintf(stderr,"FAIL: %s\n",m);return 1;}}while(0) + +static const char decode_src[]= +".text\n" +" JMPP r64@0x00000\n" +" CALLP r64@0x00008\n" +" HALT\n"; + +static const char run_src[]= +".data\n" +".align 8\n" +"table:\n" +" .cptr leaf\n" +" .cptr done\n" +".text\n" +" LEA r64@0x00000, table\n" +" LOADP r64@0x00008, r64@0x00000\n" +" CALLP r64@0x00008\n" +" PADDI r64@0x00000, r64@0x00000, 8\n" +" LOADP r64@0x00010, r64@0x00000\n" +" JMPP r64@0x00010\n" +" LDI r64@0x00020, 99\n" +" HALT\n" +"leaf:\n" +" LDI r64@0x00020, 42\n" +" RET\n" +"done:\n" +" HALT\n"; + +static void +put_u64_reg( unsigned addr, __mpu_uint64_t v ) +{ + __cpu_data_put_u64( __cpu_reg_ptr( 64, addr ), v ); +} + +int +main( void ) +{ + cpu_asm_image decim, im, bad; + cpu_instruction insn, execi; + cpu_register r64 = + { + 64, 0x00100 + } + , r32 = + { + 32, 0x00100 + }; + __mpu_uint64_t p0, p1, pvalid, pbad, ret, saved_sp, saved_pc; + __mpu_uint32_t psw; + mpu_int64_t forty_two; + unsigned char before[16], after[16]; + char err[256]; + int rc; + + __cpu_init(); + + CHECK( cpu_assemble_text( decode_src, &decim, err, sizeof( err ) ) == 0, err ); + CHECK( decim.code_size == 12, "JMPP/CALLP bytecode lengths" ); + CHECK( __cpu_program_set( decim.code, decim.code_size ) == 0, "decode program set" ); + __cpu_pc = 0; + CHECK( __cpu_decode( &insn ) == CPU_EXEC_SUCCESS, "decode JMPP" ); + CHECK( insn.opcode == CPU_OP_JMPP, "JMPP opcode" ); + CHECK( insn.length == CPU_OPCODE_SIZE + CPU_REG24_SIZE, "JMPP length 5" ); + CHECK( insn.operand[0].type == CPU_OPERAND_REGISTER, "JMPP register operand" ); + CHECK( insn.operand[0].value.reg.bits == 64, "JMPP r64 width" ); + __cpu_pc = 5; + CHECK( __cpu_decode( &insn ) == CPU_EXEC_SUCCESS, "decode CALLP" ); + CHECK( insn.opcode == CPU_OP_CALLP, "CALLP opcode" ); + CHECK( insn.length == CPU_OPCODE_SIZE + CPU_REG24_SIZE, "CALLP length 5" ); + cpu_asm_image_free( &decim ); + + __cpu_program_free(); + CHECK( cpu_assemble_text( run_src, &im, err, sizeof( err ) ) == 0, err ); + CHECK( im.data_size == CPU_DATA_NULL_GUARD_SIZE + 16, "two .cptr entries in DATA" ); + p0 = __cpu_data_get_u64( im.data + CPU_DATA_NULL_GUARD_SIZE ); + p1 = __cpu_data_get_u64( im.data + CPU_DATA_NULL_GUARD_SIZE + 8 ); + CHECK( __cpu_ptr_segment( p0 ) == CPU_PTR_SEG_PROGRAM, "first .cptr PROGRAM segment" ); + CHECK( __cpu_ptr_segment( p1 ) == CPU_PTR_SEG_PROGRAM, "second .cptr PROGRAM segment" ); + CHECK( __cpu_ptr_offset( p0 ) < im.code_size, "first .cptr target in PROGRAM" ); + CHECK( __cpu_ptr_offset( p1 ) < im.code_size, "second .cptr target in PROGRAM" ); + CHECK( p0 != p1, ".cptr labels resolve to distinct targets" ); + + CHECK( __cpu_program_set( im.code, im.code_size ) == 0, "indirect program set" ); + CHECK( __cpu_data_set( im.data, im.data_size ) == 0, "indirect data set" ); + CHECK( __cpu_stack_alloc( 512 ) == 0, "indirect stack alloc" ); + __cpu_pc = 0; + CHECK( __cpu_run() == CPU_EXEC_SUCCESS, "indirect CALLP/JMPP run" ); + iatoi( ( mpu_int * ) forty_two, "42", NB_I64 ); + CHECK( !memcmp( __cpu_reg_ptr( 64, 0x00020 ), forty_two, NB_I64 ), "CALLP returned and JMPP reached done" ); + CHECK( __cpu_sp == 512, "CALLP/RET restored SP" ); + + /* Direct executor tests: JMPP must only install a validated PROGRAM PTR64. */ + memset( &execi, 0, sizeof( execi ) ); + execi.opcode = CPU_OP_JMPP; + execi.operand[0].type = CPU_OPERAND_REGISTER; + execi.operand[0].value.reg = r64; + pvalid = p0; + put_u64_reg( 0x00100, pvalid ); + __cpu_pc = 7; + __cpu_sp = 512; + __cpu_set_psw( PSW_CF_MASK | PSW_TF_MASK ); + psw = __cpu_get_psw(); + CHECK( __cpu_exec( &execi ) == CPU_EXEC_BRANCH, "JMPP executor success" ); + CHECK( __cpu_pc == __cpu_ptr_offset( pvalid ), "JMPP installed target PC" ); + CHECK( __cpu_sp == 512, "JMPP does not change SP" ); + CHECK( __cpu_get_psw() == psw, "JMPP preserves PSW" ); + + CHECK( __cpu_ptr_make( &pbad, CPU_PTR_SEG_DATA, CPU_DATA_NULL_GUARD_SIZE ) == 0, "make DATA pointer" ); + put_u64_reg( 0x00100, pbad ); + __cpu_pc = 9; + saved_pc = __cpu_pc; + saved_sp = __cpu_sp; + CHECK( __cpu_exec( &execi ) == CPU_EXEC_POINTER_FAULT, "JMPP rejects non-PROGRAM PTR64" ); + CHECK( __cpu_pc == saved_pc && __cpu_sp == saved_sp, "JMPP pointer fault is atomic" ); + + CHECK( __cpu_ptr_make( &pbad, CPU_PTR_SEG_PROGRAM, im.code_size ) == 0, "make out-of-range PROGRAM pointer" ); + put_u64_reg( 0x00100, pbad ); + __cpu_pc = 11; + saved_pc = __cpu_pc; + CHECK( __cpu_exec( &execi ) == CPU_EXEC_POINTER_FAULT, "JMPP rejects PROGRAM end pointer" ); + CHECK( __cpu_pc == saved_pc, "JMPP range fault preserves PC" ); + + put_u64_reg( 0x00100, CPU_NULL_PTR ); + CHECK( __cpu_exec( &execi ) == CPU_EXEC_POINTER_FAULT, "JMPP rejects NULL PTR64" ); + + execi.operand[0].value.reg = r32; + CHECK( __cpu_exec( &execi ) == CPU_EXEC_WIDTH_FAULT, "JMPP requires physical r64" ); + + /* CALLP validates pointer, return PC and stack before architectural mutation. */ + memset( &execi, 0, sizeof( execi ) ); + execi.opcode = CPU_OP_CALLP; + execi.operand[0].type = CPU_OPERAND_REGISTER; + execi.operand[0].value.reg = r64; + put_u64_reg( 0x00100, pvalid ); + __cpu_pc = 10; + __cpu_sp = 512; + memset( __cpu_stack_ptr() + 496, 0xa5, 16 ); + __cpu_set_psw( PSW_CF_MASK | PSW_TF_MASK ); + psw = __cpu_get_psw(); + CHECK( __cpu_exec( &execi ) == CPU_EXEC_BRANCH, "CALLP executor success" ); + CHECK( __cpu_pc == __cpu_ptr_offset( pvalid ), "CALLP installed target PC" ); + CHECK( __cpu_sp == 504, "CALLP pushed 8-byte return PC" ); + ret = __cpu_data_get_u64( __cpu_stack_ptr() + 504 ); + CHECK( ret == 15, "CALLP return PC is next 5-byte instruction" ); + CHECK( __cpu_get_psw() == psw, "CALLP preserves PSW" ); + + CHECK( __cpu_ptr_make( &pbad, CPU_PTR_SEG_STACK, 0 ) == 0, "make STACK pointer" ); + put_u64_reg( 0x00100, pbad ); + __cpu_pc = 20; + __cpu_sp = 512; + saved_pc = __cpu_pc; + saved_sp = __cpu_sp; + memcpy( before, __cpu_stack_ptr() + 496, 16 ); + CHECK( __cpu_exec( &execi ) == CPU_EXEC_POINTER_FAULT, "CALLP rejects non-PROGRAM PTR64" ); + memcpy( after, __cpu_stack_ptr() + 496, 16 ); + CHECK( __cpu_pc == saved_pc && __cpu_sp == saved_sp && !memcmp( before, after, 16 ), "CALLP pointer fault is atomic" ); + + put_u64_reg( 0x00100, pvalid ); + __cpu_pc = 20; + __cpu_sp = 4; + saved_pc = __cpu_pc; + saved_sp = __cpu_sp; + CHECK( __cpu_exec( &execi ) == CPU_EXEC_STACK_FAULT, "CALLP checks return-address stack space" ); + CHECK( __cpu_pc == saved_pc && __cpu_sp == saved_sp, "CALLP stack fault is atomic" ); + + execi.operand[0].value.reg = r32; + __cpu_sp = 512; + CHECK( __cpu_exec( &execi ) == CPU_EXEC_WIDTH_FAULT, "CALLP requires physical r64" ); + + CHECK( cpu_assemble_text( ".text\n JMPP r32@0x00000\n", &bad, err, sizeof( err ) ) != 0, "assembler rejects JMPP r32" ); + CHECK( cpu_assemble_text( ".text\n CALLP c64@0x00000\n", &bad, err, sizeof( err ) ) != 0, "assembler rejects CALLP c64 spelling" ); + CHECK( cpu_assemble_text( ".data\n .cptr nowhere\n.text\n HALT\n", &bad, err, sizeof( err ) ) != 0, "assembler rejects undefined .cptr label" ); + + cpu_asm_image_free( &im ); + __cpu_free_context(); + puts( "libcpu indirect control-flow regression: PASS" ); + return( 0 ); +} |
