Skip to content

Commit

Permalink
add reject callx r10 check in vm_validate
Browse files Browse the repository at this point in the history
refactor tests to setup a minimal instr ctx

optimize minimal instr ctx init

add call reg tests
  • Loading branch information
ravyu-jump committed Jun 26, 2024
1 parent b71945c commit 9757139
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 26 deletions.
3 changes: 3 additions & 0 deletions src/flamenco/vm/Local.mk
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ ifdef FD_HAS_SECP256K1
$(call add-hdrs,fd_vm_base.h fd_vm.h fd_vm_private.h) # FIXME: PRIVATE TEMPORARILY HERE DUE TO SOME MESSINESS IN FD_VM_SYSCALL.H
$(call add-objs,fd_vm fd_vm_interp fd_vm_disasm fd_vm_trace,fd_flamenco)

$(call add-hdrs,test_vm_util.h)
$(call add-objs,test_vm_util,fd_flamenco)

$(call make-bin,fd_vm_tool,fd_vm_tool,fd_flamenco fd_funk fd_ballet fd_util fd_disco,$(SECP256K1_LIBS))

$(call make-unit-test,test_vm_interp,test_vm_interp,fd_flamenco fd_funk fd_ballet fd_util fd_disco,$(SECP256K1_LIBS))
Expand Down
39 changes: 29 additions & 10 deletions src/flamenco/vm/fd_vm.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#include "fd_vm_private.h"
#include "../runtime/context/fd_exec_epoch_ctx.h"
#include "../runtime/context/fd_exec_slot_ctx.h"
#include "../features/fd_features.h"

