LCOV - code coverage report
Current view: top level - flamenco/vm/syscall - fd_vm_syscall_util.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 297 428 69.4 %
Date: 2025-08-05 05:04:49 Functions: 8 13 61.5 %

          Line data    Source code
       1             : #include "fd_vm_syscall.h"
       2             : 
       3             : #include "../../../ballet/base64/fd_base64.h"
       4             : #include "../../../ballet/utf8/fd_utf8.h"
       5             : #include "../../runtime/sysvar/fd_sysvar.h"
       6             : #include "../../runtime/sysvar/fd_sysvar_clock.h"
       7             : #include "../../runtime/sysvar/fd_sysvar_epoch_schedule.h"
       8             : #include "../../runtime/context/fd_exec_txn_ctx.h"
       9             : #include "../../runtime/context/fd_exec_instr_ctx.h"
      10             : 
      11             : int
      12             : fd_vm_syscall_abort( /**/            void *  _vm,
      13             :                      FD_PARAM_UNUSED ulong   r1,
      14             :                      FD_PARAM_UNUSED ulong   r2,
      15             :                      FD_PARAM_UNUSED ulong   r3,
      16             :                      FD_PARAM_UNUSED ulong   r4,
      17             :                      FD_PARAM_UNUSED ulong   r5,
      18           0 :                      FD_PARAM_UNUSED ulong * _ret ) {
      19             :   /* https://github.com/anza-xyz/agave/blob/v2.0.6/programs/bpf_loader/src/syscalls/mod.rs#L630 */
      20           0 :   fd_vm_t * vm = (fd_vm_t *)_vm;
      21           0 :   FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_ABORT );
      22           0 :   return FD_VM_SYSCALL_ERR_ABORT;
      23           0 : }
      24             : 
      25             : /* FD_TRANSLATE_STRING returns a read only pointer to the host address of
      26             :    a valid utf8 string, or it errors.
      27             : 
      28             :    Analogous of Agave's translate_string_and_do().
      29             :    https://github.com/anza-xyz/agave/blob/v2.0.6/programs/bpf_loader/src/syscalls/mod.rs#L601
      30             : 
      31             :    As of v0.2.6, the only two usages are in syscall panic and syscall log. */
      32           9 : #define FD_TRANSLATE_STRING( vm, vaddr, msg_sz ) (__extension__({                          \
      33           9 :     char const * msg = FD_VM_MEM_SLICE_HADDR_LD( vm, vaddr, FD_VM_ALIGN_RUST_U8, msg_sz ); \
      34           9 :     if( FD_UNLIKELY( !fd_utf8_verify( msg, msg_sz ) ) ) {                                  \
      35           0 :       FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_STRING );                   \
      36           0 :       return FD_VM_SYSCALL_ERR_INVALID_STRING;                                             \
      37           0 :     }                                                                                      \
      38           9 :     msg;                                                                                   \
      39           9 : }))
      40             : 
      41             : int
      42             : fd_vm_syscall_sol_panic( /**/            void *  _vm,
      43             :                          /**/            ulong   file_vaddr,
      44             :                          /**/            ulong   file_sz,
      45             :                          /**/            ulong   line,
      46             :                          /**/            ulong   column,
      47             :                          FD_PARAM_UNUSED ulong   r5,
      48           0 :                          FD_PARAM_UNUSED ulong * _ret ) {
      49           0 :   fd_vm_t * vm = (fd_vm_t *)_vm;
      50             : 
      51             :   /* https://github.com/anza-xyz/agave/blob/v2.0.6/programs/bpf_loader/src/syscalls/mod.rs#L637
      52             : 
      53             :      Note: this syscall is not used by the Rust SDK, only by the C SDK.
      54             :      Rust transforms `panic!()` into a log, followed by an abort.
      55             :      It's unclear if this syscall actually makes any sense... */
      56           0 :   FD_VM_CU_UPDATE( vm, file_sz );
      57             : 
      58             :   /* Validate string */
      59           0 :   FD_TRANSLATE_STRING( vm, file_vaddr, file_sz );
      60             : 
      61             :   /* Note: we truncate the log, ignoring file, line, column.
      62             :      As mentioned above, it's unclear if anyone is even using this syscall,
      63             :      so dealing with the complexity of Agave's log is a waste of time. */
      64           0 :   (void)line;
      65           0 :   (void)column;
      66             : 
      67           0 :   FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_PANIC );
      68           0 :   return FD_VM_SYSCALL_ERR_PANIC;
      69           0 : }
      70             : 
      71             : int
      72             : fd_vm_syscall_sol_log( /**/            void *  _vm,
      73             :                        /**/            ulong   msg_vaddr,
      74             :                        /**/            ulong   msg_sz,
      75             :                        FD_PARAM_UNUSED ulong   r3,
      76             :                        FD_PARAM_UNUSED ulong   r4,
      77             :                        FD_PARAM_UNUSED ulong   r5,
      78           9 :                        /**/            ulong * _ret ) {
      79           9 :   fd_vm_t * vm = (fd_vm_t *)_vm;
      80             : 
      81             :   /* https://github.com/anza-xyz/agave/blob/v2.0.6/programs/bpf_loader/src/syscalls/logging.rs#L5 */
      82             : 
      83           9 :   FD_VM_CU_UPDATE( vm, fd_ulong_max( msg_sz, FD_VM_SYSCALL_BASE_COST ) );
      84             : 
      85             :   /* Note: when msg_sz==0, msg can be undefined. fd_log_collector_program_log() handles it.
      86             :      FIXME: Macro invocation in function invocation? */
      87           9 :   fd_log_collector_program_log( vm->instr_ctx, FD_TRANSLATE_STRING( vm, msg_vaddr, msg_sz ), msg_sz );
      88             : 
      89           0 :   *_ret = 0UL;
      90           9 :   return FD_VM_SUCCESS;
      91          18 : }
      92             : 
      93             : int
      94             : fd_vm_syscall_sol_log_64( void *  _vm,
      95             :                           ulong   r1,
      96             :                           ulong   r2,
      97             :                           ulong   r3,
      98             :                           ulong   r4,
      99             :                           ulong   r5,
     100           3 :                           ulong * _ret ) {
     101           3 :   fd_vm_t * vm = (fd_vm_t *)_vm;
     102             : 
     103             :   /* https://github.com/anza-xyz/agave/blob/v2.0.6/programs/bpf_loader/src/syscalls/logging.rs#L37 */
     104             : 
     105           3 :   FD_VM_CU_UPDATE( vm, FD_VM_LOG_64_UNITS );
     106             : 
     107             :   /* Max msg_sz: 46 - 15 + 16*5 = 111 < 127 => we can use printf */
     108           0 :   fd_log_collector_printf_dangerous_max_127( vm->instr_ctx,
     109           3 :     "Program log: 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx", r1, r2, r3, r4, r5 );
     110             : 
     111           3 :   *_ret = 0UL;
     112           3 :   return FD_VM_SUCCESS;
     113           3 : }
     114             : 
     115             : int
     116             : fd_vm_syscall_sol_log_compute_units( /**/            void *  _vm,
     117             :                                      FD_PARAM_UNUSED ulong   r1,
     118             :                                      FD_PARAM_UNUSED ulong   r2,
     119             :                                      FD_PARAM_UNUSED ulong   r3,
     120             :                                      FD_PARAM_UNUSED ulong   r4,
     121             :                                      FD_PARAM_UNUSED ulong   r5,
     122           0 :                                      /**/            ulong * _ret ) {
     123           0 :   fd_vm_t * vm = (fd_vm_t *)_vm;
     124             : 
     125             :   /* https://github.com/anza-xyz/agave/blob/v2.0.6/programs/bpf_loader/src/syscalls/logging.rs#L60 */
     126             : 
     127           0 :   FD_VM_CU_UPDATE( vm, FD_VM_SYSCALL_BASE_COST );
     128             : 
     129             :   /* Max msg_sz: 40 - 3 + 20 = 57 < 127 => we can use printf */
     130           0 :   fd_log_collector_printf_dangerous_max_127( vm->instr_ctx,
     131           0 :     "Program consumption: %lu units remaining", vm->cu );
     132             : 
     133           0 :   *_ret = 0UL;
     134           0 :   return FD_VM_SUCCESS;
     135           0 : }
     136             : 
     137             : int
     138             : fd_vm_syscall_sol_log_pubkey( /**/            void *  _vm,
     139             :                               /**/            ulong   pubkey_vaddr,
     140             :                               FD_PARAM_UNUSED ulong   r2,
     141             :                               FD_PARAM_UNUSED ulong   r3,
     142             :                               FD_PARAM_UNUSED ulong   r4,
     143             :                               FD_PARAM_UNUSED ulong   r5,
     144           0 :                               /**/            ulong * _ret ) {
     145           0 :   fd_vm_t * vm = (fd_vm_t *)_vm;
     146             : 
     147             :   /* https://github.com/anza-xyz/agave/blob/v2.0.6/programs/bpf_loader/src/syscalls/logging.rs#L84 */
     148             : 
     149           0 :   FD_VM_CU_UPDATE( vm, FD_VM_LOG_PUBKEY_UNITS );
     150             : 
     151           0 :   void const * pubkey = FD_VM_MEM_HADDR_LD( vm, pubkey_vaddr, FD_VM_ALIGN_RUST_PUBKEY, sizeof(fd_pubkey_t) );
     152             : 
     153           0 :   char msg[ FD_BASE58_ENCODED_32_SZ ]; ulong msg_sz;
     154           0 :   if( FD_UNLIKELY( fd_base58_encode_32( pubkey, &msg_sz, msg )==NULL ) ) {
     155           0 :     return FD_VM_SYSCALL_ERR_INVALID_STRING;
     156           0 :   }
     157             : 
     158           0 :   fd_log_collector_program_log( vm->instr_ctx, msg, msg_sz );
     159             : 
     160           0 :   *_ret = 0UL;
     161           0 :   return FD_VM_SUCCESS;
     162           0 : }
     163             : 
     164             : int
     165             : fd_vm_syscall_sol_log_data( /**/            void *  _vm,
     166             :                             /**/            ulong   slice_vaddr,
     167             :                             /**/            ulong   slice_cnt,
     168             :                             FD_PARAM_UNUSED ulong   r3,
     169             :                             FD_PARAM_UNUSED ulong   r4,
     170             :                             FD_PARAM_UNUSED ulong   r5,
     171           3 :                             /**/            ulong * _ret ) {
     172           3 :   fd_vm_t * vm = (fd_vm_t *)_vm;
     173             : 
     174             :   /* https://github.com/anza-xyz/agave/blob/v2.0.6/programs/bpf_loader/src/syscalls/logging.rs#L109
     175             : 
     176             :      Note: this is implemented following Agave's perverse behavior.
     177             :      We need to loop the slice multiple times to match the exact error,
     178             :      first compute budget, then memory mapping.
     179             :      And finally we can loop to log. */
     180             : 
     181             :   /* https://github.com/anza-xyz/agave/blob/v2.0.6/programs/bpf_loader/src/syscalls/logging.rs#L121 */
     182             : 
     183           3 :   FD_VM_CU_UPDATE( vm, FD_VM_SYSCALL_BASE_COST );
     184             : 
     185             :   /* https://github.com/anza-xyz/agave/blob/v2.0.6/programs/bpf_loader/src/syscalls/logging.rs#L123-L128 */
     186             : 
     187           6 :   fd_vm_vec_t const * slice = (fd_vm_vec_t const *)FD_VM_MEM_SLICE_HADDR_LD( vm, slice_vaddr, FD_VM_ALIGN_RUST_SLICE_U8_REF,
     188           6 :     fd_ulong_sat_mul( slice_cnt, sizeof(fd_vm_vec_t) ) );
     189             : 
     190             :   /* https://github.com/anza-xyz/agave/blob/v2.0.6/programs/bpf_loader/src/syscalls/logging.rs#L130-L135 */
     191             : 
     192           3 :   FD_VM_CU_UPDATE( vm, fd_ulong_sat_mul( FD_VM_SYSCALL_BASE_COST, slice_cnt ) );
     193             : 
     194             :   /* https://github.com/anza-xyz/agave/blob/v2.0.6/programs/bpf_loader/src/syscalls/logging.rs#L136-L141 */
     195             : 
     196          18 :   for( ulong i=0UL; i<slice_cnt; i++ ) {
     197          15 :     FD_VM_CU_UPDATE( vm, slice[i].len );
     198          15 :   }
     199             : 
     200             :   /* https://github.com/anza-xyz/agave/blob/v2.0.6/programs/bpf_loader/src/syscalls/logging.rs#L145-L152 */
     201             : 
     202           3 :   ulong msg_sz = 14UL; /* "Program data: ", with space */
     203          18 :   for( ulong i=0UL; i<slice_cnt; i++ ) {
     204          15 :     ulong cur_len = slice[i].len;
     205             :     /* This fails the syscall in case of memory mapping issues */
     206          30 :     FD_VM_MEM_SLICE_HADDR_LD( vm, slice[i].addr, FD_VM_ALIGN_RUST_U8, cur_len );
     207             :     /* Every buffer will be base64 encoded + space separated */
     208           0 :     msg_sz += (slice[i].len + 2)/3*4 + (i > 0);
     209          30 :   }
     210             : 
     211             :   /* https://github.com/anza-xyz/agave/blob/v2.0.6/programs/bpf_loader/src/syscalls/logging.rs#L156 */
     212             : 
     213           3 :   char msg[ FD_LOG_COLLECTOR_MAX ];
     214           3 :   ulong bytes_written = fd_log_collector_check_and_truncate( &vm->instr_ctx->txn_ctx->log_collector, msg_sz );
     215           3 :   if( FD_LIKELY( bytes_written < ULONG_MAX ) ) {
     216           3 :     fd_memcpy( msg, "Program data: ", 14 );
     217           3 :     char * buf = msg + 14;
     218             : 
     219          18 :     for( ulong i=0UL; i<slice_cnt; i++ ) {
     220          15 :       ulong cur_len = slice[i].len;
     221          30 :       void const * bytes = FD_VM_MEM_SLICE_HADDR_LD( vm, slice[i].addr, FD_VM_ALIGN_RUST_U8, cur_len );
     222             : 
     223          15 :       if( i ) { *buf = ' '; ++buf; } /* skip first */
     224          30 :       buf += fd_base64_encode( buf, bytes, cur_len );
     225          30 :     }
     226           3 :     FD_TEST( (ulong)(buf-msg)==msg_sz );
     227             : 
     228           3 :     fd_log_collector_msg( vm->instr_ctx, msg, msg_sz );
     229           3 :   }
     230             : 
     231           3 :   *_ret = 0;
     232           3 :   return FD_VM_SUCCESS;
     233           3 : }
     234             : 
     235             : int
     236             : fd_vm_syscall_sol_alloc_free( /**/            void *  _vm,
     237             :                               /**/            ulong   sz,
     238             :                               /**/            ulong   free_vaddr,
     239             :                               FD_PARAM_UNUSED ulong   r3,
     240             :                               FD_PARAM_UNUSED ulong   r4,
     241             :                               FD_PARAM_UNUSED ulong   r5,
     242           0 :                               /**/            ulong * _ret ) {
     243           0 :   fd_vm_t * vm = (fd_vm_t *)_vm;
     244             : 
     245             :   /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mod.rs#L666 */
     246             : 
     247             :   /* This syscall is ... uh ... problematic.  But the community has
     248             :      already recognized this and deprecated it:
     249             : 
     250             :      https://github.com/solana-labs/solana/blob/v1.17.23/sdk/src/feature_set.rs#L846
     251             : 
     252             :      Unfortunately, old code never dies so, practically, this will need
     253             :      to be supported until the heat death of the universe.
     254             : 
     255             :      The most serious issue is that there is nothing to stop VM code
     256             :      making a decision based on the _location_ of the returned
     257             :      allocation.  If different validator implementations use different
     258             :      allocator algorithms, though each implementation would behave
     259             :      functionally correct in isolation, the VM code that uses it would
     260             :      actually break consensus.
     261             : 
     262             :      As a result, every validator needs to use a bit-for-bit identical
     263             :      allocation algorithm.  Fortunately, Solana is just using a basic
     264             :      bump allocator:
     265             : 
     266             :      https://github.com/solana-labs/solana/blob/v1.17.23/program-runtime/src/invoke_context.rs#L122-L148
     267             : 
     268             :      vm->heap_{sz,max} and the below replicate this exactly.
     269             : 
     270             :      Another major issue is that this alloc doesn't always conform
     271             :      typical malloc/free semantics (e.g. C/C++ requires malloc to have
     272             :      an alignment safe for primitive types ... 8 for the Solana machine
     273             :      model).  This is clearly to support backward compat with older VM
     274             :      code (though ideally a malloc syscall should have behaved like ...
     275             :      well ... malloc from day 1).  So the alignment behavior below is a
     276             :      bug-for-bug replication of that:
     277             : 
     278             :      https://github.com/solana-labs/solana/blob/v1.17.23/programs/bpf_loader/src/syscalls/mod.rs#L645-L681
     279             :      https://github.com/solana-labs/solana/blob/v1.17.23/sdk/program/src/entrypoint.rs#L265-L266
     280             : 
     281             :      More generally and already ranted about elsewhere, any code that
     282             :      uses malloc/free style dynamic allocation is inherently broken.  So
     283             :      this syscall should have never existed in the first place ... it
     284             :      just feeds the trolls.  The above is just additional implementation
     285             :      horror because people consistent think malloc/free is much simpler
     286             :      than it actually is.  This is also an example of how quickly
     287             :      mistakes fossilize and become a thorn-in-the-side forever.
     288             : 
     289             :      IMPORTANT SAFETY TIP!  heap_start must be non zero and both
     290             :      heap_start and heap_end should have an alignment of at least 8.
     291             :      This existing runtime policies around heap implicitly satisfy this.
     292             : 
     293             :      IMPORTANT SAFETY TIP!  The specification for Rust's align_offset
     294             :      doesn't seem to provide a strong guarantee that it will return the
     295             :      minimal positive offset necessary to align pointers.  It is
     296             :      possible for a "conforming" Rust compiler to break consensus by
     297             :      using a different align_offset implementation that aligned pointer
     298             :      between different compilations of the Solana validator and the
     299             :      below. */
     300             : 
     301             :   /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mod.rs#L676-L680 */
     302             : 
     303           0 :   ulong align = fd_vm_is_check_align_enabled( vm ) ? 8UL : FD_VM_ALIGN_RUST_U8;
     304             : 
     305             :   /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mod.rs#L681-L683
     306             :      Nothing to do. This section can't error, see:
     307             :      https://doc.rust-lang.org/1.81.0/src/core/alloc/layout.rs.html#70
     308             :      https://doc.rust-lang.org/1.81.0/src/core/alloc/layout.rs.html#100 */
     309             : 
     310             : 
     311             :   /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mod.rs#L684
     312             :      Nothing to do.
     313             :      TODO: unclear if it throw InstructionError::CallDepth
     314             :      https://github.com/anza-xyz/agave/blob/v2.0.8/program-runtime/src/invoke_context.rs#L662 */
     315             : 
     316             :   /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mod.rs#L685-L693 */
     317             : 
     318             :   /* Non-zero free address implies that this is a free() call.  Since
     319             :      this is a bump allocator, free is a no-op. */
     320           0 :   if( FD_UNLIKELY( free_vaddr ) ) {
     321           0 :     *_ret = 0UL;
     322           0 :     return FD_VM_SUCCESS;
     323           0 :   }
     324             : 
     325             : 
     326           0 :   ulong heap_sz    = fd_ulong_align_up( vm->heap_sz, align                           );
     327           0 :   ulong heap_vaddr = fd_ulong_sat_add ( heap_sz,     FD_VM_MEM_MAP_HEAP_REGION_START );
     328           0 :   /**/  heap_sz    = fd_ulong_sat_add ( heap_sz,     sz                              );
     329             : 
     330           0 :   if( FD_UNLIKELY( heap_sz > vm->heap_max ) ) { /* Not enough free memory */
     331           0 :     *_ret = 0UL;
     332           0 :     return FD_VM_SUCCESS;
     333           0 :   }
     334             : 
     335           0 :   vm->heap_sz = heap_sz;
     336             : 
     337           0 :   *_ret = heap_vaddr;
     338           0 :   return FD_VM_SUCCESS;
     339           0 : }
     340             : 
     341             : /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mem_ops.rs#L145 */
     342             : int
     343             : fd_vm_memmove( fd_vm_t * vm,
     344             :                ulong     dst_vaddr,
     345             :                ulong     src_vaddr,
     346          66 :                ulong     sz ) {
     347          66 :   if( FD_UNLIKELY( !sz ) ) {
     348           0 :     return FD_VM_SUCCESS;
     349           0 :   }
     350             : 
     351          66 :   if( !vm->direct_mapping ) {
     352             :     /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mem_ops.rs#L188-L192 */
     353          24 :     fd_vm_haddr_query_t dst_ref_mut_query = {
     354          24 :       .vaddr    = dst_vaddr,
     355          24 :       .align    = FD_VM_ALIGN_RUST_U8,
     356          24 :       .sz       = sz,
     357          24 :       .is_slice = 1,
     358          24 :     };
     359             : 
     360          24 :     fd_vm_haddr_query_t * queries[] = { &dst_ref_mut_query };
     361          24 :     FD_VM_TRANSLATE_MUT( vm, queries );
     362             : 
     363          48 :     void const * src = FD_VM_MEM_HADDR_LD( vm, src_vaddr, FD_VM_ALIGN_RUST_U8, sz );
     364           0 :     memmove( dst_ref_mut_query.haddr, src, sz );
     365          48 :   } else {
     366             :     /* If the src and dst vaddrs overlap and src_vaddr < dst_vaddr, Agave iterates through input regions backwards
     367             :        to maintain correct memmove behavior for overlapping cases. Although this logic should only apply to the src and dst
     368             :        vaddrs being in the input data region (since that is the only possible case you could have overlapping, chunked-up memmoves),
     369             :        Agave will iterate backwards in ANY region. If it eventually reaches the end of a region after iterating backwards and
     370             :        hits an access violation, the bytes from [region_begin, start_vaddr] will still be written to, causing fuzzing mismatches.
     371             :        In this case, if we didn't have the reverse flag, we would have thrown an access violation before any bytes were copied.
     372             :        The same logic applies to memmoves that go past the high end of a region - reverse iteration logic would throw an access
     373             :        violation before any bytes were copied, while the current logic would copy the bytes until the end of the region.
     374             :        https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mem_ops.rs#L184 */
     375          42 :     uchar reverse = !!( dst_vaddr >= src_vaddr && dst_vaddr - src_vaddr < sz );
     376             : 
     377             :     /* In reverse calculations, start from the rightmost vaddr that will be accessed (note the - 1). */
     378          42 :     ulong dst_vaddr_begin = reverse ? fd_ulong_sat_add( dst_vaddr, sz - 1UL ) : dst_vaddr;
     379          42 :     ulong src_vaddr_begin = reverse ? fd_ulong_sat_add( src_vaddr, sz - 1UL ) : src_vaddr;
     380             : 
     381             :     /* Find the correct src and dst haddrs to start operating from. If the src or dst vaddrs
     382             :        belong to the input data region (4), keep track of region statistics to memmove in chunks. */
     383          42 :     ulong   dst_region                  = FD_VADDR_TO_REGION( dst_vaddr_begin );
     384          42 :     uchar   dst_is_input_mem_region     = ( dst_region==FD_VM_INPUT_REGION );
     385          42 :     ulong   dst_offset                  = dst_vaddr_begin & FD_VM_OFFSET_MASK;
     386          42 :     ulong   dst_region_idx              = 0UL;
     387          42 :     ulong   dst_bytes_rem_in_cur_region;
     388          42 :     uchar * dst_haddr;
     389          42 :     if( dst_is_input_mem_region ) {
     390          18 :       FD_VM_MEM_HADDR_AND_REGION_IDX_FROM_INPUT_REGION_CHECKED( vm, dst_offset, dst_region_idx, dst_haddr );
     391          18 :       if( FD_UNLIKELY( !vm->input_mem_regions[ dst_region_idx ].is_writable ) ) {
     392           0 :         FD_VM_ERR_FOR_LOG_EBPF( vm, FD_VM_ERR_EBPF_ACCESS_VIOLATION );
     393           0 :         return FD_VM_SYSCALL_ERR_SEGFAULT;
     394           0 :       }
     395          18 :       if( FD_UNLIKELY( reverse ) ) {
     396             :         /* Bytes remaining between region begin and current position (+ 1 for inclusive region beginning). */
     397           6 :         dst_bytes_rem_in_cur_region = fd_ulong_sat_sub( dst_offset + 1UL, vm->input_mem_regions[ dst_region_idx ].vaddr_offset );
     398          12 :       } else {
     399             :         /* Bytes remaining between current position and region end. */
     400          12 :         dst_bytes_rem_in_cur_region = fd_ulong_sat_sub( vm->input_mem_regions[ dst_region_idx ].region_sz, ( dst_offset - vm->input_mem_regions[ dst_region_idx ].vaddr_offset ) );
     401          12 :       }
     402          24 :     } else {
     403          24 :       dst_haddr = (uchar*)FD_VM_MEM_HADDR_ST_NO_SZ_CHECK( vm, dst_vaddr_begin, FD_VM_ALIGN_RUST_U8 );
     404             : 
     405          18 :       if( FD_UNLIKELY( reverse ) ) {
     406             :         /* Bytes remaining is minimum of the offset from the beginning of the current
     407             :            region (+1 for inclusive region beginning) and the number of storable bytes in the region. */
     408           3 :         dst_bytes_rem_in_cur_region = fd_ulong_min( vm->region_st_sz[ dst_region ], dst_offset + 1UL );
     409             : 
     410          15 :       } else {
     411             :         /* Bytes remaining is the number of writable bytes left in the region */
     412          15 :         dst_bytes_rem_in_cur_region = fd_ulong_sat_sub( vm->region_st_sz[ dst_region ], dst_offset );
     413          15 :       }
     414          18 :     }
     415             : 
     416             :     /* Logic for src vaddr translation is similar to above excluding any writable checks. */
     417          36 :     ulong   src_region                  = FD_VADDR_TO_REGION( src_vaddr_begin );
     418          36 :     uchar   src_is_input_mem_region     = ( src_region==FD_VM_INPUT_REGION );
     419          36 :     ulong   src_offset                  = src_vaddr_begin & FD_VM_OFFSET_MASK;
     420          36 :     ulong   src_region_idx              = 0UL;
     421          36 :     ulong   src_bytes_rem_in_cur_region;
     422          36 :     uchar * src_haddr;
     423          36 :     if( src_is_input_mem_region ) {
     424          18 :       FD_VM_MEM_HADDR_AND_REGION_IDX_FROM_INPUT_REGION_CHECKED( vm, src_offset, src_region_idx, src_haddr );
     425          18 :       if( FD_UNLIKELY( reverse ) ) {
     426           6 :         src_bytes_rem_in_cur_region = fd_ulong_sat_sub( src_offset + 1UL, vm->input_mem_regions[ src_region_idx ].vaddr_offset );
     427          12 :       } else {
     428          12 :         src_bytes_rem_in_cur_region = fd_ulong_sat_sub( vm->input_mem_regions[ src_region_idx ].region_sz, ( src_offset - vm->input_mem_regions[ src_region_idx ].vaddr_offset ) );
     429          12 :       }
     430          18 :     } else {
     431          54 :       src_haddr = (uchar*)FD_VM_MEM_HADDR_LD_NO_SZ_CHECK( vm, src_vaddr_begin, FD_VM_ALIGN_RUST_U8 );
     432             : 
     433          18 :       if( FD_UNLIKELY( reverse ) ) {
     434           3 :         src_bytes_rem_in_cur_region = fd_ulong_min( vm->region_ld_sz[ src_region ], src_offset + 1UL );
     435             : 
     436          15 :       } else {
     437          15 :         src_bytes_rem_in_cur_region = fd_ulong_sat_sub( vm->region_ld_sz[ src_region ], src_offset );
     438          15 :       }
     439          54 :     }
     440             : 
     441             :     /* Short circuit: if the number of copyable bytes stays within all memory regions,
     442             :        just memmove and return. This is a majority case in mainnet, devnet, and testnet.
     443             :        Someone would have to be very crafty and clever to construct a transaction that
     444             :        deploys and invokes a custom program that does not fall into this branch. */
     445          36 :     if( FD_LIKELY( sz<=dst_bytes_rem_in_cur_region && sz<=src_bytes_rem_in_cur_region ) ) {
     446          21 :       if( FD_UNLIKELY( reverse ) ) {
     447             :         /* In the reverse iteration case, the haddrs point to the end of the region here. Since the
     448             :            above checks guarantee that there are enough bytes left in the src and dst regions to do
     449             :            a direct memmove, we can just subtract (sz-1) from the haddrs, memmove, and return. */
     450           3 :         memmove( dst_haddr - sz + 1UL, src_haddr - sz + 1UL, sz );
     451          18 :       } else {
     452             :         /* In normal iteration, the haddrs correspond to the correct starting point for the memcpy,
     453             :            so no further translation has to be done. */
     454          18 :         memmove( dst_haddr, src_haddr, sz );
     455          18 :       }
     456          21 :       return FD_VM_SUCCESS;
     457          21 :     }
     458             : 
     459             :     /* Copy over the bytes from each region in chunks. */
     460          57 :     while( sz>0UL ) {
     461             :       /* End of region case */
     462          45 :       if( FD_UNLIKELY( src_bytes_rem_in_cur_region==0UL ) ) {
     463             :         /* Same as above, except no writable checks. */
     464          30 :         if( FD_LIKELY( !reverse &&
     465          30 :                         src_is_input_mem_region &&
     466          30 :                         src_region_idx+1UL<vm->input_mem_regions_cnt ) ) {
     467          12 :           if( FD_UNLIKELY( vm->input_mem_regions[ src_region_idx+1UL ].is_acct_data != vm->input_mem_regions[ src_region_idx ].is_acct_data ) ) {
     468           0 :             FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );
     469           0 :             return FD_VM_SYSCALL_ERR_SEGFAULT;
     470           0 :           }
     471          12 :           src_region_idx++;
     472          12 :           src_haddr = (uchar*)vm->input_mem_regions[ src_region_idx ].haddr;
     473          18 :         } else if( FD_LIKELY( reverse && src_region_idx>0UL ) ) {
     474          15 :           if( FD_UNLIKELY( vm->input_mem_regions[ src_region_idx-1UL ].is_acct_data != vm->input_mem_regions[ src_region_idx ].is_acct_data ) ) {
     475           0 :             FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );
     476           0 :             return FD_VM_SYSCALL_ERR_SEGFAULT;
     477           0 :           }
     478          15 :           src_region_idx--;
     479          15 :           src_haddr = (uchar*)vm->input_mem_regions[ src_region_idx ].haddr + vm->input_mem_regions[ src_region_idx ].region_sz - 1UL;
     480          15 :         } else {
     481           3 :           FD_VM_ERR_FOR_LOG_EBPF( vm, FD_VM_ERR_EBPF_ACCESS_VIOLATION );
     482           3 :           return FD_VM_SYSCALL_ERR_SEGFAULT;
     483           3 :         }
     484          27 :         src_bytes_rem_in_cur_region = vm->input_mem_regions[ src_region_idx ].region_sz;
     485          27 :       }
     486          42 :       if( FD_UNLIKELY( dst_bytes_rem_in_cur_region==0UL ) ) {
     487             :         /* Only proceed if:
     488             :             - We are in the input memory region
     489             :             - There are remaining input memory regions to copy from (for both regular and reverse iteration orders)
     490             :             - The next input memory region is writable
     491             :            Fail otherwise. */
     492           9 :         if( FD_LIKELY( !reverse &&
     493           9 :                         dst_is_input_mem_region &&
     494           9 :                         dst_region_idx+1UL<vm->input_mem_regions_cnt &&
     495           9 :                         vm->input_mem_regions[ dst_region_idx+1UL ].is_writable ) ) {
     496           6 :           if( FD_UNLIKELY( vm->input_mem_regions[ dst_region_idx+1UL ].is_acct_data != vm->input_mem_regions[ dst_region_idx ].is_acct_data ) ) {
     497           0 :             FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );
     498           0 :             return FD_VM_SYSCALL_ERR_SEGFAULT;
     499           0 :           }
     500             :           /* In normal iteration, we move the haddr to the beginning of the next region. */
     501           6 :           dst_region_idx++;
     502           6 :           dst_haddr = (uchar*)vm->input_mem_regions[ dst_region_idx ].haddr;
     503           6 :         } else if( FD_LIKELY( reverse &&
     504           3 :                               dst_region_idx>0UL &&
     505           3 :                               vm->input_mem_regions[ dst_region_idx-1UL ].is_writable ) ) {
     506           3 :           if( FD_UNLIKELY( vm->input_mem_regions[ dst_region_idx-1UL ].is_acct_data != vm->input_mem_regions[ dst_region_idx ].is_acct_data ) ) {
     507           0 :             FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );
     508           0 :             return FD_VM_SYSCALL_ERR_SEGFAULT;
     509           0 :           }
     510             :           /* Note that when reverse iterating, we set the haddr to the END of the PREVIOUS region. */
     511           3 :           dst_region_idx--;
     512           3 :           dst_haddr = (uchar*)vm->input_mem_regions[ dst_region_idx ].haddr + vm->input_mem_regions[ dst_region_idx ].region_sz - 1UL;
     513           3 :         } else {
     514           0 :           FD_VM_ERR_FOR_LOG_EBPF( vm, FD_VM_ERR_EBPF_ACCESS_VIOLATION );
     515           0 :           return FD_VM_SYSCALL_ERR_SEGFAULT;
     516           0 :         }
     517           9 :         dst_bytes_rem_in_cur_region = vm->input_mem_regions[ dst_region_idx ].region_sz;
     518           9 :       }
     519             : 
     520             :       /* Number of bytes to operate on in this iteration is the min of:
     521             :          - number of bytes left to copy
     522             :          - bytes left in the current src region
     523             :          - bytes left in the current dst region */
     524          42 :       ulong num_bytes_to_copy = fd_ulong_min( sz, fd_ulong_min( src_bytes_rem_in_cur_region, dst_bytes_rem_in_cur_region ) );
     525          42 :       if( FD_UNLIKELY( reverse ) ) {
     526          21 :         memmove( dst_haddr - num_bytes_to_copy + 1UL, src_haddr - num_bytes_to_copy + 1UL, num_bytes_to_copy );
     527          21 :         dst_haddr -= num_bytes_to_copy;
     528          21 :         src_haddr -= num_bytes_to_copy;
     529          21 :       } else {
     530          21 :         memmove( dst_haddr, src_haddr, num_bytes_to_copy );
     531          21 :         dst_haddr += num_bytes_to_copy;
     532          21 :         src_haddr += num_bytes_to_copy;
     533          21 :       }
     534             : 
     535             :       /* Update size trackers */
     536          42 :       sz                          -= num_bytes_to_copy;
     537          42 :       src_bytes_rem_in_cur_region -= num_bytes_to_copy;
     538          42 :       dst_bytes_rem_in_cur_region -= num_bytes_to_copy;
     539          42 :     }
     540          15 :   }
     541             : 
     542          27 :   return FD_VM_SUCCESS;
     543          66 : }
     544             : 
     545             : /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mem_ops.rs#L41 */
     546             : int
     547             : fd_vm_syscall_sol_memmove( /**/            void *  _vm,
     548             :                            /**/            ulong   dst_vaddr,
     549             :                            /**/            ulong   src_vaddr,
     550             :                            /**/            ulong   sz,
     551             :                            FD_PARAM_UNUSED ulong   r4,
     552             :                            FD_PARAM_UNUSED ulong   r5,
     553          36 :                            /**/            ulong * _ret ) {
     554          36 :   *_ret = 0;
     555          36 :   fd_vm_t * vm = (fd_vm_t *)_vm;
     556             : 
     557          36 :   FD_VM_CU_MEM_OP_UPDATE( vm, sz );
     558             : 
     559             :   /* No overlap check for memmove. */
     560           0 :   return fd_vm_memmove( vm, dst_vaddr, src_vaddr, sz );
     561          36 : }
     562             : 
     563             : /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mem_ops.rs#L18 */
     564             : int
     565             : fd_vm_syscall_sol_memcpy( /**/            void *  _vm,
     566             :                           /**/            ulong   dst_vaddr,
     567             :                           /**/            ulong   src_vaddr,
     568             :                           /**/            ulong   sz,
     569             :                           FD_PARAM_UNUSED ulong   r4,
     570             :                           FD_PARAM_UNUSED ulong   r5,
     571          48 :                           /**/            ulong * _ret ) {
     572          48 :   *_ret = 0;
     573          48 :   fd_vm_t * vm = (fd_vm_t *)_vm;
     574             : 
     575          48 :   FD_VM_CU_MEM_OP_UPDATE( vm, sz );
     576             : 
     577             :   /* Exact same as memmove, except also check overlap.
     578             :      https://github.com/anza-xyz/agave/blob/v2.2.17/programs/bpf_loader/src/syscalls/mem_ops.rs#L45 */
     579          48 :   FD_VM_MEM_CHECK_NON_OVERLAPPING( vm, src_vaddr, sz, dst_vaddr, sz );
     580             : 
     581          30 :   return fd_vm_memmove( vm, dst_vaddr, src_vaddr, sz );
     582          48 : }
     583             : 
     584             : int
     585             : fd_vm_syscall_sol_memcmp( /**/            void *  _vm,
     586             :                           /**/            ulong   m0_vaddr,
     587             :                           /**/            ulong   m1_vaddr,
     588             :                           /**/            ulong   sz,
     589             :                           /**/            ulong   out_vaddr,
     590             :                           FD_PARAM_UNUSED ulong   r5,
     591          15 :                           /**/            ulong * _ret ) {
     592          15 :   *_ret = 0;
     593          15 :   fd_vm_t * vm = (fd_vm_t *)_vm;
     594             : 
     595             :   /* https://github.com/anza-xyz/agave/blob/v2.2.17/programs/bpf_loader/src/syscalls/mem_ops.rs#L84 */
     596             : 
     597          15 :   FD_VM_CU_MEM_OP_UPDATE( vm, sz );
     598             : 
     599             :   /* Note: though this behaves like a normal C-style memcmp, we can't
     600             :      use the compilers / libc memcmp directly because the specification
     601             :      doesn't provide strong enough guarantees about the return value (it
     602             :      only promises the sign). */
     603             : 
     604          15 :   if( !vm->direct_mapping ) {
     605           0 :     uchar const * m0 = (uchar const *)FD_VM_MEM_SLICE_HADDR_LD( vm, m0_vaddr, FD_VM_ALIGN_RUST_U8, sz );
     606           0 :     uchar const * m1 = (uchar const *)FD_VM_MEM_SLICE_HADDR_LD( vm, m1_vaddr, FD_VM_ALIGN_RUST_U8, sz );
     607             : 
     608             :     /* Silly that this doesn't use r0 to return ... slower, more edge
     609             :       case, different from libc style memcmp, harder to callers to use,
     610             :       etc ... probably too late to do anything about it now ... sigh */
     611             : 
     612             :     /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mem_ops.rs#L121-L125 */
     613           0 :     fd_vm_haddr_query_t cmp_result_ref_mut_query = {
     614           0 :       .vaddr    = out_vaddr,
     615           0 :       .align    = FD_VM_ALIGN_RUST_I32,
     616           0 :       .sz       = 4UL,
     617           0 :       .is_slice = 0,
     618           0 :     };
     619             : 
     620           0 :     fd_vm_haddr_query_t * queries[] = { &cmp_result_ref_mut_query };
     621           0 :     FD_VM_TRANSLATE_MUT( vm, queries );
     622             : 
     623           0 :     int out = 0;
     624           0 :     for( ulong i=0UL; i<sz; i++ ) {
     625           0 :       int i0 = (int)m0[i];
     626           0 :       int i1 = (int)m1[i];
     627           0 :       if( i0!=i1 ) {
     628           0 :         out = i0 - i1;
     629           0 :         break;
     630           0 :       }
     631           0 :     }
     632             : 
     633           0 :     fd_memcpy( cmp_result_ref_mut_query.haddr, &out, 4UL ); /* Sigh ... see note above (and might be unaligned ... double sigh) */
     634             : 
     635           0 :     return FD_VM_SUCCESS;
     636          15 :   } else {
     637             :     /* In the case that direct mapping is enabled, the behavior for memcmps
     638             :        differ significantly from the non-dm case. The key difference is that
     639             :        invalid loads will instantly lead to errors in the non-dm case. However,
     640             :        when direct mapping is enabled, we will first try to memcmp the largest
     641             :        size valid chunk first, and will exit successfully if a difference is
     642             :        found without aborting from the VM. A chunk is defined as the largest
     643             :        valid vaddr range in both memory regions that doesn't span multiple
     644             :        regions.
     645             : 
     646             :        Example:
     647             :        fd_vm_syscall_sol_memcmp( vm, m0_addr : 0x4000, m1_vaddr : 0x2000, 0x200, ... );
     648             :        m0's region: m0_addr 0x4000 -> 0x4000 + 0x50  (region sz 0x50)
     649             :        m1's region: m1_addr 0x2000 -> 0x2000 + 0x100 (region sz 0x100)
     650             :        sz: 0x200
     651             : 
     652             :        Case 1: 0x4000 -> 0x4050 does     have the same bytes as 0x2000 -> 0x2050
     653             :        Case 2: 0x4000 -> 0x4050 does NOT have the same bytes as 0x2000 -> 0x2050
     654             : 
     655             :        Pre-DM:
     656             :        This will fail out before any bytes are compared because the memory
     657             :        translation is done first.
     658             : 
     659             :        Post-DM:
     660             :        For case 1, the memcmp will return an error and the VM will exit because
     661             :        the memcmp will eventually try to access 0x4051 which is invalid. First
     662             :        0x50 bytes are compared, but the next chunk will lead to an invalid
     663             :        access.
     664             : 
     665             :        For case 2, the memcmp will first translate the first 0x50 bytes and will
     666             :        see that the bytes are not the same. This will lead to the syscall
     667             :        exiting out successfully without detecting the access violation.
     668             : 
     669             :       https://github.com/anza-xyz/agave/blob/v2.0.10/programs/bpf_loader/src/syscalls/mem_ops.rs#L213
     670             :        */
     671             : 
     672             :     /* TODO: Refactor to use `FD_VM_TRANSLATE_MUT` macro when direct mapping is rewritten */
     673          15 :     void * _out = FD_VM_MEM_HADDR_ST( vm, out_vaddr, FD_VM_ALIGN_RUST_I32, 4UL );
     674           0 :     int     out = 0;
     675             : 
     676             :     /* Lookup host address chunks. Try to do a standard memcpy if the regions
     677             :        do not cross memory regions. The translation logic is different if the
     678             :        the virtual address region is the input region vs. not. See the comment
     679             :        in fd_bpf_loader_serialization for more details on how the input
     680             :        region is different from other regions. The input data region will try
     681             :        to lookup the number of remaining bytes in the specific data region. If
     682             :        the memory access is not in the input data region, assume the bytes in
     683             :        the current region are bound by the size of the remaining bytes in the
     684             :        region. */
     685             : 
     686          12 :     ulong   m0_region              = FD_VADDR_TO_REGION( m0_vaddr );
     687          12 :     ulong   m0_offset              = m0_vaddr & FD_VM_OFFSET_MASK;
     688          12 :     ulong   m0_region_idx          = 0UL;
     689          12 :     ulong   m0_bytes_in_cur_region = sz;
     690          12 :     uchar * m0_haddr               = NULL;
     691          12 :     if( m0_region==FD_VM_INPUT_REGION ) {
     692           6 :       m0_region_idx          = fd_vm_get_input_mem_region_idx( vm, m0_offset );
     693           6 :       m0_haddr               = (uchar*)(vm->input_mem_regions[ m0_region_idx ].haddr + m0_offset - vm->input_mem_regions[ m0_region_idx ].vaddr_offset);
     694           6 :       m0_bytes_in_cur_region = fd_ulong_min( sz, fd_ulong_sat_sub( vm->input_mem_regions[ m0_region_idx ].region_sz,
     695           6 :                                                                         ((ulong)m0_haddr - vm->input_mem_regions[ m0_region_idx ].haddr) ) );
     696           6 :     } else {
     697             :       /* We can safely load a slice of 1 byte here because we know that we will
     698             :          not ever read more than the number of bytes that are left in the
     699             :          region. */
     700           6 :       m0_bytes_in_cur_region = fd_ulong_min( sz, vm->region_ld_sz[ m0_region ] - m0_offset );
     701          12 :       m0_haddr               = (uchar *)FD_VM_MEM_SLICE_HADDR_LD_SZ_UNCHECKED( vm, m0_vaddr, FD_VM_ALIGN_RUST_U8 );
     702          12 :     }
     703             : 
     704          12 :     ulong   m1_region              = FD_VADDR_TO_REGION( m1_vaddr );
     705          12 :     ulong   m1_offset              = m1_vaddr & FD_VM_OFFSET_MASK;
     706          12 :     ulong   m1_region_idx          = 0UL;
     707          12 :     ulong   m1_bytes_in_cur_region = sz;
     708          12 :     uchar * m1_haddr               = NULL;
     709          12 :     if( m1_region==FD_VM_INPUT_REGION ) {
     710           6 :       m1_region_idx          = fd_vm_get_input_mem_region_idx( vm, m1_offset );
     711           6 :       m1_haddr               = (uchar*)(vm->input_mem_regions[ m1_region_idx ].haddr + m1_offset - vm->input_mem_regions[ m1_region_idx ].vaddr_offset);
     712           6 :       m1_bytes_in_cur_region = fd_ulong_min( sz, fd_ulong_sat_sub( vm->input_mem_regions[ m1_region_idx ].region_sz,
     713           6 :                                                                         ((ulong)m1_haddr - vm->input_mem_regions[ m1_region_idx ].haddr) ) );
     714           6 :     } else {
     715           6 :       m1_bytes_in_cur_region = fd_ulong_min( sz, vm->region_ld_sz[ m1_region ] - m1_offset );
     716          12 :       m1_haddr               = (uchar *)FD_VM_MEM_SLICE_HADDR_LD_SZ_UNCHECKED( vm, m1_vaddr, FD_VM_ALIGN_RUST_U8 );
     717          12 :     }
     718             : 
     719             :     /* Case where the operation spans multiple regions. Copy over the bytes
     720             :        from each region while iterating to the next one. */
     721             :     /* TODO: An optimization would be to memcmp chunks at once */
     722          12 :     ulong m0_idx = 0UL;
     723          12 :     ulong m1_idx = 0UL;
     724         612 :     for( ulong i=0UL; i<sz; i++ ) {
     725         609 :       if( FD_UNLIKELY( !m0_bytes_in_cur_region ) ) {
     726             :         /* If the memory is not in the input region or it is the last input
     727             :            memory region, that means that if we don't exit now we will have
     728             :            an access violation. */
     729           6 :         if( FD_UNLIKELY( m0_region!=FD_VM_INPUT_REGION || ++m0_region_idx>=vm->input_mem_regions_cnt ) ) {
     730           0 :           FD_VM_ERR_FOR_LOG_EBPF( vm, FD_VM_ERR_EBPF_ACCESS_VIOLATION );
     731           0 :           return FD_VM_SYSCALL_ERR_SEGFAULT;
     732           0 :         }
     733           6 :         if( FD_UNLIKELY( vm->input_mem_regions[ m0_region_idx-1UL ].is_acct_data != vm->input_mem_regions[ m0_region_idx ].is_acct_data ) ) {
     734           0 :           FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );
     735           0 :           return FD_VM_SYSCALL_ERR_SEGFAULT;
     736           0 :         }
     737             :         /* Otherwise, query the next input region. */
     738           6 :         m0_haddr = (uchar*)vm->input_mem_regions[ m0_region_idx ].haddr;
     739           6 :         m0_idx = 0UL;
     740           6 :         m0_bytes_in_cur_region = vm->input_mem_regions[ m0_region_idx ].region_sz;
     741           6 :       }
     742         609 :       if( FD_UNLIKELY( !m1_bytes_in_cur_region ) ) {
     743           0 :         if( FD_UNLIKELY( m1_region!=FD_VM_INPUT_REGION || ++m1_region_idx>=vm->input_mem_regions_cnt ) ) {
     744           0 :           FD_VM_ERR_FOR_LOG_EBPF( vm, FD_VM_ERR_EBPF_ACCESS_VIOLATION );
     745           0 :           return FD_VM_SYSCALL_ERR_SEGFAULT;
     746           0 :         }
     747           0 :         if( FD_UNLIKELY( vm->input_mem_regions[ m1_region_idx-1UL ].is_acct_data != vm->input_mem_regions[ m1_region_idx ].is_acct_data ) ) {
     748           0 :           FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );
     749           0 :           return FD_VM_SYSCALL_ERR_SEGFAULT;
     750           0 :         }
     751           0 :         m1_haddr = (uchar*)vm->input_mem_regions[ m1_region_idx ].haddr;
     752           0 :         m1_idx = 0UL;
     753           0 :         m1_bytes_in_cur_region = vm->input_mem_regions[ m1_region_idx ].region_sz;
     754           0 :       }
     755             : 
     756         609 :       int i0 = (int)m0_haddr[ m0_idx ];
     757         609 :       int i1 = (int)m1_haddr[ m1_idx ];
     758         609 :       if( i0!=i1 ) {
     759           9 :         out = i0 - i1;
     760           9 :         break;
     761           9 :       }
     762             : 
     763         600 :       m0_bytes_in_cur_region--;
     764         600 :       m1_bytes_in_cur_region--;
     765         600 :       m0_idx++;
     766         600 :       m1_idx++;
     767         600 :     }
     768          12 :     fd_memcpy( _out, &out, 4UL ); /* Sigh ... see note above (and might be unaligned ... double sigh) */
     769          12 :     return FD_VM_SUCCESS;
     770          12 :   }
     771          15 : }
     772             : 
     773             : int
     774             : fd_vm_syscall_sol_memset( /**/            void *  _vm,
     775             :                           /**/            ulong   dst_vaddr,
     776             :                           /**/            ulong   c,
     777             :                           /**/            ulong   sz,
     778             :                           FD_PARAM_UNUSED ulong   r4,
     779             :                           FD_PARAM_UNUSED ulong   r5,
     780          27 :                           /**/            ulong * _ret ) {
     781          27 :   fd_vm_t * vm = (fd_vm_t *)_vm;
     782          27 :   *_ret = 0;
     783             : 
     784             :   /* https://github.com/anza-xyz/agave/blob/v2.2.17/programs/bpf_loader/src/syscalls/mem_ops.rs#L142 */
     785             : 
     786          27 :   FD_VM_CU_MEM_OP_UPDATE( vm, sz );
     787             : 
     788          27 :   if( FD_UNLIKELY( !sz ) ) {
     789           0 :     return FD_VM_SUCCESS;
     790           0 :   }
     791             : 
     792          27 :   ulong   region = FD_VADDR_TO_REGION( dst_vaddr );
     793          27 :   ulong   offset = dst_vaddr & FD_VM_OFFSET_MASK;
     794          27 :   uchar * haddr;
     795             : 
     796          27 :   int b = (int)(c & 255UL);
     797             : 
     798          27 :   if( !vm->direct_mapping ) {
     799             :     /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mem_ops.rs#L155-L159 */
     800           9 :     fd_vm_haddr_query_t haddr_query = {
     801           9 :       .vaddr    = dst_vaddr,
     802           9 :       .align    = FD_VM_ALIGN_RUST_U8,
     803           9 :       .sz       = sz,
     804           9 :       .is_slice = 1,
     805           9 :     };
     806             : 
     807           9 :     fd_vm_haddr_query_t * queries[] = { &haddr_query };
     808           9 :     FD_VM_TRANSLATE_MUT( vm, queries );
     809           6 :     fd_memset( haddr_query.haddr, b, sz );
     810          18 :   } else if( region!=FD_VM_INPUT_REGION ) {
     811             :     /* Here we special case non-input region memsets: we try to memset
     812             :        as many bytes as possible until it reaches an unwritable section.
     813             :        This is done in order to ensure error-code conformance with
     814             :        Agave. */
     815           9 :     haddr = (uchar*)FD_VM_MEM_HADDR_ST_FAST( vm, dst_vaddr );
     816           9 :     ulong bytes_in_cur_region = fd_ulong_sat_sub( vm->region_st_sz[ region ], offset );
     817           9 :     ulong bytes_to_set        = fd_ulong_min( sz, bytes_in_cur_region );
     818           9 :     fd_memset( haddr, b, bytes_to_set );
     819           9 :     if( FD_UNLIKELY( bytes_to_set<sz ) ) {
     820           3 :       FD_VM_ERR_FOR_LOG_EBPF( vm, FD_VM_ERR_EBPF_ACCESS_VIOLATION );
     821           3 :       return FD_VM_SYSCALL_ERR_SEGFAULT;
     822           3 :     }
     823           9 :   } else {
     824             :     /* In this case, we are in the input region AND direct mapping is
     825             :        enabled. Get the haddr and input region and check if it's
     826             :        writable. This means that we may potentially iterate over
     827             :        multiple regions. */
     828           9 :     ulong region_idx;
     829           9 :     FD_VM_MEM_HADDR_AND_REGION_IDX_FROM_INPUT_REGION_CHECKED( vm, offset, region_idx, haddr );
     830           0 :     ulong offset_in_cur_region = offset - vm->input_mem_regions[ region_idx ].vaddr_offset;
     831           9 :     ulong bytes_in_cur_region  = fd_ulong_sat_sub( vm->input_mem_regions[ region_idx ].region_sz, offset_in_cur_region );
     832             : 
     833             :     /* Check that current region is writable */
     834           9 :     if( FD_UNLIKELY( !vm->input_mem_regions[ region_idx ].is_writable ) ) {
     835           0 :       FD_VM_ERR_FOR_LOG_EBPF( vm, FD_VM_ERR_EBPF_ACCESS_VIOLATION );
     836           0 :       return FD_VM_SYSCALL_ERR_SEGFAULT;
     837           0 :     }
     838             : 
     839             :     /* Memset goes into multiple regions. */
     840          30 :     while( sz>0UL ) {
     841             : 
     842             :       /* Memset bytes */
     843          30 :       ulong num_bytes_to_set = fd_ulong_min( sz, bytes_in_cur_region );
     844          30 :       fd_memset( haddr, b, num_bytes_to_set );
     845          30 :       sz -= num_bytes_to_set;
     846             : 
     847          30 :       if( !sz ) {
     848           6 :         break;
     849           6 :       }
     850             : 
     851             :       /* If no more regions left, break. */
     852          24 :       if( ++region_idx==vm->input_mem_regions_cnt ) {
     853           0 :         break;
     854           0 :       }
     855             : 
     856             :       /* Check that new region is writable. */
     857          24 :       if( FD_UNLIKELY( !vm->input_mem_regions[ region_idx ].is_writable ) ) {
     858           3 :         break;
     859           3 :       }
     860             : 
     861             :       /* If new region crosses into/out of account region, error out. */
     862          21 :       if( FD_UNLIKELY( vm->input_mem_regions[ region_idx ].is_acct_data !=
     863          21 :                        vm->input_mem_regions[ region_idx-1UL ].is_acct_data ) ) {
     864           0 :         FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );
     865           0 :         return FD_VM_SYSCALL_ERR_SEGFAULT;
     866           0 :       }
     867             : 
     868             :       /* Move haddr to next region. */
     869          21 :       haddr               = (uchar*)vm->input_mem_regions[ region_idx ].haddr;
     870          21 :       bytes_in_cur_region = vm->input_mem_regions[ region_idx ].region_sz;
     871          21 :     }
     872             : 
     873             :     /* If we were not able to successfully set all the bytes, throw an error. */
     874           9 :     if( FD_UNLIKELY( sz>0 ) ) {
     875           3 :       FD_VM_ERR_FOR_LOG_EBPF( vm, FD_VM_ERR_EBPF_ACCESS_VIOLATION );
     876           3 :       return FD_VM_SYSCALL_ERR_SEGFAULT;
     877           3 :     }
     878           9 :   }
     879          18 :   return FD_VM_SUCCESS;
     880          27 : }

Generated by: LCOV version 1.14