LCOV - code coverage report
Current view: top level - flamenco/vm - fd_vm_private.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 144 204 70.6 %
Date: 2025-11-28 04:36:48 Functions: 31 598 5.2 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_vm_fd_vm_private_h
       2             : #define HEADER_fd_src_flamenco_vm_fd_vm_private_h
       3             : 
       4             : #include "fd_vm.h"
       5             : 
       6             : #include "../../ballet/sbpf/fd_sbpf_instr.h"
       7             : #include "../../ballet/sbpf/fd_sbpf_opcodes.h"
       8             : #include "../../ballet/murmur3/fd_murmur3.h"
       9             : #include "../runtime/fd_runtime_const.h"
      10             : #include "../runtime/fd_runtime.h"
      11             : #include "../features/fd_features.h"
      12             : #include "../runtime/context/fd_exec_instr_ctx.h"
      13             : #include "fd_vm_base.h"
      14             : 
      15             : /* FD_VM_ALIGN_RUST_{} define the alignments for relevant rust types.
      16             :    Alignments are derived with std::mem::align_of::<T>() and are enforced
      17             :    by the VM (with the exception of v1 loader).
      18             : 
      19             :    In our implementation, when calling FD_VM_MEM_HADDR_ST / FD_VM_MEM_HADDR_LD,
      20             :    we need to make sure we're passing the correct alignment based on the Rust
      21             :    type in the corresponding mapping in Agave.
      22             : 
      23             :    FD_VM_ALIGN_RUST_{} has been generated with this Rust code:
      24             :    ```rust
      25             :       pub type Epoch = u64;
      26             :       pub struct Pubkey(pub [u8; 32]);
      27             :       pub struct AccountMeta {
      28             :           pub lamports: u64,
      29             :           pub rent_epoch: Epoch,
      30             :           pub owner: Pubkey,
      31             :           pub executable: bool,
      32             :       }
      33             : 
      34             :       pub struct PodScalar(pub [u8; 32]);
      35             : 
      36             :       fn main() {
      37             :           println!("u8: {}", std::mem::align_of::<u8>());
      38             :           println!("u32: {}", std::mem::align_of::<u32>());
      39             :           println!("u64: {}", std::mem::align_of::<u64>());
      40             :           println!("u128: {}", std::mem::align_of::<u128>());
      41             :           println!("&[u8]: {}", std::mem::align_of::<&[u8]>());
      42             :           println!("AccountMeta: {}", std::mem::align_of::<AccountMeta>());
      43             :           println!("PodScalar: {}", std::mem::align_of::<PodScalar>());
      44             :           println!("Pubkey: {}", std::mem::align_of::<Pubkey>());
      45             :       }
      46             :     ``` */
      47             : 
      48          93 : #define FD_VM_ALIGN_RUST_U8                       (1UL)
      49             : #define FD_VM_ALIGN_RUST_U32                      (4UL)
      50          15 : #define FD_VM_ALIGN_RUST_I32                      (4UL)
      51             : #define FD_VM_ALIGN_RUST_U64                      (8UL)
      52             : #define FD_VM_ALIGN_RUST_U128                    (16UL)
      53             : #define FD_VM_ALIGN_RUST_SLICE_U8_REF             (8UL)
      54          18 : #define FD_VM_ALIGN_RUST_POD_U8_ARRAY             (1UL)
      55           0 : #define FD_VM_ALIGN_RUST_PUBKEY                   (1UL)
      56           0 : #define FD_VM_ALIGN_RUST_SYSVAR_CLOCK             (8UL)
      57           0 : #define FD_VM_ALIGN_RUST_SYSVAR_EPOCH_SCHEDULE    (8UL)
      58           0 : #define FD_VM_ALIGN_RUST_SYSVAR_RENT              (8UL)
      59           0 : #define FD_VM_ALIGN_RUST_SYSVAR_LAST_RESTART_SLOT (8UL)
      60             : #define FD_VM_ALIGN_RUST_SYSVAR_EPOCH_REWARDS    (16UL)
      61             : #define FD_VM_ALIGN_RUST_STABLE_INSTRUCTION       (8UL)
      62             : 
      63             : /* fd_vm_vec_t is the in-memory representation of a vector descriptor.
      64             :    Equal in layout to the Rust slice header &[_] and various vector
      65             :    types in the C version of the syscall API. */
      66             : /* FIXME: WHEN IS VADDR NULL AND/OR SZ 0 OKAY? */
      67             : /* FIXME: MOVE FD_VM_RUST_VEC_T FROM SYSCALL/FD_VM_CPI.H HERE TOO? */
      68             : 
      69             : #define FD_VM_VEC_ALIGN FD_VM_ALIGN_RUST_SLICE_U8_REF
      70             : #define FD_VM_VEC_SIZE  (16UL)
      71             : 
      72             : struct __attribute__((packed)) fd_vm_vec {
      73             :   ulong addr; /* FIXME: NAME -> VADDR */
      74             :   ulong len;  /* FIXME: NAME -> SZ */
      75             : };
      76             : 
      77             : typedef struct fd_vm_vec fd_vm_vec_t;
      78             : 
      79             : FD_STATIC_ASSERT( sizeof(fd_vm_vec_t)==FD_VM_VEC_SIZE, fd_vm_vec size mismatch );
      80             : 
      81             : /* SBPF version and features
      82             :    https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/program.rs#L28
      83             :    Note: SIMDs enable or disable features, e.g. BPF instructions.
      84             :    If we have macros with names ENABLE vs DISABLE, we have the advantage that
      85             :    the condition is always pretty clear: sbpf_version <= activation_version,
      86             :    but the disadvantage of inconsistent names.
      87             :    Viceversa, calling everything ENABLE has the risk to invert a <= with a >=
      88             :    and create a huge mess.
      89             :    We define both, so hopefully it's foolproof. */
      90             : 
      91             : #define FD_VM_SBPF_REJECT_RODATA_STACK_OVERLAP(v)  ( v != FD_SBPF_V0 )
      92             : #define FD_VM_SBPF_ENABLE_ELF_VADDR(v)             ( v != FD_SBPF_V0 )
      93             : /* SIMD-0166 */
      94   805499463 : #define FD_VM_SBPF_DYNAMIC_STACK_FRAMES(v)         ( v >= FD_SBPF_V1 )
      95             : /* SIMD-0173 */
      96        7884 : #define FD_VM_SBPF_CALLX_USES_SRC_REG(v)           ( v >= FD_SBPF_V2 )
      97             : #define FD_VM_SBPF_DISABLE_LDDW(v)                 ( v >= FD_SBPF_V2 )
      98       78000 : #define FD_VM_SBPF_ENABLE_LDDW(v)                  ( v <  FD_SBPF_V2 )
      99             : #define FD_VM_SBPF_DISABLE_LE(v)                   ( v >= FD_SBPF_V2 )
     100       39000 : #define FD_VM_SBPF_ENABLE_LE(v)                    ( v <  FD_SBPF_V2 )
     101      936000 : #define FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(v)       ( v >= FD_SBPF_V2 )
     102             : /* SIMD-0174 */
     103     1053000 : #define FD_VM_SBPF_ENABLE_PQR(v)                   ( v >= FD_SBPF_V2 )
     104             : #define FD_VM_SBPF_DISABLE_NEG(v)                  ( v >= FD_SBPF_V2 )
     105       39000 : #define FD_VM_SBPF_ENABLE_NEG(v)                   ( v <  FD_SBPF_V2 )
     106       62232 : #define FD_VM_SBPF_SWAP_SUB_REG_IMM_OPERANDS(v)    ( v >= FD_SBPF_V2 )
     107      124464 : #define FD_VM_SBPF_EXPLICIT_SIGN_EXT(v)            ( v >= FD_SBPF_V2 )
     108             : /* SIMD-0178 + SIMD-0179 */
     109      156000 : #define FD_VM_SBPF_STATIC_SYSCALLS(v)              ( v >= FD_SBPF_V3 )
     110             : /* SIMD-0189 */
     111             : #define FD_VM_SBPF_ENABLE_LOWER_BYTECODE_VADDR(v)  ( v >= FD_SBPF_V3 )
     112             : /* enable_strict_elf_headers is defined in fd_sbpf_loader.h because it's needed
     113             :    by the ELF loader, not really by the VM
     114             :    #define FD_VM_SBPF_ENABLE_STRICTER_ELF_HEADERS(v)  ( v >= FD_SBPF_V3 ) */
     115             : 
     116         789 : #define FD_VM_OFFSET_MASK (0xffffffffUL)
     117             : 
     118             : /* https://github.com/anza-xyz/agave/blob/v3.0.1/transaction-context/src/lib.rs#L32 */
     119           0 : #define FD_MAX_ACCOUNT_DATA_GROWTH_PER_TRANSACTION (FD_RUNTIME_ACC_SZ_MAX * 2UL)
     120             : 
     121             : FD_PROTOTYPES_BEGIN
     122             : 
     123             : /* Error logging handholding assertions */
     124             : 
     125             : #ifdef FD_RUNTIME_ERR_HANDHOLDING
     126             : /* Asserts that the error and error kind are populated (non-zero) */
     127             : #define FD_VM_TEST_ERR_EXISTS( vm )                                       \
     128             :     FD_TEST( vm->instr_ctx->txn_out->err.exec_err );                      \
     129             :     FD_TEST( vm->instr_ctx->txn_out->err.exec_err_kind )
     130             : 
     131             : /* Used prior to a FD_VM_ERR_FOR_LOG_INSTR call to deliberately
     132             :    bypass overwrite handholding checks.
     133             :    Only use this if you know what you're doing. */
     134             : #define FD_VM_PREPARE_ERR_OVERWRITE( vm )                                 \
     135             :    vm->instr_ctx->txn_out->err.exec_err = 0;                              \
     136             :    vm->instr_ctx->txn_out->err.exec_err_kind = 0
     137             : 
     138             : /* Asserts that the error and error kind are not populated (zero) */
     139             : #define FD_VM_TEST_ERR_OVERWRITE( vm )                                    \
     140             :     FD_TEST( !vm->instr_ctx->txn_out->err.exec_err );                     \
     141             :     FD_TEST( !vm->instr_ctx->txn_out->err.exec_err_kind )
     142             : #else
     143           0 : #define FD_VM_TEST_ERR_EXISTS( vm ) ( ( void )0 )
     144           0 : #define FD_VM_PREPARE_ERR_OVERWRITE( vm ) ( ( void )0 )
     145          81 : #define FD_VM_TEST_ERR_OVERWRITE( vm ) ( ( void )0 )
     146             : #endif
     147             : 
     148             : /* Log error within the instr_ctx to match Agave/Rust error. */
     149             : 
     150          57 : #define FD_VM_ERR_FOR_LOG_EBPF( vm, err_ ) (__extension__({                \
     151          57 :     FD_VM_TEST_ERR_OVERWRITE( vm );                                        \
     152          57 :     vm->instr_ctx->txn_out->err.exec_err = err_;                           \
     153          57 :     vm->instr_ctx->txn_out->err.exec_err_kind = FD_EXECUTOR_ERR_KIND_EBPF; \
     154          57 :   }))
     155             : 
     156          24 : #define FD_VM_ERR_FOR_LOG_SYSCALL( vm, err_ ) (__extension__({                \
     157          24 :     FD_VM_TEST_ERR_OVERWRITE( vm );                                           \
     158          24 :     vm->instr_ctx->txn_out->err.exec_err = err_;                              \
     159          24 :     vm->instr_ctx->txn_out->err.exec_err_kind = FD_EXECUTOR_ERR_KIND_SYSCALL; \
     160          24 :   }))
     161             : 
     162           0 : #define FD_VM_ERR_FOR_LOG_INSTR( vm, err_ ) (__extension__({                \
     163           0 :     FD_VM_TEST_ERR_OVERWRITE( vm );                                         \
     164           0 :     vm->instr_ctx->txn_out->err.exec_err = err_;                            \
     165           0 :     vm->instr_ctx->txn_out->err.exec_err_kind = FD_EXECUTOR_ERR_KIND_INSTR; \
     166           0 :   }))
     167             : 
     168         747 : #define FD_VADDR_TO_REGION( _vaddr ) fd_ulong_min( (_vaddr) >> FD_VM_MEM_MAP_REGION_VIRT_ADDR_BITS, FD_VM_HIGH_REGION )
     169             : 
     170             : /* fd_vm_instr APIs ***************************************************/
     171             : 
     172             : /* FIXME: MIGRATE FD_SBPF_INSTR_T STUFF TO THIS API */
     173             : 
     174             : /* fd_vm_instr returns the SBPF instruction word corresponding to the
     175             :    given fields. */
     176             : 
     177             : FD_FN_CONST static inline ulong
     178             : fd_vm_instr( ulong opcode, /* Assumed valid */
     179             :              ulong dst,    /* Assumed in [0,FD_VM_REG_CNT) */
     180             :              ulong src,    /* Assumed in [0,FD_VM_REG_CNT) */
     181             :              short offset,
     182       16587 :              uint  imm ) {
     183       16587 :   return opcode | (dst<<8) | (src<<12) | (((ulong)(ushort)offset)<<16) | (((ulong)imm)<<32);
     184       16587 : }
     185             : 
     186             : /* fd_vm_instr_* return the SBPF instruction field for the given word.
     187             :    fd_vm_instr_{normal,mem}_* only apply to {normal,mem} opclass
     188             :    instructions. */
     189             : 
     190      381843 : FD_FN_CONST static inline ulong fd_vm_instr_opcode( ulong instr ) { return   instr      & 255UL;       } /* In [0,256) */
     191      381843 : FD_FN_CONST static inline ulong fd_vm_instr_dst   ( ulong instr ) { return ((instr>> 8) &  15UL);      } /* In [0,16)  */
     192      381843 : FD_FN_CONST static inline ulong fd_vm_instr_src   ( ulong instr ) { return ((instr>>12) &  15UL);      } /* In [0,16)  */
     193      381843 : FD_FN_CONST static inline ulong fd_vm_instr_offset( ulong instr ) { return (ulong)(long)(short)(ushort)(instr>>16); }
     194      381960 : FD_FN_CONST static inline uint  fd_vm_instr_imm   ( ulong instr ) { return (uint)(instr>>32);          }
     195             : 
     196           0 : FD_FN_CONST static inline ulong fd_vm_instr_opclass       ( ulong instr ) { return  instr      & 7UL; } /* In [0,8)  */
     197           0 : FD_FN_CONST static inline ulong fd_vm_instr_normal_opsrc  ( ulong instr ) { return (instr>>3) &  1UL; } /* In [0,2)  */
     198           0 : FD_FN_CONST static inline ulong fd_vm_instr_normal_opmode ( ulong instr ) { return (instr>>4) & 15UL; } /* In [0,16) */
     199           0 : FD_FN_CONST static inline ulong fd_vm_instr_mem_opsize    ( ulong instr ) { return (instr>>3) &  3UL; } /* In [0,4)  */
     200           0 : FD_FN_CONST static inline ulong fd_vm_instr_mem_opaddrmode( ulong instr ) { return (instr>>5) &  7UL; } /* In [0,16) */
     201             : 
     202             : /* fd_vm_mem API ******************************************************/
     203             : 
     204             : /* fd_vm_mem APIs support the fast mapping of virtual address ranges to
     205             :    host address ranges.  Since the SBPF virtual address space consists
     206             :    of 4 consecutive 4GiB regions and the mapable size of each region is
     207             :    less than 4 GiB (as implied by FD_VM_MEM_MAP_REGION_SZ==2^32-1 and
     208             :    that Solana protocol limits are much smaller still), it is impossible
     209             :    for a valid virtual address range to span multiple regions. */
     210             : 
     211             : /* fd_vm_mem_cfg configures the vm's tlb arrays.  Assumes vm is valid
     212             :    and vm already has configured the rodata, stack, heap and input
     213             :    regions.  Returns vm. */
     214             : 
     215             : static inline fd_vm_t *
     216        8070 : fd_vm_mem_cfg( fd_vm_t * vm ) {
     217        8070 :   vm->region_haddr[0] = 0UL;                                vm->region_ld_sz[0]                  = (uint)0UL;             vm->region_st_sz[0]                  = (uint)0UL;
     218        8070 :   vm->region_haddr[FD_VM_PROG_REGION]  = (ulong)vm->rodata; vm->region_ld_sz[FD_VM_PROG_REGION]  = (uint)vm->rodata_sz;   vm->region_st_sz[FD_VM_PROG_REGION]  = (uint)0UL;
     219        8070 :   vm->region_haddr[FD_VM_STACK_REGION] = (ulong)vm->stack;  vm->region_ld_sz[FD_VM_STACK_REGION] = (uint)FD_VM_STACK_MAX; vm->region_st_sz[FD_VM_STACK_REGION] = (uint)FD_VM_STACK_MAX;
     220        8070 :   vm->region_haddr[FD_VM_HEAP_REGION]  = (ulong)vm->heap;   vm->region_ld_sz[FD_VM_HEAP_REGION]  = (uint)vm->heap_max;    vm->region_st_sz[FD_VM_HEAP_REGION]  = (uint)vm->heap_max;
     221        8070 :   vm->region_haddr[5]                  = 0UL;               vm->region_ld_sz[5]                  = (uint)0UL;             vm->region_st_sz[5]                  = (uint)0UL;
     222        8070 :   if( vm->direct_mapping || !vm->input_mem_regions_cnt ) {
     223             :     /* When direct mapping is enabled, we don't use these fields because
     224             :        the load and stores are fragmented. */
     225         414 :     vm->region_haddr[FD_VM_INPUT_REGION] = 0UL;
     226         414 :     vm->region_ld_sz[FD_VM_INPUT_REGION] = 0U;
     227         414 :     vm->region_st_sz[FD_VM_INPUT_REGION] = 0U;
     228        7656 :   } else {
     229        7656 :     vm->region_haddr[FD_VM_INPUT_REGION] = vm->input_mem_regions[0].haddr;
     230        7656 :     vm->region_ld_sz[FD_VM_INPUT_REGION] = vm->input_mem_regions[0].region_sz;
     231        7656 :     vm->region_st_sz[FD_VM_INPUT_REGION] = vm->input_mem_regions[0].region_sz;
     232        7656 :   }
     233        8070 :   return vm;
     234        8070 : }
     235             : 
     236             : /* Simplified version of Agave's `generate_access_violation()` function
     237             :    that simply returns either FD_VM_ERR_EBPF_ACCESS_VIOLATION or
     238             :    FD_VM_ERR_EBPF_STACK_ACCESS_VIOLATION. This has no consensus
     239             :    effects and is purely for logging purposes for fuzzing. Returns
     240             :    FD_VM_ERR_EBPF_STACK_ACCESS_VIOLATION if the provided vaddr is in the
     241             :    stack (0x200000000) and FD_VM_ERR_EBPF_ACCESS_VIOLATION otherwise.
     242             : 
     243             :    https://github.com/anza-xyz/sbpf/blob/v0.11.1/src/memory_region.rs#L834-L869 */
     244             : static FD_FN_PURE inline int
     245         255 : fd_vm_generate_access_violation( ulong vaddr, ulong sbpf_version ) {
     246             :   /* rel_offset can be negative because there is an edge case where the
     247             :      first "frame" right before the stack region should also throw a
     248             :      stack access violation. */
     249         255 :   long rel_offset = fd_long_sat_sub( (long)vaddr, (long)FD_VM_MEM_MAP_STACK_REGION_START );
     250         255 :   long stack_frame = rel_offset / (long)FD_VM_STACK_FRAME_SZ;
     251         255 :   if( !fd_sbpf_dynamic_stack_frames_enabled( sbpf_version ) &&
     252         255 :       stack_frame>=-1L && stack_frame<=(long)FD_VM_MAX_CALL_DEPTH ) {
     253           0 :     return FD_VM_ERR_EBPF_STACK_ACCESS_VIOLATION;
     254           0 :   }
     255         255 :   return FD_VM_ERR_EBPF_ACCESS_VIOLATION;
     256         255 : }
     257             : 
     258             : /* fd_vm_mem_haddr translates the vaddr range [vaddr,vaddr+sz) (in
     259             :    infinite precision math) into the non-wrapping haddr range
     260             :    [haddr,haddr+sz).  On success, returns haddr and every byte in the
     261             :    haddr range is a valid address.  On failure, returns sentinel and
     262             :    there was at least one byte in the virtual address range that did not
     263             :    have a corresponding byte in the host address range.
     264             : 
     265             :    IMPORTANT SAFETY TIP!  When sz==0, the return value currently is
     266             :    arbitrary.  This is often fine as there should be no
     267             :    actual accesses to a sz==0 region.  However, this also means that
     268             :    testing return for sentinel is insufficient to tell if mapping
     269             :    failed.  That is, assuming sentinel is a location that could never
     270             :    happen on success:
     271             : 
     272             :      sz!=0 and ret!=sentinel -> success
     273             :      sz!=0 and ret==sentinel -> failure
     274             :      sz==0 -> ignore ret, application specific handling
     275             : 
     276             :    With ~O(2) extra fast branchless instructions, the below could be
     277             :    tweaked in the sz==0 case to return NULL or return a non-NULL
     278             :    sentinel value.  What is most optimal practically depends on how
     279             :    empty ranges and NULL vaddr handling is defined in the application.
     280             : 
     281             :    Requires ~O(10) fast branchless assembly instructions with 2 L1 cache
     282             :    hit loads and pretty good ILP.
     283             : 
     284             :    fd_vm_mem_haddr_fast is when the vaddr is for use when it is already
     285             :    known that the vaddr region has a valid mapping.
     286             : 
     287             :    These assumptions don't hold if direct mapping is enabled since input
     288             :    region lookups become O(log(n)). */
     289             : 
     290             : 
     291             : /* fd_vm_get_input_mem_region_idx returns the index into the input memory
     292             :    region array with the largest region offset that is <= the offset that
     293             :    is passed in.  This function makes NO guarantees about the input being
     294             :    a valid input region offset; the caller is responsible for safely handling
     295             :    it. */
     296             : static inline ulong
     297         348 : fd_vm_get_input_mem_region_idx( fd_vm_t const * vm, ulong offset ) {
     298         348 :   uint left  = 0U;
     299         348 :   uint right = vm->input_mem_regions_cnt - 1U;
     300         348 :   uint mid   = 0U;
     301             : 
     302         576 :   while( left<right ) {
     303         228 :     mid = (left+right) / 2U;
     304         228 :     if( offset>=vm->input_mem_regions[ mid ].vaddr_offset+vm->input_mem_regions[ mid ].address_space_reserved ) {
     305          51 :       left = mid + 1U;
     306         177 :     } else {
     307         177 :       right = mid;
     308         177 :     }
     309         228 :   }
     310         348 :   return left;
     311         348 : }
     312             : 
     313             : /* If the region is an account, handle the resizing logic. This logic
     314             :    corresponds to
     315             :    solana_transaction_context::TransactionContext::access_violation_handler
     316             : 
     317             :    https://github.com/anza-xyz/agave/blob/v3.0.1/transaction-context/src/lib.rs#L510-L581 */
     318             : static inline void
     319             : fd_vm_handle_input_mem_region_oob( fd_vm_t const * vm,
     320             :                                    ulong           offset,
     321             :                                    ulong           sz,
     322             :                                    ulong           region_idx,
     323         120 :                                    uchar           write ) {
     324             :   /* If stricter_abi_and_runtime_constraints is not enabled, we don't need to
     325             :      do anything */
     326         120 :   if( FD_UNLIKELY( !vm->stricter_abi_and_runtime_constraints ) ) {
     327         102 :     return;
     328         102 :   }
     329             : 
     330             :   /* If the access is not a write, we don't need to do anything
     331             :      https://github.com/anza-xyz/agave/blob/v3.0.1/transaction-context/src/lib.rs#L523-L525 */
     332          18 :   if( FD_UNLIKELY( !write ) ) {
     333           0 :     return;
     334           0 :   }
     335             : 
     336          18 :   fd_vm_input_region_t * region = &vm->input_mem_regions[ region_idx ];
     337             :   /* If the region is not writable, we don't need to do anything
     338             :      https://github.com/anza-xyz/agave/blob/v3.0.1/transaction-context/src/lib.rs#L526-L529 */
     339          18 :   if( FD_UNLIKELY( !region->is_writable ) ) {
     340           0 :     return;
     341           0 :   }
     342             : 
     343             :   /* Calculate the requested length
     344             :      https://github.com/anza-xyz/agave/blob/v3.0.1/transaction-context/src/lib.rs#L532-L535 */
     345          18 :   ulong requested_len = fd_ulong_sat_sub( fd_ulong_sat_add( offset, sz ), region->vaddr_offset );
     346          18 :   if( FD_UNLIKELY( requested_len > region->address_space_reserved ) ) {
     347          18 :     return;
     348          18 :   }
     349             : 
     350             :   /* Calculate the remaining allowed growth
     351             :      https://github.com/anza-xyz/agave/blob/v3.0.1/transaction-context/src/lib.rs#L549-L551 */
     352           0 :   ulong remaining_allowed_growth = fd_ulong_sat_sub(
     353           0 :     FD_MAX_ACCOUNT_DATA_GROWTH_PER_TRANSACTION,
     354           0 :     vm->instr_ctx->txn_out->details.accounts_resize_delta );
     355             : 
     356             :   /* If the requested length is greater than the size of the region,
     357             :      resize the region
     358             :      https://github.com/anza-xyz/agave/blob/v3.0.1/transaction-context/src/lib.rs#L553-L571 */
     359           0 :   if( FD_UNLIKELY( requested_len > region->region_sz ) ) {
     360             :     /* Calculate the new region size
     361             :        https://github.com/anza-xyz/agave/blob/v3.0.1/transaction-context/src/lib.rs#L558-L560 */
     362           0 :     ulong new_region_sz = fd_ulong_min(
     363           0 :       fd_ulong_min( region->address_space_reserved, FD_RUNTIME_ACC_SZ_MAX ),
     364           0 :       fd_ulong_sat_add( region->region_sz, remaining_allowed_growth ) );
     365             : 
     366             :     /* Resize the account and the region
     367             :        https://github.com/anza-xyz/agave/blob/v3.0.1/transaction-context/src/lib.rs#L569-L570 */
     368           0 :     if( FD_UNLIKELY( new_region_sz > region->region_sz ) ) {
     369           0 :       vm->instr_ctx->txn_out->details.accounts_resize_delta = fd_ulong_sat_sub(
     370           0 :         fd_ulong_sat_add( vm->instr_ctx->txn_out->details.accounts_resize_delta, new_region_sz ),
     371           0 :         region->region_sz );
     372             : 
     373           0 :       fd_txn_account_resize( vm->acc_region_metas[ region->acc_region_meta_idx ].acct, new_region_sz );
     374           0 :       region->region_sz = (uint)new_region_sz;
     375           0 :     }
     376           0 :   }
     377           0 : }
     378             : 
     379             : /* fd_vm_find_input_mem_region returns the translated haddr for a given
     380             :    offset into the input region.  If an offset/sz is invalid or if an
     381             :    illegal write is performed, the sentinel value is returned. If the offset
     382             :    provided is too large, it will choose the upper-most region as the
     383             :    region_idx. However, it will get caught for being too large of an access
     384             :    in the multi-region checks. */
     385             : static inline ulong
     386             : fd_vm_find_input_mem_region( fd_vm_t const * vm,
     387             :                              ulong           offset,
     388             :                              ulong           sz,
     389             :                              uchar           write,
     390         348 :                              ulong           sentinel ) {
     391         348 :   if( FD_UNLIKELY( vm->input_mem_regions_cnt==0 ) ) {
     392           0 :     return sentinel; /* Access is too large */
     393           0 :   }
     394             : 
     395             :   /* Binary search to find the correct memory region.  If direct mapping is not
     396             :      enabled, then there is only 1 memory region which spans the input region. */
     397         348 :   ulong region_idx = fd_vm_get_input_mem_region_idx( vm, offset );
     398         348 :   if( FD_UNLIKELY( region_idx>=vm->input_mem_regions_cnt ) ) {
     399           0 :     return sentinel; /* Region not found */
     400           0 :   }
     401             : 
     402         348 :   ulong bytes_in_region = fd_ulong_sat_sub( vm->input_mem_regions[ region_idx ].region_sz,
     403         348 :                                             fd_ulong_sat_sub( offset, vm->input_mem_regions[ region_idx ].vaddr_offset ) );
     404             : 
     405             :   /* If the access is out of bounds, invoke the callback to handle the out of bounds access.
     406             :      This potentially resizes the region if necessary. */
     407         348 :   if( FD_UNLIKELY( sz>bytes_in_region ) ) {
     408         120 :     fd_vm_handle_input_mem_region_oob( vm, offset, sz, region_idx, write );
     409         120 :   }
     410             : 
     411             :   /* After potentially resizing, re-check the bounds */
     412         348 :   bytes_in_region = fd_ulong_sat_sub( vm->input_mem_regions[ region_idx ].region_sz,
     413         348 :                                       fd_ulong_sat_sub( offset, vm->input_mem_regions[ region_idx ].vaddr_offset ) );
     414             :   /* If the access is still out of bounds, return the sentinel */
     415         348 :   if( FD_UNLIKELY( sz>bytes_in_region ) ) {
     416         120 :     return sentinel;
     417         120 :   }
     418             : 
     419         228 :   if( FD_UNLIKELY( write && vm->input_mem_regions[ region_idx ].is_writable==0U ) ) {
     420           0 :     return sentinel; /* Illegal write */
     421           0 :   }
     422             : 
     423         228 :   ulong start_region_idx = region_idx;
     424             : 
     425         228 :   ulong adjusted_haddr = vm->input_mem_regions[ start_region_idx ].haddr + offset - vm->input_mem_regions[ start_region_idx ].vaddr_offset;
     426         228 :   return adjusted_haddr;
     427         228 : }
     428             : 
     429             : 
     430             : static inline ulong
     431             : fd_vm_mem_haddr( fd_vm_t const * vm,
     432             :                  ulong           vaddr,
     433             :                  ulong           sz,
     434             :                  ulong const *   vm_region_haddr, /* indexed [0,6) */
     435             :                  uint  const *   vm_region_sz,    /* indexed [0,6) */
     436             :                  uchar           write,           /* 1 if the access is a write, 0 if it is a read */
     437         747 :                  ulong           sentinel ) {
     438         747 :   ulong region = FD_VADDR_TO_REGION( vaddr );
     439         747 :   ulong offset = vaddr & FD_VM_OFFSET_MASK;
     440             : 
     441             :   /* Stack memory regions have 4kB unmapped "gaps" in-between each frame, which only exist if...
     442             :           - dynamic stack frames are not enabled (!(SBPF version >= SBPF_V1))
     443             :      https://github.com/anza-xyz/agave/blob/v2.2.12/programs/bpf_loader/src/lib.rs#L344-L351
     444             :     */
     445         747 :   if( FD_UNLIKELY( region==FD_VM_STACK_REGION &&
     446         747 :                    !fd_sbpf_dynamic_stack_frames_enabled( vm->sbpf_version ) ) ) {
     447             :     /* If an access starts in a gap region, that is an access violation */
     448           0 :     if( FD_UNLIKELY( !!(vaddr & 0x1000) ) ) {
     449           0 :       return sentinel;
     450           0 :     }
     451             : 
     452             :     /* To account for the fact that we have gaps in the virtual address space but not in the
     453             :        physical address space, we need to subtract from the offset the size of all the virtual
     454             :        gap frames underneath it.
     455             : 
     456             :        https://github.com/solana-labs/rbpf/blob/b503a1867a9cfa13f93b4d99679a17fe219831de/src/memory_region.rs#L147-L149 */
     457           0 :     ulong gap_mask = 0xFFFFFFFFFFFFF000;
     458           0 :     offset = ( ( offset & gap_mask ) >> 1 ) | ( offset & ~gap_mask );
     459           0 :   }
     460             : 
     461         747 :   ulong region_sz = (ulong)vm_region_sz[ region ];
     462         747 :   ulong sz_max    = region_sz - fd_ulong_min( offset, region_sz );
     463             : 
     464             :   /* If the region is an account, handle the resizing logic. This logic corresponds to
     465             :      solana_transaction_context::TransactionContext::access_violation_handler
     466             : 
     467             :      https://github.com/anza-xyz/agave/blob/v3.0.1/transaction-context/src/lib.rs#L510-L581 */
     468         747 :   if( region==FD_VM_INPUT_REGION ) {
     469         348 :     return fd_vm_find_input_mem_region( vm, offset, sz, write, sentinel );
     470         348 :   }
     471             : 
     472             : # ifdef FD_VM_INTERP_MEM_TRACING_ENABLED
     473             :   if ( FD_LIKELY( sz<=sz_max ) ) {
     474             :     fd_vm_trace_event_mem( vm->trace, write, vaddr, sz, vm_region_haddr[ region ] + offset );
     475             :   }
     476             : # endif
     477         399 :   return fd_ulong_if( sz<=sz_max, vm_region_haddr[ region ] + offset, sentinel );
     478         747 : }
     479             : 
     480             : static inline ulong
     481             : fd_vm_mem_haddr_fast( fd_vm_t const * vm,
     482             :                       ulong           vaddr,
     483           0 :                       ulong   const * vm_region_haddr ) { /* indexed [0,6) */
     484           0 :   ulong region   = FD_VADDR_TO_REGION( vaddr );
     485           0 :   ulong offset   = vaddr & FD_VM_OFFSET_MASK;
     486           0 :   if( FD_UNLIKELY( region==FD_VM_INPUT_REGION ) ) {
     487           0 :     return fd_vm_find_input_mem_region( vm, offset, 1UL, 0, 0UL );
     488           0 :   }
     489           0 :   return vm_region_haddr[ region ] + offset;
     490           0 : }
     491             : 
     492          54 : FD_FN_PURE static inline ulong fd_vm_mem_ld_1( ulong haddr ) {
     493          54 :   return (ulong)*(uchar const *)haddr;
     494          54 : }
     495             : 
     496          60 : FD_FN_PURE static inline ulong fd_vm_mem_ld_2( ulong haddr ) {
     497          60 :   ushort t;
     498          60 :   memcpy( &t, (void const *)haddr, sizeof(ushort) );
     499          60 :   return (ulong)t;
     500          60 : }
     501             : 
     502          60 : FD_FN_PURE static inline ulong fd_vm_mem_ld_4( ulong haddr ) {
     503          60 :   uint t;
     504          60 :   memcpy( &t, (void const *)haddr, sizeof(uint) );
     505          60 :   return (ulong)t;
     506          60 : }
     507             : 
     508          42 : FD_FN_PURE static inline ulong fd_vm_mem_ld_8( ulong haddr ) {
     509          42 :   ulong t;
     510          42 :   memcpy( &t, (void const *)haddr, sizeof(ulong) );
     511          42 :   return t;
     512          42 : }
     513             : 
     514           6 : static inline void fd_vm_mem_st_1( ulong haddr, uchar val ) {
     515           6 :   *(uchar *)haddr = val;
     516           6 : }
     517             : 
     518             : static inline void fd_vm_mem_st_2( ulong  haddr,
     519           6 :                                    ushort val ) {
     520           6 :   memcpy( (void *)haddr, &val, sizeof(ushort) );
     521           6 : }
     522             : 
     523             : static inline void fd_vm_mem_st_4( ulong haddr,
     524           6 :                                    uint  val ) {
     525           6 :   memcpy( (void *)haddr, &val, sizeof(uint) );
     526           6 : }
     527             : 
     528             : static inline void fd_vm_mem_st_8( ulong haddr,
     529           6 :                                    ulong val ) {
     530           6 :   memcpy( (void *)haddr, &val, sizeof(ulong) );
     531           6 : }
     532             : 
     533             : FD_PROTOTYPES_END
     534             : 
     535             : #endif /* HEADER_fd_src_flamenco_vm_fd_vm_private_h */

Generated by: LCOV version 1.14