char const *
fd_vm_strerror( int err ) {
Expand Down Expand Up @@ -50,6 +53,7 @@ fd_vm_strerror( int err ) {
case FD_VM_ERR_INCOMPLETE_LDQ: return "INCOMPLETE_LDQ detected an incomplete ldq at program end";
case FD_VM_ERR_LDQ_NO_ADDL_IMM: return "LDQ_NO_ADDL_IMM detected a ldq without an addl imm following it";
case FD_VM_ERR_NO_SUCH_EXT_CALL: return "NO_SUCH_EXT_CALL detected a call imm with no function was registered for that immediate";
case FD_VM_ERR_INVALID_REG: return "INVALID_REG detected an invalid register number in a callx instruction";

default: break;
}
Expand All @@ -72,15 +76,16 @@ fd_vm_validate( fd_vm_t const * vm ) {
/* A mapping of all the possible 1-byte sBPF opcodes to their
validation criteria. */

# define FD_VALID ((uchar)0) /* Valid opcode */
# define FD_CHECK_JMP ((uchar)1) /* Validation should check that the instruction is a valid jump */
# define FD_CHECK_END ((uchar)2) /* Validation should check that the instruction is a valid endianness conversion */
# define FD_CHECK_ST ((uchar)3) /* Validation should check that the instruction is a valid store */
# define FD_CHECK_LDQ ((uchar)4) /* Validation should check that the instruction is a valid load-quad */
# define FD_CHECK_DIV ((uchar)5) /* Validation should check that the instruction is a valid division by immediate */
# define FD_CHECK_SH32 ((uchar)6) /* Validation should check that the immediate is a valid 32-bit shift exponent */
# define FD_CHECK_SH64 ((uchar)7) /* Validation should check that the immediate is a valid 64-bit shift exponent */
# define FD_INVALID ((uchar)8) /* The opcode is invalid */
# define FD_VALID ((uchar)0) /* Valid opcode */
# define FD_CHECK_JMP ((uchar)1) /* Validation should check that the instruction is a valid jump */
# define FD_CHECK_END ((uchar)2) /* Validation should check that the instruction is a valid endianness conversion */
# define FD_CHECK_ST ((uchar)3) /* Validation should check that the instruction is a valid store */
# define FD_CHECK_LDQ ((uchar)4) /* Validation should check that the instruction is a valid load-quad */
# define FD_CHECK_DIV ((uchar)5) /* Validation should check that the instruction is a valid division by immediate */
# define FD_CHECK_SH32 ((uchar)6) /* Validation should check that the immediate is a valid 32-bit shift exponent */
# define FD_CHECK_SH64 ((uchar)7) /* Validation should check that the immediate is a valid 64-bit shift exponent */
# define FD_INVALID ((uchar)8) /* The opcode is invalid */
# define FD_CHECK_CALLX ((uchar)9) /* Validation should check that callx has valid register number */

static uchar const validation_map[ 256 ] = {
/* 0x00 */ FD_INVALID, /* 0x01 */ FD_INVALID, /* 0x02 */ FD_INVALID, /* 0x03 */ FD_INVALID,
Expand Down Expand Up @@ -118,7 +123,7 @@ fd_vm_validate( fd_vm_t const * vm ) {
/* 0x80 */ FD_INVALID, /* 0x81 */ FD_INVALID, /* 0x82 */ FD_INVALID, /* 0x83 */ FD_INVALID,
/* 0x84 */ FD_VALID, /* 0x85 */ FD_VALID, /* 0x86 */ FD_INVALID, /* 0x87 */ FD_VALID,
/* 0x88 */ FD_INVALID, /* 0x89 */ FD_INVALID, /* 0x8a */ FD_INVALID, /* 0x8b */ FD_INVALID,
/* 0x8c */ FD_INVALID, /* 0x8d */ FD_VALID, /* 0x8e */ FD_INVALID, /* 0x8f */ FD_INVALID,
/* 0x8c */ FD_INVALID, /* 0x8d */ FD_CHECK_CALLX,/* 0x8e */ FD_INVALID, /* 0x8f */ FD_INVALID,
/* 0x90 */ FD_INVALID, /* 0x91 */ FD_INVALID, /* 0x92 */ FD_INVALID, /* 0x93 */ FD_INVALID,
/* 0x94 */ FD_CHECK_DIV, /* 0x95 */ FD_VALID, /* 0x96 */ FD_INVALID, /* 0x97 */ FD_CHECK_DIV,
/* 0x98 */ FD_INVALID, /* 0x99 */ FD_INVALID, /* 0x9a */ FD_INVALID, /* 0x9b */ FD_INVALID,
Expand Down Expand Up @@ -201,6 +206,15 @@ fd_vm_validate( fd_vm_t const * vm ) {
break;
}

case FD_CHECK_CALLX: {
/* The register number to read is stored in the immediate.
https://github.com/solana-labs/rbpf/blob/v0.8.1/src/verifier.rs#L218 */
if( FD_UNLIKELY( instr.imm > ( FD_FEATURE_ACTIVE( vm->instr_ctx->slot_ctx, reject_callx_r10 ) ? 9 : 10 ) ) ) {
return FD_VM_ERR_INVALID_REG;
}
break;
}

case FD_INVALID: default: return FD_VM_ERR_INVALID_OPCODE;
}

Expand Down Expand Up @@ -337,6 +351,11 @@ fd_vm_init(
return NULL;
}

if ( FD_UNLIKELY( instr_ctx == NULL ) ) {
FD_LOG_WARNING(( "NULL instr_ctx" ));
return NULL;
}

// Set the vm fields
vm->instr_ctx = instr_ctx;
vm->heap_max = heap_max;
Expand Down
1 change: 1 addition & 0 deletions src/flamenco/vm/fd_vm_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
#define FD_VM_ERR_INCOMPLETE_LDQ (-32) /* detected an incomplete ldq at program end */
#define FD_VM_ERR_LDQ_NO_ADDL_IMM (-33) /* detected a ldq without an addl imm following it */
#define FD_VM_ERR_NO_SUCH_EXT_CALL (-34) /* detected a call imm with no function was registered for that immediate */
#define FD_VM_ERR_INVALID_REG (-35) /* detected an invalid register */

FD_PROTOTYPES_BEGIN

Expand Down
18 changes: 18 additions & 0 deletions src/flamenco/vm/instr_test/jump.instr
Original file line number Diff line number Diff line change
Expand Up @@ -362,3 +362,21 @@ $ op=dd dst=6 src=5 off=ffff r6=3333333344449444 r5 =3333333344444444 : ok # n
$ op=dd dst=6 src=5 off=ffff r6=3333333344444444 r5 =3333333344444444 : err # taken
$ op=dd dst=a src=1 off=0000 : vfy
$ op=dd dst=9 src=b off=0000 : vfy

# call_reg reg[imm] (these should fail during exec by default)
$ op=8d dst=9 src=9 imm=0 : err
$ op=8d dst=9 src=9 imm=1 : err
$ op=8d dst=9 src=9 imm=9 : err
$ op=8d dst=9 src=9 imm=b : vfy
$ op=8d dst=9 src=9 imm=ffffffff : vfy
$ op=8d dst=9 src=9 imm=a : err

$ reject_callx_r10=1
op=8d dst=9 src=9 imm=a : vfy
$ op=8d dst=9 src=9 imm=0 : err
$ op=8d dst=9 src=9 imm=1 : err
$ op=8d dst=9 src=9 imm=9 : err
$ op=8d dst=9 src=9 imm=b : vfy
$ op=8d dst=9 src=9 imm=fffffffff : vfy

$ reject_callx_r10=0
2 changes: 1 addition & 1 deletion src/flamenco/vm/syscall/Local.mk
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ ifdef FD_HAS_HOSTED
$(call add-hdrs,fd_vm_syscall.h fd_vm_cpi.h)
$(call add-objs,fd_vm_syscall fd_vm_syscall_cpi fd_vm_syscall_hash fd_vm_syscall_crypto fd_vm_syscall_curve fd_vm_syscall_pda fd_vm_syscall_runtime fd_vm_syscall_util,fd_flamenco)

$(call make-unit-test,test_vm_syscalls,test_vm_syscalls,fd_flamenco fd_funk fd_ballet fd_util)
$(call make-unit-test,test_vm_syscall_cpi,test_vm_syscall_cpi,fd_flamenco fd_funk fd_ballet fd_util)
$(call make-unit-test,test_vm_syscall_curve,test_vm_syscall_curve,fd_flamenco fd_funk fd_ballet fd_util)
$(call make-unit-test,test_vm_syscalls,test_vm_syscalls,fd_flamenco fd_funk fd_ballet fd_util)

$(call run-unit-test,test_vm_syscalls)
$(call run-unit-test,test_vm_syscall_cpi)
Expand Down
6 changes: 5 additions & 1 deletion src/flamenco/vm/syscall/test_vm_syscalls.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "fd_vm_syscall.h"
#include "../test_vm_util.h"

static inline void set_memory_region( uchar * mem, ulong sz ) { for( ulong i=0UL; i<sz; i++ ) mem[i] = (uchar)(i & 0xffUL); }

Expand Down Expand Up @@ -173,9 +174,11 @@ main( int argc,
uchar rodata[ rodata_sz ];
set_memory_region( rodata, rodata_sz );

fd_exec_instr_ctx_t * instr_ctx = test_vm_minimal_exec_instr_ctx( fd_libc_alloc_virtual(), false );

int vm_ok = !!fd_vm_init(
/* vm */ vm,
/* instr_ctx */ NULL,
/* instr_ctx */ instr_ctx,
/* heap_max */ FD_VM_HEAP_DEFAULT,
/* entry_cu */ FD_VM_COMPUTE_UNIT_LIMIT,
/* rodata */ rodata,
Expand Down Expand Up @@ -460,6 +463,7 @@ main( int argc,
fd_vm_delete ( fd_vm_leave ( vm ) );
fd_sha256_delete( fd_sha256_leave( sha ) );
fd_rng_delete ( fd_rng_leave ( rng ) );
test_vm_exec_instr_ctx_delete( instr_ctx );

FD_LOG_NOTICE(( "pass" ));
fd_halt();
Expand Down
13 changes: 12 additions & 1 deletion src/flamenco/vm/test_vm_instr.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "fd_vm.h"
#include "fd_vm_base.h"
#include "fd_vm_private.h"
#include "test_vm_util.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
Expand Down Expand Up @@ -44,6 +45,7 @@ struct test_input {
ushort off;
ulong imm;
ulong reg[REG_CNT];
bool reject_callx_r10;
};

typedef struct test_input test_input_t;
Expand Down Expand Up @@ -315,6 +317,11 @@ parse_token( test_parser_t * p,
ulong * out = p->state == PARSE_STATE_ASSERT ? p->effects.reg : p->input.reg;
out[ reg ] = parse_hex_int( p );

} else if( 0==strncmp( word, "reject_callx_r10", word_len ) ) {

parse_assign_sep( p );
p->input.reject_callx_r10 = parse_hex_int( p );

} else {

FD_LOG_ERR(( "Unexpected token '%.*s' at %s(%lu)", (int)word_len, word, p->path, p->line ));
Expand Down Expand Up @@ -396,9 +403,11 @@ run_input( test_input_t const * input,
fd_sbpf_syscalls_new(
aligned_alloc( fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ) ) );

fd_exec_instr_ctx_t * instr_ctx = test_vm_minimal_exec_instr_ctx( fd_libc_alloc_virtual(), input->reject_callx_r10 );

int vm_ok = !!fd_vm_init(
/* vm */ vm,
/* instr_ctx */ NULL,
/* instr_ctx */ instr_ctx,
/* heap_max */ 0UL,
/* entry_cu */ 100UL,
/* rodata */ (uchar const *)text,
Expand All @@ -423,6 +432,8 @@ run_input( test_input_t const * input,

run_input2( out, vm );

/* Clean up */
test_vm_exec_instr_ctx_delete( instr_ctx );
free( fd_sbpf_syscalls_delete ( fd_sbpf_syscalls_leave ( syscalls ) ) );
free( fd_sbpf_calldests_delete( fd_sbpf_calldests_leave( calldests ) ) );
free( input_copy );
Expand Down
33 changes: 20 additions & 13 deletions src/flamenco/vm/test_vm_interp.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "fd_vm.h"
#include "fd_vm_base.h"
#include "fd_vm_private.h"
#include "test_vm_util.h"

static int
accumulator_syscall( FD_PARAM_UNUSED void * _vm,
Expand All @@ -15,11 +16,12 @@ accumulator_syscall( FD_PARAM_UNUSED void * _vm,
}

static void
test_program_success( char * test_case_name,
ulong expected_result,
ulong const * text,
ulong text_cnt,
fd_sbpf_syscalls_t * syscalls ) {
test_program_success( char * test_case_name,
ulong expected_result,
ulong const * text,
ulong text_cnt,
fd_sbpf_syscalls_t * syscalls,
fd_exec_instr_ctx_t * instr_ctx ) {
//FD_LOG_NOTICE(( "Test program: %s", test_case_name ));

fd_sha256_t _sha[1];
Expand All @@ -31,7 +33,7 @@ test_program_success( char * test_case_name,

int vm_ok = !!fd_vm_init(
/* vm */ vm,
/* instr_ctx */ NULL,
/* instr_ctx */ instr_ctx,
/* heap_max */ FD_VM_HEAP_DEFAULT,
/* entry_cu */ FD_VM_COMPUTE_UNIT_LIMIT,
/* rodata */ (uchar *)text,
Expand Down Expand Up @@ -215,13 +217,14 @@ test_0cu_exit( void ) {
fd_vm_instr( FD_SBPF_OP_EXIT, 0, 0, 0, 0 )
};
ulong text_cnt = 3UL;
fd_exec_instr_ctx_t * instr_ctx = test_vm_minimal_exec_instr_ctx( fd_libc_alloc_virtual(), false /* not tested here*/ );

/* Ensure the VM exits with success if the CU count after the final
exit instruction reaches zero. */

int vm_ok = !!fd_vm_init(
/* vm */ vm,
/* instr_ctx */ NULL,
/* instr_ctx */ instr_ctx,
/* heap_max */ FD_VM_HEAP_DEFAULT,
/* entry_cu */ text_cnt,
/* rodata */ (uchar *)text,
Expand All @@ -248,7 +251,7 @@ test_0cu_exit( void ) {

vm_ok = !!fd_vm_init(
/* vm */ vm,
/* instr_ctx */ NULL,
/* instr_ctx */ instr_ctx,
/* heap_max */ FD_VM_HEAP_DEFAULT,
/* entry_cu */ text_cnt - 1UL,
/* rodata */ (uchar *)text,
Expand All @@ -271,6 +274,7 @@ test_0cu_exit( void ) {
FD_TEST( fd_vm_exec ( vm )==FD_VM_ERR_SIGCOST );

fd_vm_delete( fd_vm_leave( vm ) );
test_vm_exec_instr_ctx_delete( instr_ctx );
fd_sha256_delete( fd_sha256_leave( sha ) );
}

Expand All @@ -285,11 +289,13 @@ main( int argc,

fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_join( fd_sbpf_syscalls_new( _syscalls ) ); FD_TEST( syscalls );

fd_exec_instr_ctx_t * instr_ctx = test_vm_minimal_exec_instr_ctx( fd_libc_alloc_virtual(), false );

FD_TEST( fd_vm_syscall_register( syscalls, "accumulator", accumulator_syscall )==FD_VM_SUCCESS );

# define TEST_PROGRAM_SUCCESS( test_case_name, expected_result, text_cnt, ... ) do { \
ulong _text[ text_cnt ] = { __VA_ARGS__ }; \
test_program_success( (test_case_name), (expected_result), _text, (text_cnt), syscalls ); \
test_program_success( (test_case_name), (expected_result), _text, (text_cnt), syscalls, instr_ctx ); \
} while(0)

# define FD_SBPF_INSTR(op, dst, src, off, val) (fd_vm_instr( op, dst, src, off, val ))
Expand Down Expand Up @@ -921,23 +927,24 @@ main( int argc,
ulong * text = (ulong *)malloc( sizeof(ulong)*text_cnt ); /* FIXME: gross */

generate_random_alu_instrs( rng, text, text_cnt );
test_program_success( "alu_bench", 0x0, text, text_cnt, syscalls );
test_program_success( "alu_bench", 0x0, text, text_cnt, syscalls, instr_ctx );

generate_random_alu64_instrs( rng, text, text_cnt );
test_program_success( "alu64_bench", 0x0, text, text_cnt, syscalls );
test_program_success( "alu64_bench", 0x0, text, text_cnt, syscalls, instr_ctx );

text_cnt = 1024UL;
generate_random_alu_instrs( rng, text, text_cnt );
test_program_success( "alu_bench_short", 0x0, text, text_cnt, syscalls );
test_program_success( "alu_bench_short", 0x0, text, text_cnt, syscalls, instr_ctx );

generate_random_alu64_instrs( rng, text, text_cnt );
test_program_success( "alu64_bench_short", 0x0, text, text_cnt, syscalls );
test_program_success( "alu64_bench_short", 0x0, text, text_cnt, syscalls, instr_ctx );

test_0cu_exit();

free( text );

fd_sbpf_syscalls_delete( fd_sbpf_syscalls_leave( syscalls ) );
test_vm_exec_instr_ctx_delete( instr_ctx );

FD_LOG_NOTICE(( "pass" ));
fd_rng_delete( fd_rng_leave( rng ) );
Expand Down
64 changes: 64 additions & 0 deletions src/flamenco/vm/test_vm_util.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include "test_vm_util.h"
#include "../runtime/context/fd_exec_epoch_ctx.h"
#include "../runtime/context/fd_exec_slot_ctx.h"

/* Generates a minimal instruction context to supply to fd_vm_t.
For now, we just need to setup feature flags. */
fd_exec_instr_ctx_t *
test_vm_minimal_exec_instr_ctx(
fd_valloc_t valloc,
bool reject_callx_r10 ) {

void * _ctx = fd_exec_instr_ctx_new( fd_valloc_malloc( valloc, FD_EXEC_INSTR_CTX_ALIGN, FD_EXEC_INSTR_CTX_FOOTPRINT ) );

fd_exec_instr_ctx_t * ctx = fd_exec_instr_ctx_join( _ctx );

if ( !ctx ) {
return NULL;
}

ctx->valloc = valloc;

/* Keep slot_ctx and epoch_ctx initialization simple. We only want features ATM.
Feel free to change this to use actual init semantics (*_new and *_join),
but remember to update the cleanup function below :) */
void * _slot_ctx = fd_valloc_malloc( valloc, FD_EXEC_SLOT_CTX_ALIGN, FD_EXEC_SLOT_CTX_FOOTPRINT );
fd_exec_slot_ctx_t * slot_ctx = (fd_exec_slot_ctx_t *)( _slot_ctx );

void * _epoch_ctx = fd_valloc_malloc( valloc, fd_exec_epoch_ctx_align() , sizeof(fd_exec_epoch_ctx_t) );
fd_exec_epoch_ctx_t * epoch_ctx = (fd_exec_epoch_ctx_t *) _epoch_ctx;

if ( !epoch_ctx || !slot_ctx ) {
return NULL;
}

ctx->epoch_ctx = epoch_ctx; /* technically not necessary, given how FEATURE_ACTIVE macro works */
ctx->slot_ctx = slot_ctx;

slot_ctx->epoch_ctx = epoch_ctx;

/* Setup feature flags */
fd_features_disable_all( &epoch_ctx->features );
if ( reject_callx_r10 ) {
fd_features_set( &epoch_ctx->features, fd_feature_id_query(TEST_VM_REJECT_CALLX_R10_FEATURE_PREFIX), 0UL );
}

return ctx;
}

void
test_vm_exec_instr_ctx_delete(
fd_exec_instr_ctx_t * ctx ) {

fd_valloc_t valloc = ctx->valloc;
fd_exec_slot_ctx_t * slot_ctx = ctx->slot_ctx;
fd_exec_epoch_ctx_t * epoch_ctx = slot_ctx->epoch_ctx;

fd_exec_instr_ctx_delete( fd_exec_instr_ctx_leave( ctx ) );

fd_valloc_free( valloc, epoch_ctx );
fd_valloc_free( valloc, slot_ctx );
fd_valloc_free( valloc, ctx );

return;
}
Loading

0 comments on commit 9757139

Please sign in to comment.