LCOV - code coverage report
Current view: top level - flamenco/vm - fd_vm_private.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 254 322 78.9 %
Date: 2024-11-13 11:58:15 Functions: 36 650 5.5 %

          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/context/fd_exec_txn_ctx.h"
      10             : #include "../runtime/fd_runtime.h"
      11             : #include "../features/fd_features.h"
      12             : 
      13             : /* FD_VM_ALIGN_RUST_{} define the alignments for relevant rust types.
      14             :    Alignments are derived with std::mem::align_of::<T>() and are enforced
      15             :    by the VM (with the exception of v1 loader).
      16             : 
      17             :    In our implementation, when calling FD_VM_MEM_HADDR_ST / FD_VM_MEM_HADDR_LD,
      18             :    we need to make sure we're passing the correct alignment based on the Rust
      19             :    type in the corresponding mapping in Agave.
      20             : 
      21             :    FD_VM_ALIGN_RUST_{} has been generated with this Rust code:
      22             :    ```rust
      23             :       pub type Epoch = u64;
      24             :       pub struct Pubkey(pub [u8; 32]);
      25             :       pub struct AccountMeta {
      26             :           pub lamports: u64,
      27             :           pub rent_epoch: Epoch,
      28             :           pub owner: Pubkey,
      29             :           pub executable: bool,
      30             :       }
      31             : 
      32             :       pub struct PodScalar(pub [u8; 32]);
      33             : 
      34             :       fn main() {
      35             :           println!("u8: {}", std::mem::align_of::<u8>());
      36             :           println!("u32: {}", std::mem::align_of::<u32>());
      37             :           println!("u64: {}", std::mem::align_of::<u64>());
      38             :           println!("u128: {}", std::mem::align_of::<u128>());
      39             :           println!("&[u8]: {}", std::mem::align_of::<&[u8]>());
      40             :           println!("AccountMeta: {}", std::mem::align_of::<AccountMeta>());
      41             :           println!("PodScalar: {}", std::mem::align_of::<PodScalar>());
      42             :           println!("Pubkey: {}", std::mem::align_of::<Pubkey>());
      43             :       }
      44             :     ``` */
      45             : 
      46           0 : #define FD_VM_ALIGN_RUST_U8                       (1UL)
      47             : #define FD_VM_ALIGN_RUST_U32                      (4UL)
      48             : #define FD_VM_ALIGN_RUST_I32                      (4UL)
      49             : #define FD_VM_ALIGN_RUST_U64                      (8UL)
      50             : #define FD_VM_ALIGN_RUST_U128                     (16UL)
      51             : #define FD_VM_ALIGN_RUST_SLICE_U8_REF             (8UL)
      52             : #define FD_VM_ALIGN_RUST_POD_U8_ARRAY             (1UL)
      53             : #define FD_VM_ALIGN_RUST_PUBKEY                   (1UL)
      54             : #define FD_VM_ALIGN_RUST_SYSVAR_CLOCK             (8UL)
      55             : #define FD_VM_ALIGN_RUST_SYSVAR_EPOCH_SCHEDULE    (8UL)
      56             : #define FD_VM_ALIGN_RUST_SYSVAR_FEES              (8UL)
      57             : #define FD_VM_ALIGN_RUST_SYSVAR_RENT              (8UL)
      58             : #define FD_VM_ALIGN_RUST_SYSVAR_LAST_RESTART_SLOT (8UL)
      59             : #define FD_VM_ALIGN_RUST_STABLE_INSTRUCTION       (8UL)
      60             : 
      61             : /* fd_vm_vec_t is the in-memory representation of a vector descriptor.
      62             :    Equal in layout to the Rust slice header &[_] and various vector
      63             :    types in the C version of the syscall API. */
      64             : /* FIXME: WHEN IS VADDR NULL AND/OR SZ 0 OKAY? */
      65             : /* FIXME: MOVE FD_VM_RUST_VEC_T FROM SYSCALL/FD_VM_CPI.H HERE TOO? */
      66             : 
      67             : #define FD_VM_VEC_ALIGN FD_VM_ALIGN_RUST_SLICE_U8_REF
      68             : #define FD_VM_VEC_SIZE  (16UL)
      69             : 
      70             : struct __attribute__((packed)) fd_vm_vec {
      71             :   ulong addr; /* FIXME: NAME -> VADDR */
      72             :   ulong len;  /* FIXME: NAME -> SZ */
      73             : };
      74             : 
      75             : typedef struct fd_vm_vec fd_vm_vec_t;
      76             : 
      77             : FD_PROTOTYPES_BEGIN
      78             : 
      79             : /* Log error within the instr_ctx to match Agave/Rust error. */
      80             : 
      81         816 : #define FD_VM_ERR_FOR_LOG_EBPF( vm, err ) (__extension__({                \
      82         816 :     vm->instr_ctx->txn_ctx->exec_err = err;                               \
      83         816 :     vm->instr_ctx->txn_ctx->exec_err_kind = FD_EXECUTOR_ERR_KIND_EBPF;    \
      84         816 :   }))
      85             : 
      86         300 : #define FD_VM_ERR_FOR_LOG_SYSCALL( vm, err ) (__extension__({             \
      87         300 :     vm->instr_ctx->txn_ctx->exec_err = err;                               \
      88         300 :     vm->instr_ctx->txn_ctx->exec_err_kind = FD_EXECUTOR_ERR_KIND_SYSCALL; \
      89         300 :   }))
      90             : 
      91         174 : #define FD_VM_ERR_FOR_LOG_INSTR( vm, err ) (__extension__({               \
      92         174 :     vm->instr_ctx->txn_ctx->exec_err = err;                               \
      93         174 :     vm->instr_ctx->txn_ctx->exec_err_kind = FD_EXECUTOR_ERR_KIND_INSTR;   \
      94         174 :   }))
      95             : 
      96             : /* fd_vm_cu API *******************************************************/
      97             : 
      98             : /* FIXME: CONSIDER MOVING TO FD_VM_SYSCALL.H */
      99             : /* FD_VM_CU_UPDATE charges the vm cost compute units.
     100             : 
     101             :    If the vm does not have more than cost cu available, this will cause
     102             :    the caller to zero out the vm->cu and return with FD_VM_ERR_SIGCOST.
     103             :    This macro is robust.
     104             :    This is meant to be used by syscall implementations and strictly
     105             :    conforms with the vm-syscall ABI interface.
     106             : 
     107             :    Note: in Agave a syscall can return success leaving 0 available CUs.
     108             :    The instruction will fail at the next instruction (e.g., exit).
     109             :    To reproduce the same behavior, we do not return FD_VM_ERR_SIGCOST
     110             :    when cu == 0.
     111             : 
     112             :    FD_VM_CU_MEM_UPDATE charges the vm the equivalent of sz bytes of
     113             :    compute units.  Behavior is otherwise identical to FD_VM_CU_UPDATE.
     114             :    FIXME: THIS API PROBABLY BELONGS IN SYSCALL CPI LAND. */
     115             : 
     116       26619 : #define FD_VM_CU_UPDATE( vm, cost ) (__extension__({ \
     117       26619 :     fd_vm_t * _vm   = (vm);                          \
     118       26619 :     ulong     _cost = (cost);                        \
     119       26619 :     ulong     _cu   = _vm->cu;                       \
     120       26619 :     if( FD_UNLIKELY( _cost>_cu ) ) {                 \
     121         171 :       _vm->cu = 0UL;                                 \
     122         171 :       FD_VM_ERR_FOR_LOG_INSTR( vm, FD_EXECUTOR_INSTR_ERR_COMPUTE_BUDGET_EXCEEDED ); \
     123         171 :       return FD_VM_ERR_SIGCOST;                      \
     124         171 :     }                                                \
     125       26619 :     _vm->cu = _cu - _cost;                           \
     126       26448 :   }))
     127             : 
     128             : /* https://github.com/anza-xyz/agave/blob/5263c9d61f3af060ac995956120bef11c1bbf182/programs/bpf_loader/src/syscalls/mem_ops.rs#L7 */
     129             : #define FD_VM_CU_MEM_OP_UPDATE( vm, sz ) \
     130        6933 :   FD_VM_CU_UPDATE( vm, fd_ulong_max( FD_VM_MEM_OP_BASE_COST, sz / FD_VM_CPI_BYTES_PER_UNIT ) )
     131             : 
     132     1447590 : #define FD_VADDR_TO_REGION( _vaddr ) fd_ulong_min( (_vaddr) >> 32, 5UL )
     133             : 
     134             : /* fd_vm_instr APIs ***************************************************/
     135             : 
     136             : /* FIXME: MIGRATE FD_SBPF_INSTR_T STUFF TO THIS API */
     137             : 
     138             : /* fd_vm_instr returns the SBPF instruction word corresponding to the
     139             :    given fields. */
     140             : 
     141             : FD_FN_CONST static inline ulong
     142             : fd_vm_instr( ulong opcode, /* Assumed valid */
     143             :              ulong dst,    /* Assumed in [0,FD_VM_REG_CNT) */
     144             :              ulong src,    /* Assumed in [0,FD_VM_REG_CNT) */
     145             :              short offset,
     146        7686 :              uint  imm ) {
     147        7686 :   return opcode | (dst<<8) | (src<<12) | (((ulong)(ushort)offset)<<16) | (((ulong)imm)<<32);
     148        7686 : }
     149             : 
     150             : /* fd_vm_instr_* return the SBPF instruction field for the given word.
     151             :    fd_vm_instr_{normal,mem}_* only apply to {normal,mem} opclass
     152             :    instructions. */
     153             : 
     154     5799636 : FD_FN_CONST static inline ulong fd_vm_instr_opcode( ulong instr ) { return   instr      & 255UL;       } /* In [0,256) */
     155     5799636 : FD_FN_CONST static inline ulong fd_vm_instr_dst   ( ulong instr ) { return ((instr>> 8) &  15UL);      } /* In [0,16)  */
     156     5799636 : FD_FN_CONST static inline ulong fd_vm_instr_src   ( ulong instr ) { return ((instr>>12) &  15UL);      } /* In [0,16)  */
     157     5799636 : FD_FN_CONST static inline short fd_vm_instr_offset( ulong instr ) { return (short)(ushort)(instr>>16); }
     158     5881917 : FD_FN_CONST static inline uint  fd_vm_instr_imm   ( ulong instr ) { return (uint)(instr>>32);          }
     159             : 
     160           0 : FD_FN_CONST static inline ulong fd_vm_instr_opclass       ( ulong instr ) { return  instr      & 7UL; } /* In [0,8)  */
     161           0 : FD_FN_CONST static inline ulong fd_vm_instr_normal_opsrc  ( ulong instr ) { return (instr>>3) &  1UL; } /* In [0,2)  */
     162           0 : FD_FN_CONST static inline ulong fd_vm_instr_normal_opmode ( ulong instr ) { return (instr>>4) & 15UL; } /* In [0,16) */
     163           0 : FD_FN_CONST static inline ulong fd_vm_instr_mem_opsize    ( ulong instr ) { return (instr>>3) &  3UL; } /* In [0,4)  */
     164           0 : FD_FN_CONST static inline ulong fd_vm_instr_mem_opaddrmode( ulong instr ) { return (instr>>5) &  7UL; } /* In [0,16) */
     165             : 
     166             : /* fd_vm_mem API ******************************************************/
     167             : 
     168             : /* fd_vm_mem APIs support the fast mapping of virtual address ranges to
     169             :    host address ranges.  Since the SBPF virtual address space consists
     170             :    of 4 consecutive 4GiB regions and the mapable size of each region is
     171             :    less than 4 GiB (as implied by FD_VM_MEM_MAP_REGION_SZ==2^32-1 and
     172             :    that Solana protocol limits are much smaller still), it is impossible
     173             :    for a valid virtual address range to span multiple regions. */
     174             : 
     175             : /* fd_vm_mem_cfg configures the vm's tlb arrays.  Assumes vm is valid
     176             :    and vm already has configured the rodata, stack, heap and input
     177             :    regions.  Returns vm. */
     178             : 
     179             : static inline fd_vm_t *
     180       30276 : fd_vm_mem_cfg( fd_vm_t * vm ) {
     181       30276 :   vm->region_haddr[0] = 0UL;               vm->region_ld_sz[0] = (uint)0UL;             vm->region_st_sz[0] = (uint)0UL;
     182       30276 :   vm->region_haddr[1] = (ulong)vm->rodata; vm->region_ld_sz[1] = (uint)vm->rodata_sz;   vm->region_st_sz[1] = (uint)0UL;
     183       30276 :   vm->region_haddr[2] = (ulong)vm->stack;  vm->region_ld_sz[2] = (uint)FD_VM_STACK_MAX; vm->region_st_sz[2] = (uint)FD_VM_STACK_MAX;
     184       30276 :   vm->region_haddr[3] = (ulong)vm->heap;   vm->region_ld_sz[3] = (uint)vm->heap_max;    vm->region_st_sz[3] = (uint)vm->heap_max;
     185       30276 :   vm->region_haddr[5] = 0UL;               vm->region_ld_sz[5] = (uint)0UL;             vm->region_st_sz[5] = (uint)0UL;
     186       30276 :   if( FD_FEATURE_ACTIVE( vm->instr_ctx->slot_ctx, bpf_account_data_direct_mapping ) || !vm->input_mem_regions_cnt ) {
     187             :     /* When direct mapping is enabled, we don't use these fields because
     188             :        the load and stores are fragmented. */
     189       25581 :     vm->region_haddr[4] = 0UL; 
     190       25581 :     vm->region_ld_sz[4] = 0U; 
     191       25581 :     vm->region_st_sz[4] = 0U;
     192       25581 :   } else {
     193        4695 :     vm->region_haddr[4] = vm->input_mem_regions[0].haddr;  
     194        4695 :     vm->region_ld_sz[4] = vm->input_mem_regions[0].region_sz;    
     195        4695 :     vm->region_st_sz[4] = vm->input_mem_regions[0].region_sz;
     196        4695 :   }
     197       30276 :   return vm;
     198       30276 : }
     199             : 
     200             : /* fd_vm_mem_haddr translates the vaddr range [vaddr,vaddr+sz) (in
     201             :    infinite precision math) into the non-wrapping haddr range
     202             :    [haddr,haddr+sz).  On success, returns haddr and every byte in the
     203             :    haddr range is a valid address.  On failure, returns sentinel and
     204             :    there was at least one byte in the virtual address range that did not
     205             :    have a corresponding byte in the host address range.
     206             : 
     207             :    IMPORTANT SAFETY TIP!  When sz==0, the return value currently is
     208             :    arbitrary.  This is often fine as there should be no
     209             :    actual accesses to a sz==0 region.  However, this also means that
     210             :    testing return for sentinel is insufficient to tell if mapping
     211             :    failed.  That is, assuming sentinel is a location that could never
     212             :    happen on success:
     213             : 
     214             :      sz!=0 and ret!=sentinel -> success
     215             :      sz!=0 and ret==sentinel -> failure
     216             :      sz==0 -> ignore ret, application specific handling
     217             : 
     218             :    With ~O(2) extra fast branchless instructions, the below could be
     219             :    tweaked in the sz==0 case to return NULL or return a non-NULL
     220             :    sentinel value.  What is most optimal practically depends on how
     221             :    empty ranges and NULL vaddr handling is defined in the application.
     222             : 
     223             :    Requires ~O(10) fast branchless assembly instructions with 2 L1 cache
     224             :    hit loads and pretty good ILP.
     225             : 
     226             :    fd_vm_mem_haddr_fast is when the vaddr is for use when it is already
     227             :    known that the vaddr region has a valid mapping.
     228             :    
     229             :    These assumptions don't hold if direct mapping is enabled since input
     230             :    region lookups become O(log(n)). */
     231             : 
     232             : 
     233             : /* fd_vm_get_input_mem_region_idx returns the index into the input memory
     234             :    region array with the largest region offset that is <= the offset that
     235             :    is passed in.  This function makes NO guarantees about the input being
     236             :    a valid input region offset; the caller is responsible for safely handling
     237             :    it. */
     238             : static inline ulong
     239       93225 : fd_vm_get_input_mem_region_idx( fd_vm_t const * vm, ulong offset ) {
     240       93225 :   uint left  = 0U;
     241       93225 :   uint right = vm->input_mem_regions_cnt - 1U;
     242       93225 :   uint mid   = 0U;
     243             : 
     244       99027 :   while( left<right ) {
     245        5802 :     mid = (left+right) / 2U;
     246        5802 :     if( offset>=vm->input_mem_regions[ mid ].vaddr_offset+vm->input_mem_regions[ mid ].region_sz ) {
     247        1917 :       left = mid + 1U;
     248        3885 :     } else {
     249        3885 :       right = mid;
     250        3885 :     }
     251        5802 :   }
     252       93225 :   return left;
     253       93225 : }
     254             : 
     255             : /* fd_vm_find_input_mem_region returns the translated haddr for a given
     256             :    offset into the input region.  If an offset/sz is invalid or if an 
     257             :    illegal write is performed, the sentinel value is returned. If the offset
     258             :    provided is too large, it will choose the upper-most region as the
     259             :    region_idx. However, it will get caught for being too large of an access
     260             :    in the multi-region checks. */
     261             : static inline ulong
     262             : fd_vm_find_input_mem_region( fd_vm_t const * vm, 
     263             :                              ulong           offset,
     264             :                              ulong           sz, 
     265             :                              uchar           write,
     266             :                              ulong           sentinel,
     267       92700 :                              uchar *         is_multi_region ) {
     268             : 
     269             :   /* Binary search to find the correct memory region.  If direct mapping is not
     270             :      enabled, then there is only 1 memory region which spans the input region. */
     271       92700 :   ulong region_idx = fd_vm_get_input_mem_region_idx( vm, offset );
     272             : 
     273       92700 :   ulong bytes_left          = sz;
     274       92700 :   ulong bytes_in_cur_region = fd_ulong_sat_sub( vm->input_mem_regions[ region_idx ].region_sz,
     275       92700 :                                                 fd_ulong_sat_sub( offset, vm->input_mem_regions[ region_idx ].vaddr_offset ) );
     276             : 
     277       92700 :   if( FD_UNLIKELY( write && vm->input_mem_regions[ region_idx ].is_writable==0U ) ) {
     278           3 :     return sentinel; /* Illegal write */
     279           3 :   }
     280             : 
     281       92697 :   ulong start_region_idx = region_idx;
     282             : 
     283       92697 :   *is_multi_region = 0;
     284       92727 :   while( FD_UNLIKELY( bytes_left>bytes_in_cur_region ) ) {
     285         267 :     *is_multi_region = 1;
     286         267 :     FD_LOG_DEBUG(( "Size of access spans multiple memory regions" ));
     287         267 :     if( FD_UNLIKELY( write && vm->input_mem_regions[ region_idx ].is_writable==0U ) ) {
     288           0 :       return sentinel; /* Illegal write */
     289           0 :     }
     290         267 :     bytes_left = fd_ulong_sat_sub( bytes_left, bytes_in_cur_region );
     291             : 
     292         267 :     region_idx += 1U;
     293             : 
     294         267 :     if( FD_UNLIKELY( region_idx==vm->input_mem_regions_cnt ) ) {
     295         237 :       return sentinel; /* Access is too large */
     296         237 :     }
     297          30 :     bytes_in_cur_region = vm->input_mem_regions[ region_idx ].region_sz;
     298          30 :   }
     299             : 
     300       92460 :   ulong adjusted_haddr = vm->input_mem_regions[ start_region_idx ].haddr + offset - vm->input_mem_regions[ start_region_idx ].vaddr_offset;
     301       92460 :   return adjusted_haddr; 
     302       92697 : }
     303             : 
     304             : 
     305             : static inline ulong
     306             : fd_vm_mem_haddr( fd_vm_t const *    vm,
     307             :                  ulong              vaddr,
     308             :                  ulong              sz,
     309             :                  ulong const *      vm_region_haddr, /* indexed [0,6) */
     310             :                  uint  const *      vm_region_sz,    /* indexed [0,6) */
     311             :                  uchar              write,           /* 1 if the access is a write, 0 if it is a read */
     312             :                  ulong              sentinel,
     313     1446081 :                  uchar *            is_multi_region ) {
     314     1446081 :   ulong region = FD_VADDR_TO_REGION( vaddr );
     315     1446081 :   ulong offset = vaddr & 0xffffffffUL;
     316             : 
     317             :   /* Stack memory regions have 4kB unmapped "gaps" in-between each frame (only if direct mapping is disabled).
     318             :     https://github.com/solana-labs/rbpf/blob/b503a1867a9cfa13f93b4d99679a17fe219831de/src/memory_region.rs#L141
     319             :     */
     320     1446081 :   if ( FD_UNLIKELY( region == 2UL && !vm->direct_mapping ) ) {
     321             :     /* If an access starts in a gap region, that is an access violation */
     322      972702 :     if ( !!( vaddr & 0x1000 ) ) {
     323          45 :       return sentinel;
     324          45 :     }
     325             : 
     326             :     /* To account for the fact that we have gaps in the virtual address space but not in the 
     327             :        physical address space, we need to subtract from the offset the size of all the virtual
     328             :        gap frames underneath it.
     329             :        
     330             :        https://github.com/solana-labs/rbpf/blob/b503a1867a9cfa13f93b4d99679a17fe219831de/src/memory_region.rs#L147-L149 */
     331      972657 :     ulong gap_mask = 0xFFFFFFFFFFFFF000;
     332      972657 :     offset = ( ( offset & gap_mask ) >> 1 ) | ( offset & ~gap_mask ); 
     333      972657 :   }
     334             : 
     335     1446036 :   ulong region_sz = (ulong)vm_region_sz[ region ];
     336     1446036 :   ulong sz_max    = region_sz - fd_ulong_min( offset, region_sz );
     337             : 
     338     1446036 :   if( region==4UL ) {
     339       92700 :     return fd_vm_find_input_mem_region( vm, offset, sz, write, sentinel, is_multi_region );
     340       92700 :   }
     341             :   
     342             : # ifdef FD_VM_INTERP_MEM_TRACING_ENABLED
     343             :   if ( FD_LIKELY( sz<=sz_max ) ) {
     344             :     fd_vm_trace_event_mem( vm->trace, write, vaddr, sz, vm_region_haddr[ region ] + offset );
     345             :   }
     346             : # endif
     347     1353336 :   return fd_ulong_if( sz<=sz_max, vm_region_haddr[ region ] + offset, sentinel );
     348     1446036 : }
     349             : 
     350             : FD_FN_PURE static inline ulong
     351             : fd_vm_mem_haddr_fast( fd_vm_t const * vm, 
     352             :                       ulong           vaddr,
     353         447 :                       ulong   const * vm_region_haddr ) { /* indexed [0,6) */
     354         447 :   uchar is_multi = 0;
     355         447 :   ulong region   = FD_VADDR_TO_REGION( vaddr );
     356         447 :   ulong offset   = vaddr & 0xffffffffUL;
     357         447 :   if( FD_UNLIKELY( region==4UL ) ) {
     358           0 :     return fd_vm_find_input_mem_region( vm, offset, 1UL, 0, 0UL, &is_multi );
     359           0 :   }
     360         447 :   return vm_region_haddr[ region ] + offset;
     361         447 : }
     362             : 
     363             : /* fd_vm_mem_ld_N loads N bytes from the host address location haddr,
     364             :    zero extends it to a ulong and returns the ulong.  haddr need not be
     365             :    aligned.  fd_vm_mem_ld_multi handles the case where the load spans 
     366             :    multiple input memory regions. */
     367             : 
     368          24 : static inline void fd_vm_mem_ld_multi( fd_vm_t const * vm, uint sz, ulong vaddr, ulong haddr, uchar * dst ) {
     369             : 
     370          24 :   ulong offset              = vaddr & 0xffffffffUL;
     371          24 :   ulong region_idx          = fd_vm_get_input_mem_region_idx( vm, offset );
     372          24 :   uint  bytes_in_cur_region = fd_uint_sat_sub( vm->input_mem_regions[ region_idx ].region_sz,
     373          24 :                                               (uint)fd_ulong_sat_sub( offset, vm->input_mem_regions[ region_idx ].vaddr_offset ) );
     374             : 
     375         132 :   while( sz-- ) {
     376         108 :     if( !bytes_in_cur_region ) {
     377          30 :       region_idx++;
     378          30 :       bytes_in_cur_region = fd_uint_sat_sub( vm->input_mem_regions[ region_idx ].region_sz,
     379          30 :                                              (uint)fd_ulong_sat_sub( offset, vm->input_mem_regions[ region_idx ].vaddr_offset ) );
     380          30 :       haddr               = vm->input_mem_regions[ region_idx ].haddr;
     381          30 :     }
     382             : 
     383         108 :     *dst++ = *(uchar *)haddr++;
     384         108 :     bytes_in_cur_region--;
     385         108 :   }
     386          24 : }
     387             : 
     388       96990 : FD_FN_PURE static inline ulong fd_vm_mem_ld_1( ulong haddr ) { 
     389       96990 :   return (ulong)*(uchar const *)haddr; 
     390       96990 : }
     391             : 
     392        1956 : FD_FN_PURE static inline ulong fd_vm_mem_ld_2( fd_vm_t const * vm, ulong vaddr, ulong haddr, uint is_multi_region ) { 
     393        1956 :   ushort t; 
     394        1956 :   if( FD_LIKELY( !is_multi_region ) ) {
     395        1950 :     memcpy( &t, (void const *)haddr, sizeof(ushort) ); 
     396        1950 :   } else {
     397           6 :     fd_vm_mem_ld_multi( vm, 2U, vaddr, haddr, (uchar *)&t );
     398           6 :   }
     399        1956 :   return (ulong)t;
     400        1956 : }
     401             : 
     402       19479 : FD_FN_PURE static inline ulong fd_vm_mem_ld_4( fd_vm_t const * vm, ulong vaddr, ulong haddr, uint is_multi_region ) {
     403       19479 :   uint t; 
     404       19479 :   if( FD_LIKELY( !is_multi_region ) ) {
     405       19467 :     memcpy( &t, (void const *)haddr, sizeof(uint) ); 
     406       19467 :   } else {
     407          12 :     fd_vm_mem_ld_multi( vm, 4U, vaddr, haddr, (uchar *)&t );
     408          12 :   }
     409       19479 :   return (ulong)t;
     410       19479 : }
     411             : 
     412      631647 : FD_FN_PURE static inline ulong fd_vm_mem_ld_8( fd_vm_t const * vm, ulong vaddr, ulong haddr, uint is_multi_region ) {
     413      631647 :   ulong t; 
     414      631647 :   if( FD_LIKELY( !is_multi_region ) ) {
     415      631641 :     memcpy( &t, (void const *)haddr, sizeof(ulong) ); 
     416      631641 :   } else {
     417           6 :     fd_vm_mem_ld_multi( vm, 8U, vaddr, haddr, (uchar *)&t );
     418           6 :   }
     419      631647 :   return t;
     420      631647 : }
     421             : 
     422             : /* fd_vm_mem_st_N stores val in little endian order to the host address
     423             :    location haddr.  haddr need not be aligned. fd_vm_mem_st_multi handles
     424             :    the case where the store spans multiple input memory regions. */
     425             : 
     426           0 : static inline void fd_vm_mem_st_multi( fd_vm_t const * vm, uint sz, ulong vaddr, ulong haddr, uchar * src ) {
     427           0 :   ulong   offset              = vaddr & 0xffffffffUL;
     428           0 :   ulong   region_idx          = fd_vm_get_input_mem_region_idx( vm, offset );
     429           0 :   ulong   bytes_in_cur_region = fd_uint_sat_sub( vm->input_mem_regions[ region_idx ].region_sz,
     430           0 :                                                  (uint)fd_ulong_sat_sub( offset, vm->input_mem_regions[ region_idx ].vaddr_offset ) );
     431           0 :   uchar * dst                 = (uchar*)haddr;
     432             : 
     433           0 :   while( sz-- ) {
     434           0 :     if( !bytes_in_cur_region ) {
     435           0 :       region_idx++;
     436           0 :       bytes_in_cur_region = fd_uint_sat_sub( vm->input_mem_regions[ region_idx ].region_sz,
     437           0 :                                              (uint)fd_ulong_sat_sub( offset, vm->input_mem_regions[ region_idx ].vaddr_offset ) );
     438           0 :       dst                 = (uchar *)vm->input_mem_regions[ region_idx ].haddr;
     439           0 :     }
     440             : 
     441           0 :     *dst++ = *src++;
     442           0 :     bytes_in_cur_region--;
     443           0 :   }
     444           0 : }
     445             : 
     446       65115 : static inline void fd_vm_mem_st_1( ulong haddr, uchar val ) { 
     447       65115 :   *(uchar *)haddr = val;
     448       65115 : }
     449             : 
     450             : static inline void fd_vm_mem_st_2( fd_vm_t const * vm,
     451             :                                    ulong           vaddr,
     452             :                                    ulong           haddr, 
     453             :                                    ushort          val, 
     454        2034 :                                    uint            is_multi_region ) { 
     455        2034 :   if( FD_LIKELY( !is_multi_region ) ) {
     456        2034 :     memcpy( (void *)haddr, &val, sizeof(ushort) ); 
     457        2034 :   } else {
     458           0 :     fd_vm_mem_st_multi( vm, 2U, vaddr, haddr, (uchar *)&val );
     459           0 :   }
     460        2034 : }
     461             : 
     462             : static inline void fd_vm_mem_st_4( fd_vm_t const * vm,
     463             :                                    ulong           vaddr,
     464             :                                    ulong           haddr, 
     465             :                                    uint            val, 
     466       14922 :                                    uint            is_multi_region ) { 
     467       14922 :   if( FD_LIKELY( !is_multi_region ) ) {
     468       14922 :     memcpy( (void *)haddr, &val, sizeof(uint)   ); 
     469       14922 :   } else {
     470           0 :     fd_vm_mem_st_multi( vm, 4U, vaddr, haddr, (uchar *)&val );
     471           0 :   }
     472       14922 : }
     473             : 
     474             : static inline void fd_vm_mem_st_8( fd_vm_t const * vm,
     475             :                                    ulong           vaddr,
     476             :                                    ulong           haddr,
     477             :                                    ulong           val,
     478      540246 :                                    uint            is_multi_region ) { 
     479      540246 :   if( FD_LIKELY( !is_multi_region ) ) {
     480      540246 :     memcpy( (void *)haddr, &val, sizeof(ulong)  ); 
     481      540246 :   } else {
     482           0 :     fd_vm_mem_st_multi( vm, 8U, vaddr, haddr, (uchar *)&val );
     483           0 :   }
     484      540246 : }
     485             : 
     486             : /* FIXME: CONSIDER MOVING TO FD_VM_SYSCALL.H */
     487             : /* FD_VM_MEM_HADDR_LD returns a read only pointer to the first byte
     488             :    in the host address space corresponding to vm's virtual address range
     489             :    [vaddr,vaddr+sz).  If the vm has check_align enabled, the vaddr
     490             :    should be aligned to align and the returned pointer will be similarly
     491             :    aligned.  Align is assumed to be a power of two <= 8 (FIXME: CHECK
     492             :    THIS LIMIT).
     493             : 
     494             :    If the virtual address range cannot be mapped to the host address
     495             :    space completely and/or (when applicable) vaddr is not appropriately
     496             :    aligned, this will cause the caller to return FD_VM_ERR_SIGSEGV.
     497             :    This macro is robust.  This is meant to be used by syscall
     498             :    implementations and strictly conforms with the vm-syscall ABI
     499             :    interface.
     500             : 
     501             :    FD_VM_MEM_HADDR_ST returns a read-write pointer but is otherwise
     502             :    identical to FD_VM_MEM_HADDR_LD.
     503             : 
     504             :    FD_VM_MEM_HADDR_LD_FAST and FD_VM_HADDR_ST_FAST are for use when the
     505             :    corresponding vaddr region it known to correctly resolve (e.g.  a
     506             :    syscall has already done preflight checks on them).
     507             : 
     508             :    These macros intentionally don't support multi region loads/stores.
     509             :    The load/store macros are used by vm syscalls and mirror the use
     510             :    of translate_slice{_mut}. However, this check does not allow for 
     511             :    multi region accesses. So if there is an attempt at a multi region
     512             :    translation, an error will be returned. 
     513             : 
     514             :    FD_VM_MEM_HADDR_ST_UNCHECKED has all of the checks of a load or a 
     515             :    store, but intentionally omits the is_writable checks for the 
     516             :    input region that are done during memory translation. */
     517             : 
     518       43185 : #define FD_VM_MEM_HADDR_LD( vm, vaddr, align, sz ) (__extension__({                                         \
     519       43185 :     fd_vm_t const * _vm       = (vm);                                                                       \
     520       43185 :     uchar           _is_multi = 0;                                                                          \
     521       43185 :     ulong           _vaddr    = (vaddr);                                                                    \
     522       43185 :     ulong           _haddr    = fd_vm_mem_haddr( vm, _vaddr, (sz), _vm->region_haddr, _vm->region_ld_sz, 0, 0UL, &_is_multi ); \
     523       43185 :     int             _sigbus   = fd_vm_is_check_align_enabled( vm ) & (!fd_ulong_is_aligned( _haddr, (align) )); \
     524       43185 :     if ( FD_UNLIKELY( sz > LONG_MAX ) ) {                                                                   \
     525           0 :       FD_VM_ERR_FOR_LOG_SYSCALL( _vm, FD_VM_ERR_SYSCALL_INVALID_LENGTH );                                   \
     526           0 :       return FD_VM_ERR_SIGSEGV;                                                                             \
     527           0 :     }                                                                                                       \
     528       43185 :     if( FD_UNLIKELY( (!_haddr) | _is_multi) ) {                                                             \
     529         168 :       FD_VM_ERR_FOR_LOG_EBPF( _vm, FD_VM_ERR_EBPF_ACCESS_VIOLATION );                                       \
     530         168 :       return FD_VM_ERR_SIGSEGV;                                                                             \
     531         168 :     }                                                                                                       \
     532       43185 :     if ( FD_UNLIKELY( _sigbus ) ) {                                                                         \
     533          24 :       FD_VM_ERR_FOR_LOG_SYSCALL( _vm, FD_VM_ERR_SYSCALL_UNALIGNED_POINTER );                                \
     534          24 :       return FD_VM_ERR_SIGSEGV;                                                                             \
     535          24 :     }                                                                                                       \
     536       43017 :     (void const *)_haddr;                                                                                   \
     537       42993 :   }))
     538             : 
     539        5646 : #define FD_VM_MEM_HADDR_LD_UNCHECKED( vm, vaddr, align, sz ) (__extension__({                               \
     540        5646 :     fd_vm_t const * _vm       = (vm);                                                                       \
     541        5646 :     uchar           _is_multi = 0;                                                                          \
     542        5646 :     ulong           _vaddr    = (vaddr);                                                                    \
     543        5646 :     ulong           _haddr    = fd_vm_mem_haddr( vm, _vaddr, (sz), _vm->region_haddr, _vm->region_ld_sz, 0, 0UL, &_is_multi ); \
     544        5646 :     (void const *)_haddr;                                                                                   \
     545        5646 :   }))
     546             : 
     547       24561 : #define FD_VM_MEM_HADDR_ST( vm, vaddr, align, sz ) (__extension__({                                         \
     548       24561 :     fd_vm_t const * _vm       = (vm);                                                                       \
     549       24561 :     uchar           _is_multi = 0;                                                                          \
     550       24561 :     ulong           _vaddr    = (vaddr);                                                                    \
     551       24561 :     ulong           _haddr    = fd_vm_mem_haddr( vm, _vaddr, (sz), _vm->region_haddr, _vm->region_st_sz, 1, 0UL, &_is_multi ); \
     552       24561 :     int             _sigbus   = fd_vm_is_check_align_enabled( vm ) & (!fd_ulong_is_aligned( _haddr, (align) )); \
     553       24561 :     if ( FD_UNLIKELY( sz > LONG_MAX ) ) {                                                                   \
     554           0 :       FD_VM_ERR_FOR_LOG_SYSCALL( _vm, FD_VM_ERR_SYSCALL_INVALID_LENGTH );                                   \
     555           0 :       return FD_VM_ERR_SIGSEGV;                                                                             \
     556           0 :     }                                                                                                       \
     557       24561 :     if( FD_UNLIKELY( (!_haddr) | _is_multi) ) {                                                             \
     558         102 :       FD_VM_ERR_FOR_LOG_EBPF( _vm, FD_VM_ERR_EBPF_ACCESS_VIOLATION );                                       \
     559         102 :       return FD_VM_ERR_SIGSEGV;                                                                             \
     560         102 :     }                                                                                                       \
     561       24561 :     if ( FD_UNLIKELY( _sigbus ) ) {                                                                         \
     562           0 :       FD_VM_ERR_FOR_LOG_SYSCALL( _vm, FD_VM_ERR_SYSCALL_UNALIGNED_POINTER );                                \
     563           0 :       return FD_VM_ERR_SIGSEGV;                                                                             \
     564           0 :     }                                                                                                       \
     565       24459 :     (void *)_haddr;                                                                                         \
     566       24459 :   }))
     567             : 
     568             : #define FD_VM_MEM_HADDR_ST_UNCHECKED( vm, vaddr, align, sz ) (__extension__({                               \
     569             :     fd_vm_t const * _vm       = (vm);                                                                       \
     570             :     uchar           _is_multi = 0;                                                                          \
     571             :     ulong           _vaddr    = (vaddr);                                                                    \
     572             :     ulong           _haddr    = fd_vm_mem_haddr( vm, _vaddr, (sz), _vm->region_haddr, _vm->region_st_sz, 1, 0UL, &_is_multi ); \
     573             :     (void const *)_haddr;                                                                                   \
     574             :   }))
     575             : 
     576           0 : #define FD_VM_MEM_HADDR_ST_WRITE_UNCHECKED( vm, vaddr, align, sz ) (__extension__({                         \
     577           0 :     fd_vm_t const * _vm       = (vm);                                                                       \
     578           0 :     uchar           _is_multi = 0;                                                                          \
     579           0 :     ulong           _vaddr    = (vaddr);                                                                    \
     580           0 :     ulong           _haddr    = fd_vm_mem_haddr( vm, _vaddr, (sz), _vm->region_haddr, _vm->region_st_sz, 0, 0UL, &_is_multi ); \
     581           0 :     int             _sigbus   = fd_vm_is_check_align_enabled( vm ) & (!fd_ulong_is_aligned( _haddr, (align) )); \
     582           0 :     if ( FD_UNLIKELY( sz > LONG_MAX ) ) {                                                                   \
     583           0 :       FD_VM_ERR_FOR_LOG_SYSCALL( _vm, FD_VM_ERR_SYSCALL_INVALID_LENGTH );                                   \
     584           0 :       return FD_VM_ERR_SIGSEGV;                                                                             \
     585           0 :     }                                                                                                       \
     586           0 :     if( FD_UNLIKELY( (!_haddr) | _is_multi ) ) {                                                            \
     587           0 :       FD_VM_ERR_FOR_LOG_EBPF( _vm, FD_VM_ERR_EBPF_ACCESS_VIOLATION );                                       \
     588           0 :       return FD_VM_ERR_SIGSEGV;                                                                             \
     589           0 :     }                                                                                                       \
     590           0 :     if ( FD_UNLIKELY( _sigbus ) ) {                                                                         \
     591           0 :       FD_VM_ERR_FOR_LOG_SYSCALL( _vm, FD_VM_ERR_SYSCALL_UNALIGNED_POINTER );                                \
     592           0 :       return FD_VM_ERR_SIGSEGV;                                                                             \
     593           0 :     }                                                                                                       \
     594           0 :     (void *)_haddr;                                                                                         \
     595           0 :   }))
     596             : 
     597             : 
     598         147 : #define FD_VM_MEM_HADDR_LD_FAST( vm, vaddr ) ((void const *)fd_vm_mem_haddr_fast( (vm), (vaddr), (vm)->region_haddr ))
     599         300 : #define FD_VM_MEM_HADDR_ST_FAST( vm, vaddr ) ((void       *)fd_vm_mem_haddr_fast( (vm), (vaddr), (vm)->region_haddr ))
     600             : 
     601             : /* FD_VM_MEM_HADDR_AND_REGION_IDX_FROM_INPUT_REGION_UNCHECKED simply converts a vaddr within the input memory region
     602             :    into an haddr. The macro assumes that the caller already checked that the vaddr exists within the
     603             :    input region (region==4UL) and sets the region_idx and haddr. */
     604         486 : #define FD_VM_MEM_HADDR_AND_REGION_IDX_FROM_INPUT_REGION_UNCHECKED( _vm, _offset, _out_region_idx, _out_haddr ) (__extension__({                \
     605         486 :   _out_region_idx = fd_vm_get_input_mem_region_idx( _vm, _offset );                                                                             \
     606         486 :   _out_haddr      = (uchar*)_vm->input_mem_regions[ _out_region_idx ].haddr + _offset - _vm->input_mem_regions[ _out_region_idx ].vaddr_offset; \
     607         486 : }))
     608             : 
     609             : /* FD_VM_MEM_SLICE_HADDR_[LD, ST] macros return an arbitrary value if sz == 0. This is because
     610             :    Agave's translate_slice function returns an empty array if the sz == 0.
     611             : 
     612             :    Users of this macro should be aware that they should never access the returned value if sz==0.
     613             : 
     614             :    https://github.com/solana-labs/solana/blob/767d24e5c10123c079e656cdcf9aeb8a5dae17db/programs/bpf_loader/src/syscalls/mod.rs#L560 
     615             : 
     616             :    LONG_MAX check: https://github.com/anza-xyz/agave/blob/dc4b9dcbbf859ff48f40d00db824bde063fdafcc/programs/bpf_loader/src/syscalls/mod.rs#L580
     617             :    Technically, the check in Agave is against
     618             :    "pointer-sized signed integer type ... The size of this primitive is
     619             :     how many bytes it takes to reference any location in memory. For
     620             :     example, on a 32 bit target, this is 4 bytes and on a 64 bit target,
     621             :     this is 8 bytes."
     622             :    Realistically, given the amount of memory that a validator consumes,
     623             :    no one is going to be running on a 32 bit target. So, we don't bother
     624             :    with conditionally compiling in an INT_MAX check. We just assume
     625             :    LONG_MAX. */
     626       34551 : #define FD_VM_MEM_SLICE_HADDR_LD( vm, vaddr, align, sz ) (__extension__({                                       \
     627       34551 :     if ( FD_UNLIKELY( sz > LONG_MAX ) ) {                                                                       \
     628           9 :       FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_ERR_SYSCALL_INVALID_LENGTH );                                        \
     629           9 :       return FD_VM_ERR_INVAL;                                                                                   \
     630           9 :     }                                                                                                           \
     631       34551 :     void const * haddr = 0UL;                                                                                   \
     632       34542 :     if ( FD_LIKELY( (ulong)sz > 0UL ) ) {                                                                       \
     633      102942 :       haddr = FD_VM_MEM_HADDR_LD( vm, vaddr, align, sz );                                                       \
     634      102942 :     }                                                                                                           \
     635       34542 :     haddr;                                                                                                      \
     636       34467 : }))
     637             : 
     638             : 
     639             : /* This is the same as the above function but passes in a size of 1 to support
     640             :    loads with no size bounding support. */
     641          27 : #define FD_VM_MEM_SLICE_HADDR_LD_SZ_UNCHECKED( vm, vaddr, align ) (__extension__({                              \
     642          27 :     if ( FD_UNLIKELY( sz > LONG_MAX ) ) {                                                                       \
     643           0 :       FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_ERR_SYSCALL_INVALID_LENGTH );                                        \
     644           0 :       return FD_VM_ERR_INVAL;                                                                                   \
     645           0 :     }                                                                                                           \
     646          27 :     void const * haddr = 0UL;                                                                                   \
     647          27 :     if ( FD_LIKELY( (ulong)sz > 0UL ) ) {                                                                       \
     648          81 :       haddr = FD_VM_MEM_HADDR_LD( vm, vaddr, align, 1UL );                                                      \
     649          81 :     }                                                                                                           \
     650          27 :     haddr;                                                                                                      \
     651          27 : }))
     652             : 
     653        9891 : #define FD_VM_MEM_SLICE_HADDR_ST( vm, vaddr, align, sz ) (__extension__({                                       \
     654        9891 :     if ( FD_UNLIKELY( sz > LONG_MAX ) ) {                                                                       \
     655           0 :       FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_ERR_SYSCALL_INVALID_LENGTH );                                        \
     656           0 :       return FD_VM_ERR_INVAL;                                                                                   \
     657           0 :     }                                                                                                           \
     658        9891 :     void * haddr = 0UL;                                                                                         \
     659        9891 :     if ( FD_LIKELY( (ulong)sz > 0UL ) ) {                                                                       \
     660       29631 :       haddr = FD_VM_MEM_HADDR_ST( vm, vaddr, align, sz );                                                       \
     661       29631 :     }                                                                                                           \
     662        9891 :     haddr;                                                                                                      \
     663        9870 : }))
     664             : 
     665             : /* FIXME: use overlap logic from runtime? */
     666        6000 : #define FD_VM_MEM_CHECK_NON_OVERLAPPING( vm, vaddr0, sz0, vaddr1, sz1 ) do {                                    \
     667        6000 :   if( FD_UNLIKELY( ((vaddr0> vaddr1) && ((vaddr0-vaddr1)<sz1)) ||                                               \
     668        6000 :                    ((vaddr1>=vaddr0) && ((vaddr1-vaddr0)<sz0)) ) ) {                                            \
     669          75 :     FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_ERR_SYSCALL_COPY_OVERLAPPING );                                        \
     670          75 :     return FD_VM_ERR_MEM_OVERLAP;                                                                               \
     671          75 :   }                                                                                                             \
     672        6000 : } while(0)
     673             : 
     674             : FD_PROTOTYPES_END
     675             : 
     676             : #endif /* HEADER_fd_src_flamenco_vm_fd_vm_private_h */

Generated by: LCOV version 1.14