LCOV - code coverage report
Current view: top level - flamenco/runtime/tests - fd_vm_harness.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 487 0.0 %
Date: 2025-10-27 04:40:00 Functions: 0 5 0.0 %

          Line data    Source code
       1             : #include "fd_instr_harness.h"
       2             : #include "../fd_executor.h"
       3             : #include "../fd_system_ids.h"
       4             : #include "../program/fd_bpf_loader_serialization.h"
       5             : #include "../context/fd_exec_txn_ctx.h"
       6             : #include "../../../ballet/sbpf/fd_sbpf_loader.h"
       7             : #include "../../vm/fd_vm.h"
       8             : #include "../../vm/test_vm_util.h"
       9             : #include "generated/vm.pb.h"
      10             : 
      11             : static int
      12             : fd_runtime_fuzz_vm_syscall_noop( void * _vm,
      13             :                                  ulong arg0,
      14             :                                  ulong arg1,
      15             :                                  ulong arg2,
      16             :                                  ulong arg3,
      17             :                                  ulong arg4,
      18           0 :                                  ulong* _ret){
      19             :   /* TODO: have input message determine CUs to deduct?
      20             :   fd_vm_t * vm = (fd_vm_t *) _vm;
      21             :   vm->cu = vm->cu - 5;
      22             :   */
      23             : 
      24           0 :   (void) _vm;
      25           0 :   (void) arg0;
      26           0 :   (void) arg1;
      27           0 :   (void) arg2;
      28           0 :   (void) arg3;
      29           0 :   (void) arg4;
      30           0 :   *_ret = 0;
      31           0 :   return 0;
      32           0 : }
      33             : 
      34             : static fd_sbpf_syscalls_t *
      35             : fd_runtime_fuzz_lookup_syscall_func( fd_sbpf_syscalls_t * syscalls,
      36             :                                      const char *         syscall_name,
      37           0 :                                      size_t               len) {
      38           0 :   ulong i;
      39             : 
      40           0 :   if (!syscall_name) return NULL;
      41             : 
      42           0 :   for (i = 0; i < fd_sbpf_syscalls_slot_cnt(); ++i) {
      43           0 :     if (!fd_sbpf_syscalls_key_inval(syscalls[i].key) && syscalls[i].name && strlen(syscalls[i].name) == len) {
      44           0 :       if (!memcmp(syscalls[i].name, syscall_name, len)) {
      45           0 :         return syscalls + i;
      46           0 :       }
      47           0 :     }
      48           0 :   }
      49             : 
      50           0 :   return NULL;
      51           0 : }
      52             : 
      53             : static ulong
      54             : fd_runtime_fuzz_load_from_vm_input_regions( fd_vm_input_region_t const *        input,
      55             :                                             uint                                input_count,
      56             :                                             fd_exec_test_input_data_region_t ** output,
      57             :                                             pb_size_t *                         output_count,
      58             :                                             void *                              output_buf,
      59           0 :                                             ulong                               output_bufsz ) {
      60             :   /* pre-flight checks on output buffer size*/
      61           0 :   ulong input_regions_total_sz = 0;
      62           0 :   for( ulong i=0; i<input_count; i++ ) {
      63           0 :     input_regions_total_sz += input[i].region_sz;
      64           0 :   }
      65             : 
      66           0 :   if( FD_UNLIKELY(   input_regions_total_sz == 0
      67           0 :                   || output_bufsz < input_regions_total_sz ) ) {
      68           0 :     *output = NULL;
      69           0 :     *output_count = 0;
      70           0 :     return 0;
      71           0 :   }
      72             : 
      73           0 :   FD_SCRATCH_ALLOC_INIT( l, output_buf );
      74           0 :   *output = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_input_data_region_t),
      75           0 :                                       input_count * sizeof (fd_exec_test_input_data_region_t) );
      76           0 :   FD_TEST( *output );
      77           0 :   *output_count = input_count;
      78             : 
      79           0 :   for( ulong i=0; i<input_count; i++ ) {
      80           0 :     fd_vm_input_region_t const * vm_region = &input[i];
      81           0 :     fd_exec_test_input_data_region_t * out_region = &(*output)[i];
      82           0 :     out_region->is_writable = vm_region->is_writable;
      83           0 :     out_region->offset = vm_region->vaddr_offset;
      84             : 
      85           0 :     if( vm_region->region_sz > 0 ) {
      86           0 :       out_region->content = FD_SCRATCH_ALLOC_APPEND( l, alignof(pb_bytes_array_t),
      87           0 :                                                  PB_BYTES_ARRAY_T_ALLOCSIZE(vm_region->region_sz) );
      88           0 :       FD_TEST( out_region->content );
      89           0 :       out_region->content->size = vm_region->region_sz;
      90           0 :       fd_memcpy( out_region->content->bytes, (void *)vm_region->haddr, vm_region->region_sz );
      91           0 :     } else {
      92           0 :       out_region->content = NULL;
      93           0 :     }
      94           0 :   }
      95             : 
      96           0 :   ulong end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
      97           0 :   return end - (ulong)output_buf; /* return the number of bytes written */
      98           0 : }
      99             : 
     100             : 
     101             : ulong
     102             : fd_solfuzz_vm_interp_run( fd_solfuzz_runner_t * runner,
     103             :                           void const *          input_,
     104             :                           void **               output_,
     105             :                           void *                output_buf,
     106           0 :                           ulong                 output_bufsz ) {
     107           0 :   fd_exec_test_syscall_context_t const * input = fd_type_pun_const( input_ );
     108           0 :   fd_exec_test_syscall_effects_t      ** output = fd_type_pun( output_ );
     109             : 
     110             :   /* Create execution context */
     111           0 :   const fd_exec_test_instr_context_t * input_instr_ctx = &input->instr_ctx;
     112           0 :   fd_exec_instr_ctx_t instr_ctx[1];
     113           0 :   if( !fd_runtime_fuzz_instr_ctx_create( runner, instr_ctx, input_instr_ctx, true /* is_syscall avoids certain checks we don't want */ ) ) {
     114           0 :     fd_runtime_fuzz_instr_ctx_destroy( runner, instr_ctx );
     115           0 :     return 0UL;
     116           0 :   }
     117             : 
     118           0 :   if( !( input->has_vm_ctx ) ) {
     119           0 :     fd_runtime_fuzz_instr_ctx_destroy( runner, instr_ctx );
     120           0 :     return 0UL;
     121           0 :   }
     122             : 
     123           0 :   fd_spad_t * spad = runner->spad;
     124             : 
     125             :   /* Create effects */
     126           0 :   ulong output_end = (ulong) output_buf + output_bufsz;
     127           0 :   FD_SCRATCH_ALLOC_INIT( l, output_buf );
     128           0 :   fd_exec_test_syscall_effects_t * effects =
     129           0 :     FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_syscall_effects_t),
     130           0 :                                 sizeof (fd_exec_test_syscall_effects_t) );
     131           0 :   *effects = (fd_exec_test_syscall_effects_t) FD_EXEC_TEST_SYSCALL_EFFECTS_INIT_ZERO;
     132             : 
     133           0 :   if( FD_UNLIKELY( _l > output_end ) ) {
     134           0 :     fd_runtime_fuzz_instr_ctx_destroy( runner, instr_ctx );
     135           0 :     return 0UL;
     136           0 :   }
     137             : 
     138           0 : do{
     139             :   /* Setup regions */
     140           0 :   if ( !input->vm_ctx.rodata ) {
     141           0 :     break;
     142           0 :   }
     143           0 :   ulong   rodata_sz = input->vm_ctx.rodata->size;
     144           0 :   uchar * rodata = fd_spad_alloc_check( spad, 8UL, rodata_sz );
     145           0 :   memcpy( rodata, input->vm_ctx.rodata->bytes, rodata_sz );
     146             : 
     147             :   /* Setup input region */
     148           0 :   ulong                    input_sz                                = 0UL;
     149           0 :   ulong                    pre_lens[256]                           = {0};
     150           0 :   fd_vm_input_region_t     input_mem_regions[1000]                 = {0}; /* We can have a max of (3 * num accounts + 1) regions */
     151           0 :   fd_vm_acc_region_meta_t  acc_region_metas[256]                   = {0}; /* instr acc idx to idx */
     152           0 :   uint                     input_mem_regions_cnt                   = 0UL;
     153           0 :   int                      direct_mapping                          = FD_FEATURE_ACTIVE( instr_ctx->txn_ctx->slot, &instr_ctx->txn_ctx->features, account_data_direct_mapping );
     154           0 :   int                      stricter_abi_and_runtime_constraints    = FD_FEATURE_ACTIVE( instr_ctx->txn_ctx->slot, &instr_ctx->txn_ctx->features, stricter_abi_and_runtime_constraints );
     155             : 
     156           0 :   uchar *                  input_ptr      = NULL;
     157           0 :   uchar                    program_id_idx = instr_ctx->instr->program_id;
     158           0 :   fd_txn_account_t const * program_acc    = &instr_ctx->txn_ctx->accounts[program_id_idx];
     159           0 :   uchar                    is_deprecated  = ( program_id_idx < instr_ctx->txn_ctx->accounts_cnt ) &&
     160           0 :                                             ( !memcmp( fd_txn_account_get_owner( program_acc ), fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) );
     161             : 
     162             :   /* Push the instruction onto the stack. This may also modify the sysvar instructions account, if its present. */
     163           0 :   int stack_push_err = fd_instr_stack_push( instr_ctx->txn_ctx, (fd_instr_info_t *)instr_ctx->instr );
     164           0 :   if( FD_UNLIKELY( stack_push_err ) ) {
     165           0 :     FD_LOG_WARNING(( "instr stack push err" ));
     166           0 :     fd_runtime_fuzz_instr_ctx_destroy( runner, instr_ctx );
     167           0 :     return 0;
     168           0 :   }
     169             : 
     170             :   /* Serialize accounts into input memory region. */
     171           0 :   int err = fd_bpf_loader_input_serialize_parameters( instr_ctx,
     172           0 :                                                       &input_sz,
     173           0 :                                                       pre_lens,
     174           0 :                                                       input_mem_regions,
     175           0 :                                                       &input_mem_regions_cnt,
     176           0 :                                                       acc_region_metas,
     177           0 :                                                       stricter_abi_and_runtime_constraints,
     178           0 :                                                       direct_mapping,
     179           0 :                                                       is_deprecated,
     180           0 :                                                       &input_ptr );
     181           0 :   if( FD_UNLIKELY( err ) ) {
     182           0 :     fd_runtime_fuzz_instr_ctx_destroy( runner, instr_ctx );
     183           0 :     return 0;
     184           0 :   }
     185             : 
     186           0 :   if( input->vm_ctx.heap_max>FD_VM_HEAP_DEFAULT ) {
     187           0 :     break;
     188           0 :   }
     189             : 
     190             :   /* Setup calldests from call_whitelist.
     191             :      Alloc calldests with the expected size (1 bit per ix, rounded up to ulong) */
     192           0 :   ulong max_pc = (rodata_sz + 7) / 8;
     193           0 :   ulong calldests_footprint = fd_sbpf_calldests_footprint( max_pc );
     194           0 :   void * calldests_mem = fd_spad_alloc_check( spad, fd_sbpf_calldests_align(), calldests_footprint );
     195           0 :   ulong * calldests = fd_sbpf_calldests_join( fd_sbpf_calldests_new( calldests_mem, max_pc ) );
     196           0 :   if( input->vm_ctx.call_whitelist && input->vm_ctx.call_whitelist->size > 0 ) {
     197           0 :     memcpy( calldests, input->vm_ctx.call_whitelist->bytes, input->vm_ctx.call_whitelist->size );
     198             :     /* Make sure bits over max_pc are all 0s. */
     199           0 :     ulong mask = (1UL << (max_pc % 64)) - 1UL;
     200           0 :     if ( max_pc % 64 != 0) {
     201           0 :       calldests[ max_pc / 64 ] &= mask;
     202           0 :     }
     203           0 :   }
     204           0 :   ulong entry_pc = fd_ulong_min( input->vm_ctx.entry_pc, rodata_sz / 8UL - 1UL );
     205           0 :   if( input->vm_ctx.sbpf_version >= FD_SBPF_V3 ) {
     206             :     /* in v3 we have to enable the entrypoint */
     207           0 :     calldests[ entry_pc / 64UL ] |= ( 1UL << ( entry_pc % 64UL ) );
     208           0 :   }
     209             : 
     210             :   /* Setup syscalls. Have them all be no-ops */
     211           0 :   fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new( fd_spad_alloc_check( spad, fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ) );
     212           0 :   fd_vm_syscall_register_slot( syscalls,
     213           0 :                                instr_ctx->txn_ctx->slot,
     214           0 :                                &instr_ctx->txn_ctx->features,
     215           0 :                                0 );
     216             : 
     217           0 :   for( ulong i=0; i< fd_sbpf_syscalls_slot_cnt(); i++ ){
     218           0 :     if( !fd_sbpf_syscalls_key_inval( syscalls[i].key ) ) {
     219           0 :       syscalls[i].func = fd_runtime_fuzz_vm_syscall_noop;
     220           0 :     }
     221           0 :   }
     222             : 
     223             :   /* Setup trace */
     224           0 :   const int enable_vm_tracing = runner->enable_vm_tracing;
     225           0 :   fd_vm_trace_t * trace       = NULL;
     226             : 
     227           0 :   if ( FD_UNLIKELY( enable_vm_tracing ) ) {
     228           0 :     trace = fd_vm_trace_new( fd_spad_alloc_check( spad, fd_vm_trace_align(), fd_vm_trace_footprint( FD_RUNTIME_VM_TRACE_EVENT_MAX, FD_RUNTIME_VM_TRACE_EVENT_DATA_MAX ) ), FD_RUNTIME_VM_TRACE_EVENT_MAX, FD_RUNTIME_VM_TRACE_EVENT_DATA_MAX );
     229           0 :   }
     230             : 
     231             :   /* Setup vm */
     232           0 :   fd_vm_t * vm = fd_vm_join( fd_vm_new( fd_spad_alloc_check( spad, fd_vm_align(), fd_vm_footprint() ) ) );
     233           0 :   FD_TEST( vm );
     234             : 
     235           0 :   fd_vm_init(
     236           0 :     vm,
     237           0 :     instr_ctx,
     238           0 :     input->vm_ctx.heap_max,
     239           0 :     input->has_instr_ctx ? input->instr_ctx.cu_avail : 0,
     240           0 :     rodata,
     241           0 :     rodata_sz,
     242           0 :     (ulong *) rodata, /* text*, same as rodata */
     243           0 :     rodata_sz / 8, /* text_cnt */
     244           0 :     0, /* text_off */
     245           0 :     rodata_sz, /* text_sz */
     246           0 :     entry_pc,
     247           0 :     calldests,
     248           0 :     input->vm_ctx.sbpf_version,
     249           0 :     syscalls,
     250           0 :     trace, /* trace */
     251           0 :     NULL, /* sha */
     252           0 :     input_mem_regions,
     253           0 :     input_mem_regions_cnt,
     254           0 :     acc_region_metas, /* vm_acc_region_meta*/
     255           0 :     is_deprecated, /* is deprecated */
     256           0 :     direct_mapping, /* direct mapping */
     257           0 :     stricter_abi_and_runtime_constraints, /* stricter_abi_and_runtime_constraints */
     258           0 :     0 /* dump_syscall_to_pb */
     259           0 :   );
     260             : 
     261             :   /* Setup registers.
     262             :      r1, r10, r11 are initialized by EbpfVm::new (r10) or EbpfVm::execute_program (r1, r11),
     263             :      or equivalently by fd_vm_init and fd_vm_setup_state_for_execution.
     264             :      Modifying them will most like break execution.
     265             :      In syscalls we allow override them (especially r1) because that simulates the fact
     266             :      that a program partially executed before reaching the syscall.
     267             :      Here we want to test what happens when the program starts from the beginning. */
     268           0 :   vm->reg[0]  = input->vm_ctx.r0;
     269             :   // vm->reg[1]  = input->vm_ctx.r1; // do not override
     270           0 :   vm->reg[2]  = input->vm_ctx.r2;
     271           0 :   vm->reg[3]  = input->vm_ctx.r3;
     272           0 :   vm->reg[4]  = input->vm_ctx.r4;
     273           0 :   vm->reg[5]  = input->vm_ctx.r5;
     274           0 :   vm->reg[6]  = input->vm_ctx.r6;
     275           0 :   vm->reg[7]  = input->vm_ctx.r7;
     276           0 :   vm->reg[8]  = input->vm_ctx.r8;
     277           0 :   vm->reg[9]  = input->vm_ctx.r9;
     278             :   // vm->reg[10]  = input->vm_ctx.r10; // do not override
     279             :   // vm->reg[11]  = input->vm_ctx.r11; // do not override
     280             : 
     281             :   // Validate the vm
     282           0 :   if( fd_vm_validate( vm ) != FD_VM_SUCCESS ) {
     283             :     // custom error, avoid -1 because we use it for "unknown error" in solfuzz-agave
     284           0 :     effects->error = -2;
     285           0 :     break;
     286           0 :   }
     287             : 
     288           0 :   if( input->syscall_invocation.stack_prefix ) {
     289           0 :     uchar * stack    = input->syscall_invocation.stack_prefix->bytes;
     290           0 :     ulong   stack_sz = fd_ulong_min(input->syscall_invocation.stack_prefix->size, FD_VM_STACK_MAX);
     291           0 :     fd_memcpy( vm->stack, stack, stack_sz );
     292           0 :   }
     293             : 
     294           0 :   if( input->syscall_invocation.heap_prefix ) {
     295           0 :     uchar * heap    = input->syscall_invocation.heap_prefix->bytes;
     296           0 :     ulong   heap_sz = fd_ulong_min(input->syscall_invocation.heap_prefix->size, FD_VM_HEAP_MAX);
     297           0 :     fd_memcpy( vm->heap, heap, heap_sz );
     298           0 :   }
     299             : 
     300             :   /* Run vm */
     301           0 :   int exec_res = 0;
     302           0 :   if ( FD_UNLIKELY( enable_vm_tracing ) ) {
     303           0 :     exec_res = fd_vm_exec_trace( vm );
     304           0 :     if( enable_vm_tracing ) fd_vm_trace_printf( trace, syscalls );
     305           0 :     fd_vm_trace_delete( fd_vm_trace_leave( trace ) );
     306           0 :   } else {
     307           0 :     exec_res = fd_vm_exec_notrace( vm );
     308           0 :   }
     309             : 
     310           0 :   effects->error       = -exec_res;
     311             : 
     312             :   /* We do not compare VM state on CU errors since CU accounting
     313             :      is non-conformant with the Agave JIT/Interpreter.
     314             :      CU consumption is not precisely defined when VM faults */
     315           0 :   if( FD_UNLIKELY( exec_res==FD_VM_ERR_EBPF_EXCEEDED_MAX_INSTRUCTIONS ) )
     316           0 :     break;
     317             : 
     318             :   /* Capture remaining outputs */
     319           0 :   effects->cu_avail    = vm->cu;
     320           0 :   effects->frame_count = vm->frame_cnt;
     321             :   /* Only capture registers if no error */
     322           0 :   effects->r0          = exec_res ? 0 : vm->reg[0];
     323           0 :   effects->r1          = exec_res ? 0 : vm->reg[1];
     324           0 :   effects->r2          = exec_res ? 0 : vm->reg[2];
     325           0 :   effects->r3          = exec_res ? 0 : vm->reg[3];
     326           0 :   effects->r4          = exec_res ? 0 : vm->reg[4];
     327           0 :   effects->r5          = exec_res ? 0 : vm->reg[5];
     328           0 :   effects->r6          = exec_res ? 0 : vm->reg[6];
     329           0 :   effects->r7          = exec_res ? 0 : vm->reg[7];
     330           0 :   effects->r8          = exec_res ? 0 : vm->reg[8];
     331           0 :   effects->r9          = exec_res ? 0 : vm->reg[9];
     332           0 :   effects->r10         = exec_res ? 0 : vm->reg[10];
     333             : 
     334             :   /* skip logs since syscalls are stubbed */
     335             : 
     336           0 :   effects->pc = vm->pc;
     337             : 
     338           0 :   if( vm->heap_max > 0 ) {
     339           0 :     effects->heap       = FD_SCRATCH_ALLOC_APPEND(
     340           0 :       l, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( vm->heap_max ) );
     341           0 :     effects->heap->size = (uint)vm->heap_max;
     342           0 :     fd_memcpy( effects->heap->bytes, vm->heap, vm->heap_max );
     343           0 :   }
     344             : 
     345             :   /* Compress stack by removing right-most 0s.
     346             :      This reduces the total size of effects/fixtures when stack is not used,
     347             :      otherwise each would waste 256kB. */
     348           0 :   int rtrim_sz;
     349           0 :   for( rtrim_sz=FD_VM_STACK_MAX-1; rtrim_sz>=0; rtrim_sz-- ) {
     350           0 :     if( vm->stack[rtrim_sz] != 0 ) break;
     351           0 :   }
     352           0 :   if( rtrim_sz > 0 || (vm->stack[0] != 0) ) {
     353           0 :     effects->stack       = FD_SCRATCH_ALLOC_APPEND(
     354           0 :       l, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( FD_VM_STACK_MAX ) );
     355           0 :     effects->stack->size = (uint)rtrim_sz+1;
     356           0 :     fd_memcpy( effects->stack->bytes, vm->stack, (ulong)rtrim_sz+1 );
     357           0 :   }
     358             : 
     359           0 :   effects->rodata       = FD_SCRATCH_ALLOC_APPEND(
     360           0 :     l, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( rodata_sz ) );
     361           0 :   effects->rodata->size = (uint)rodata_sz;
     362           0 :   fd_memcpy( effects->rodata->bytes, rodata, rodata_sz );
     363             : 
     364             :   /* Capture input data regions */
     365           0 :   ulong tmp_end = FD_SCRATCH_ALLOC_FINI(l, 1UL);
     366           0 :   ulong input_data_regions_size = fd_runtime_fuzz_load_from_vm_input_regions( vm->input_mem_regions,
     367           0 :                                                                               vm->input_mem_regions_cnt,
     368           0 :                                                                               &effects->input_data_regions,
     369           0 :                                                                               &effects->input_data_regions_count,
     370           0 :                                                                               (void *) tmp_end,
     371           0 :                                                                               fd_ulong_sat_sub( output_end, tmp_end) );
     372           0 :   FD_SCRATCH_ALLOC_APPEND( l, 1UL, input_data_regions_size );
     373             : 
     374           0 : } while(0);
     375             : 
     376           0 :   ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
     377           0 :   *output = effects;
     378           0 :   fd_runtime_fuzz_instr_ctx_destroy( runner, instr_ctx );
     379           0 :   return actual_end - (ulong)output_buf;
     380           0 : }
     381             : 
     382             : ulong
     383             : fd_solfuzz_syscall_run( fd_solfuzz_runner_t * runner,
     384             :                         void const *          input_,
     385             :                         void **               output_,
     386             :                         void *                output_buf,
     387           0 :                         ulong                 output_bufsz ) {
     388           0 :   fd_exec_test_syscall_context_t const * input =  fd_type_pun_const( input_ );
     389           0 :   fd_exec_test_syscall_effects_t **      output = fd_type_pun( output_ );
     390             : 
     391             :   /* Create execution context */
     392           0 :   const fd_exec_test_instr_context_t * input_instr_ctx = &input->instr_ctx;
     393           0 :   fd_exec_instr_ctx_t ctx[1];
     394             :   // Skip extra checks for non-CPI syscalls
     395           0 :   int is_cpi            = !strncmp( (const char *)input->syscall_invocation.function_name.bytes, "sol_invoke_signed", 17 );
     396           0 :   int skip_extra_checks = !is_cpi;
     397             : 
     398           0 :   if( !fd_runtime_fuzz_instr_ctx_create( runner, ctx, input_instr_ctx, skip_extra_checks ) )
     399           0 :     goto error;
     400             : 
     401           0 :   ctx->txn_ctx->instr_trace[0].instr_info = (fd_instr_info_t *)ctx->instr;
     402           0 :   ctx->txn_ctx->instr_trace[0].stack_height = 1;
     403             : 
     404             :   /* Capture outputs */
     405           0 :   ulong output_end = (ulong)output_buf + output_bufsz;
     406           0 :   FD_SCRATCH_ALLOC_INIT( l, output_buf );
     407           0 :   fd_exec_test_syscall_effects_t * effects =
     408           0 :     FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_syscall_effects_t),
     409           0 :                                 sizeof (fd_exec_test_syscall_effects_t) );
     410           0 :   if( FD_UNLIKELY( _l > output_end ) ) {
     411           0 :     goto error;
     412           0 :   }
     413             : 
     414           0 :   if( input->vm_ctx.return_data.program_id && input->vm_ctx.return_data.program_id->size == sizeof(fd_pubkey_t) ) {
     415           0 :     fd_memcpy( ctx->txn_ctx->return_data.program_id.uc, input->vm_ctx.return_data.program_id->bytes, sizeof(fd_pubkey_t) );
     416           0 :   }
     417             : 
     418           0 :   if( input->vm_ctx.return_data.data && input->vm_ctx.return_data.data->size>0U ) {
     419           0 :     ctx->txn_ctx->return_data.len = input->vm_ctx.return_data.data->size;
     420           0 :     fd_memcpy( ctx->txn_ctx->return_data.data, input->vm_ctx.return_data.data->bytes, ctx->txn_ctx->return_data.len );
     421           0 :   }
     422             : 
     423           0 :   *effects = (fd_exec_test_syscall_effects_t) FD_EXEC_TEST_SYSCALL_EFFECTS_INIT_ZERO;
     424             : 
     425             :   /* Set up the VM instance */
     426           0 :   fd_spad_t * spad = runner->spad;
     427           0 :   fd_sha256_t _sha[1];
     428           0 :   fd_sha256_t * sha = fd_sha256_join( fd_sha256_new( _sha ) );
     429           0 :   fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new( fd_spad_alloc_check( spad, fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ) );
     430           0 :   fd_vm_syscall_register_all( syscalls, 0 );
     431             : 
     432             :   /* Pull out the memory regions */
     433           0 :   if( !input->has_vm_ctx ) {
     434           0 :     goto error;
     435           0 :   }
     436             : 
     437           0 :   ulong rodata_sz = input->vm_ctx.rodata ? input->vm_ctx.rodata->size : 0UL;
     438           0 :   uchar * rodata = fd_spad_alloc_check( spad, 8UL, rodata_sz );
     439           0 :   if ( input->vm_ctx.rodata != NULL ) {
     440           0 :     fd_memcpy( rodata, input->vm_ctx.rodata->bytes, rodata_sz );
     441           0 :   }
     442             : 
     443           0 :   if( input->vm_ctx.heap_max > FD_VM_HEAP_MAX ) {
     444           0 :     goto error;
     445           0 :   }
     446             : 
     447           0 :   fd_vm_t * vm = fd_vm_join( fd_vm_new( fd_spad_alloc_check( spad, fd_vm_align(), fd_vm_footprint() ) ) );
     448           0 :   if ( !vm ) {
     449           0 :     goto error;
     450           0 :   }
     451             : 
     452             :   /* If the program ID account owner is the v1 BPF loader, then alignment is disabled (controlled by
     453             :      the `is_deprecated` flag) */
     454             : 
     455           0 :   ulong                   input_sz                               = 0UL;
     456           0 :   ulong                   pre_lens[256]                          = {0};
     457           0 :   fd_vm_input_region_t    input_mem_regions[1000]                = {0}; /* We can have a max of (3 * num accounts + 1) regions */
     458           0 :   fd_vm_acc_region_meta_t acc_region_metas[256]                  = {0}; /* instr acc idx to idx */
     459           0 :   uint                    input_mem_regions_cnt                  = 0U;
     460           0 :   int                     direct_mapping                         = FD_FEATURE_ACTIVE( ctx->txn_ctx->slot, &ctx->txn_ctx->features, account_data_direct_mapping );
     461           0 :   int                     stricter_abi_and_runtime_constraints   = FD_FEATURE_ACTIVE( ctx->txn_ctx->slot, &ctx->txn_ctx->features, stricter_abi_and_runtime_constraints );
     462             : 
     463           0 :   uchar *            input_ptr      = NULL;
     464           0 :   uchar              program_id_idx = ctx->instr->program_id;
     465           0 :   fd_txn_account_t * program_acc    = &ctx->txn_ctx->accounts[program_id_idx];
     466           0 :   uchar              is_deprecated  = ( program_id_idx < ctx->txn_ctx->accounts_cnt ) &&
     467           0 :                                       ( !memcmp( fd_txn_account_get_owner( program_acc ), fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) );
     468             : 
     469             :   /* Push the instruction onto the stack. This may also modify the sysvar instructions account, if its present. */
     470           0 :   int stack_push_err = fd_instr_stack_push( ctx->txn_ctx, (fd_instr_info_t *)ctx->instr );
     471           0 :   if( FD_UNLIKELY( stack_push_err ) ) {
     472           0 :       FD_LOG_WARNING(( "instr stack push err" ));
     473           0 :       goto error;
     474           0 :   }
     475             : 
     476             :   /* Serialize accounts into input memory region. */
     477           0 :   int err = fd_bpf_loader_input_serialize_parameters( ctx,
     478           0 :                                                       &input_sz,
     479           0 :                                                       pre_lens,
     480           0 :                                                       input_mem_regions,
     481           0 :                                                       &input_mem_regions_cnt,
     482           0 :                                                       acc_region_metas,
     483           0 :                                                       stricter_abi_and_runtime_constraints,
     484           0 :                                                       direct_mapping,
     485           0 :                                                       is_deprecated,
     486           0 :                                                       &input_ptr );
     487           0 :   if( FD_UNLIKELY( err ) ) {
     488           0 :     FD_LOG_WARNING(( "bpf loader input serialize parameters err" ));
     489           0 :     goto error;
     490           0 :   }
     491             : 
     492           0 :   fd_vm_init( vm,
     493           0 :               ctx,
     494           0 :               input->vm_ctx.heap_max,
     495           0 :               ctx->txn_ctx->compute_budget_details.compute_meter,
     496           0 :               rodata,
     497           0 :               rodata_sz,
     498           0 :               NULL, // TODO
     499           0 :               0, // TODO
     500           0 :               0, // TODO
     501           0 :               0, // TODO, text_sz
     502           0 :               0, // TODO
     503           0 :               NULL, // TODO
     504           0 :               TEST_VM_DEFAULT_SBPF_VERSION,
     505           0 :               syscalls,
     506           0 :               NULL, // TODO
     507           0 :               sha,
     508           0 :               input_mem_regions,
     509           0 :               input_mem_regions_cnt,
     510           0 :               acc_region_metas,
     511           0 :               is_deprecated,
     512           0 :               direct_mapping,
     513           0 :               stricter_abi_and_runtime_constraints,
     514           0 :               0 /* dump_syscall_to_pb */ );
     515             : 
     516             :   // Override some execution state values from the syscall fuzzer input
     517             :   // This is so we can test if the syscall mutates any of these erroneously
     518           0 :   vm->reg[0] = input->vm_ctx.r0;
     519           0 :   vm->reg[1] = input->vm_ctx.r1;
     520           0 :   vm->reg[2] = input->vm_ctx.r2;
     521           0 :   vm->reg[3] = input->vm_ctx.r3;
     522           0 :   vm->reg[4] = input->vm_ctx.r4;
     523           0 :   vm->reg[5] = input->vm_ctx.r5;
     524           0 :   vm->reg[6] = input->vm_ctx.r6;
     525           0 :   vm->reg[7] = input->vm_ctx.r7;
     526           0 :   vm->reg[8] = input->vm_ctx.r8;
     527           0 :   vm->reg[9] = input->vm_ctx.r9;
     528           0 :   vm->reg[10] = input->vm_ctx.r10;
     529           0 :   vm->reg[11] = input->vm_ctx.r11;
     530             : 
     531             :   // Override initial part of the heap, if specified the syscall fuzzer input
     532           0 :   if( input->syscall_invocation.heap_prefix ) {
     533           0 :     fd_memcpy( vm->heap, input->syscall_invocation.heap_prefix->bytes,
     534           0 :                fd_ulong_min(input->syscall_invocation.heap_prefix->size, vm->heap_max) );
     535           0 :   }
     536             : 
     537             :   // Override initial part of the stack, if specified the syscall fuzzer input
     538           0 :   if( input->syscall_invocation.stack_prefix ) {
     539           0 :     fd_memcpy( vm->stack, input->syscall_invocation.stack_prefix->bytes,
     540           0 :                fd_ulong_min(input->syscall_invocation.stack_prefix->size, FD_VM_STACK_MAX) );
     541           0 :   }
     542             : 
     543             :   // Look up the syscall to execute
     544           0 :   char * syscall_name = (char *)input->syscall_invocation.function_name.bytes;
     545           0 :   fd_sbpf_syscalls_t const * syscall = fd_runtime_fuzz_lookup_syscall_func(syscalls, syscall_name, input->syscall_invocation.function_name.size);
     546           0 :   if( !syscall ) {
     547           0 :     goto error;
     548           0 :   }
     549             : 
     550             :   /* There's an instr ctx struct embedded in the txn ctx instr stack. */
     551           0 :   fd_exec_instr_ctx_t * instr_ctx = &ctx->txn_ctx->instr_stack[ ctx->txn_ctx->instr_stack_sz - 1 ];
     552           0 :   *instr_ctx = (fd_exec_instr_ctx_t) {
     553           0 :     .instr     = ctx->instr,
     554           0 :     .txn_ctx   = ctx->txn_ctx,
     555           0 :   };
     556             : 
     557             :   /* Actually invoke the syscall */
     558           0 :   int syscall_err = syscall->func( vm, vm->reg[1], vm->reg[2], vm->reg[3], vm->reg[4], vm->reg[5], &vm->reg[0] );
     559           0 :   int stack_pop_err = fd_instr_stack_pop( ctx->txn_ctx, ctx->instr );
     560           0 :   if( FD_UNLIKELY( stack_pop_err ) ) {
     561           0 :       FD_LOG_WARNING(( "instr stack pop err" ));
     562           0 :       goto error;
     563           0 :   }
     564           0 :   if( syscall_err ) {
     565           0 :     fd_log_collector_program_failure( vm->instr_ctx );
     566           0 :   }
     567             : 
     568             :   /* Capture the effects */
     569           0 :   int exec_err = vm->instr_ctx->txn_ctx->exec_err;
     570           0 :   effects->error = 0;
     571           0 :   if( syscall_err ) {
     572           0 :     if( exec_err==0 ) {
     573           0 :       FD_LOG_WARNING(( "TODO: syscall returns error, but exec_err not set. this is probably missing a log." ));
     574           0 :       effects->error = -1;
     575           0 :     } else {
     576           0 :       effects->error = (exec_err <= 0) ? -exec_err : -1;
     577             : 
     578             :       /* Map error kind, equivalent to:
     579             :           effects->error_kind = (fd_exec_test_err_kind_t)(vm->instr_ctx->txn_ctx->exec_err_kind); */
     580           0 :       switch (vm->instr_ctx->txn_ctx->exec_err_kind) {
     581           0 :         case FD_EXECUTOR_ERR_KIND_EBPF:
     582           0 :           effects->error_kind = FD_EXEC_TEST_ERR_KIND_EBPF;
     583           0 :           break;
     584           0 :         case FD_EXECUTOR_ERR_KIND_SYSCALL:
     585           0 :           effects->error_kind = FD_EXEC_TEST_ERR_KIND_SYSCALL;
     586           0 :           break;
     587           0 :         case FD_EXECUTOR_ERR_KIND_INSTR:
     588           0 :           effects->error_kind = FD_EXEC_TEST_ERR_KIND_INSTRUCTION;
     589           0 :           break;
     590           0 :         default:
     591           0 :           effects->error_kind = FD_EXEC_TEST_ERR_KIND_UNSPECIFIED;
     592           0 :           break;
     593           0 :       }
     594           0 :     }
     595           0 :   }
     596           0 :   effects->r0 = syscall_err ? 0 : vm->reg[0]; // Save only on success
     597           0 :   effects->cu_avail = (ulong)vm->cu;
     598             : 
     599           0 :   if( vm->heap_max ) {
     600           0 :     effects->heap = FD_SCRATCH_ALLOC_APPEND(
     601           0 :       l, alignof(uint), PB_BYTES_ARRAY_T_ALLOCSIZE( vm->heap_max ) );
     602           0 :     if( FD_UNLIKELY( _l > output_end ) ) {
     603           0 :       goto error;
     604           0 :     }
     605           0 :     effects->heap->size = (uint)vm->heap_max;
     606           0 :     fd_memcpy( effects->heap->bytes, vm->heap, vm->heap_max );
     607           0 :   } else {
     608           0 :     effects->heap = NULL;
     609           0 :   }
     610             : 
     611           0 :   effects->stack = FD_SCRATCH_ALLOC_APPEND(
     612           0 :     l, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( FD_VM_STACK_MAX ) );
     613           0 :     if( FD_UNLIKELY( _l > output_end ) ) {
     614           0 :       goto error;
     615           0 :     }
     616           0 :   effects->stack->size = (uint)FD_VM_STACK_MAX;
     617           0 :   fd_memcpy( effects->stack->bytes, vm->stack, FD_VM_STACK_MAX );
     618             : 
     619           0 :   if( vm->rodata_sz ) {
     620           0 :     effects->rodata = FD_SCRATCH_ALLOC_APPEND(
     621           0 :       l, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( rodata_sz ) );
     622           0 :     if( FD_UNLIKELY( _l > output_end ) ) {
     623           0 :       goto error;
     624           0 :     }
     625           0 :     effects->rodata->size = (uint)rodata_sz;
     626           0 :     fd_memcpy( effects->rodata->bytes, vm->rodata, rodata_sz );
     627           0 :   } else {
     628           0 :     effects->rodata = NULL;
     629           0 :   }
     630             : 
     631           0 :   effects->frame_count = vm->frame_cnt;
     632             : 
     633           0 :   fd_log_collector_t * log = &vm->instr_ctx->txn_ctx->log_collector;
     634             :   /* Only collect log on valid errors (i.e., != -1). Follows
     635             :      https://github.com/firedancer-io/solfuzz-agave/blob/99758d3c4f3a342d56e2906936458d82326ae9a8/src/utils/err_map.rs#L148 */
     636           0 :   if( effects->error != -1 && log->buf_sz ) {
     637           0 :     effects->log = FD_SCRATCH_ALLOC_APPEND(
     638           0 :       l, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( log->buf_sz ) );
     639           0 :     if( FD_UNLIKELY( _l > output_end ) ) {
     640           0 :       goto error;
     641           0 :     }
     642           0 :     effects->log->size = (uint)fd_log_collector_debug_sprintf( log, (char *)effects->log->bytes, 0 );
     643           0 :   } else {
     644           0 :     effects->log = NULL;
     645           0 :   }
     646             : 
     647             :   /* Capture input regions */
     648           0 :   effects->inputdata = NULL; /* Deprecated, using input_data_regions instead */
     649           0 :   ulong tmp_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
     650           0 :   ulong input_regions_size = fd_runtime_fuzz_load_from_vm_input_regions( vm->input_mem_regions,
     651           0 :                                                                          vm->input_mem_regions_cnt,
     652           0 :                                                                          &effects->input_data_regions,
     653           0 :                                                                          &effects->input_data_regions_count,
     654           0 :                                                                          (void *)tmp_end,
     655           0 :                                                                          fd_ulong_sat_sub( output_end, tmp_end ) );
     656             : 
     657           0 :   if( !!vm->input_mem_regions_cnt && !effects->input_data_regions ) {
     658           0 :     goto error;
     659           0 :   }
     660             : 
     661             :   /* Return the effects */
     662           0 :   ulong actual_end = tmp_end + input_regions_size;
     663           0 :   fd_runtime_fuzz_instr_ctx_destroy( runner, ctx );
     664             : 
     665           0 :   *output = effects;
     666           0 :   return actual_end - (ulong)output_buf;
     667             : 
     668           0 : error:
     669           0 :   fd_runtime_fuzz_instr_ctx_destroy( runner, ctx );
     670           0 :   return 0;
     671           0 : }

Generated by: LCOV version 1.14