LCOV - code coverage report
Current view: top level - flamenco/vm/syscall - fd_vm_syscall_macros.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 104 141 73.8 %
Date: 2025-01-08 12:08:44 Functions: 7 16 43.8 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_vm_syscall_fd_vm_syscall_macros_h
       2             : #define HEADER_fd_src_flamenco_vm_syscall_fd_vm_syscall_macros_h
       3             : #include "../fd_vm_private.h"
       4             : 
       5             : /* fd_vm_cu API *******************************************************/
       6             : 
       7             : /* FD_VM_CU_UPDATE charges the vm cost compute units.
       8             : 
       9             :    If the vm does not have more than cost cu available, this will cause
      10             :    the caller to zero out the vm->cu and return with FD_VM_SYSCALL_ERR_COMPUTE_BUDGET_EXCEEDED.
      11             :    This macro is robust.
      12             :    This is meant to be used by syscall implementations and strictly
      13             :    conforms with the vm-syscall ABI interface.
      14             : 
      15             :    Note: in Agave a syscall can return success leaving 0 available CUs.
      16             :    The instruction will fail at the next instruction (e.g., exit).
      17             :    To reproduce the same behavior, we do not return FD_VM_SYSCALL_ERR_COMPUTE_BUDGET_EXCEEDED
      18             :    when cu == 0.
      19             : 
      20             :    FD_VM_CU_MEM_UPDATE charges the vm the equivalent of sz bytes of
      21             :    compute units.  Behavior is otherwise identical to FD_VM_CU_UPDATE.
      22             :    FIXME: THIS API PROBABLY BELONGS IN SYSCALL CPI LAND. */
      23             : 
      24       36261 : #define FD_VM_CU_UPDATE( vm, cost ) (__extension__({ \
      25       36261 :     fd_vm_t * _vm   = (vm);                          \
      26       36261 :     ulong     _cost = (cost);                        \
      27       36261 :     ulong     _cu   = _vm->cu;                       \
      28       36261 :     if( FD_UNLIKELY( _cost>_cu ) ) {                 \
      29         492 :       _vm->cu = 0UL;                                 \
      30         492 :       FD_VM_ERR_FOR_LOG_INSTR( vm, FD_EXECUTOR_INSTR_ERR_COMPUTE_BUDGET_EXCEEDED ); \
      31         492 :       return FD_VM_SYSCALL_ERR_COMPUTE_BUDGET_EXCEEDED; \
      32         492 :     }                                                \
      33       36261 :     _vm->cu = _cu - _cost;                           \
      34       35769 :   }))
      35             : 
      36             : /* https://github.com/anza-xyz/agave/blob/5263c9d61f3af060ac995956120bef11c1bbf182/programs/bpf_loader/src/syscalls/mem_ops.rs#L7 */
      37             : #define FD_VM_CU_MEM_OP_UPDATE( vm, sz ) \
      38        6972 :   FD_VM_CU_UPDATE( vm, fd_ulong_max( FD_VM_MEM_OP_BASE_COST, sz / FD_VM_CPI_BYTES_PER_UNIT ) )
      39             : 
      40             : 
      41             : /* fd_vm_mem API *****************************************************/
      42             : 
      43             : /* FD_VM_MEM_HADDR_LD returns a read only pointer to the first byte
      44             :    in the host address space corresponding to vm's virtual address range
      45             :    [vaddr,vaddr+sz).  If the vm has check_align enabled, the vaddr
      46             :    should be aligned to align and the returned pointer will be similarly
      47             :    aligned.  Align is assumed to be a power of two <= 8 (FIXME: CHECK
      48             :    THIS LIMIT).
      49             : 
      50             :    If the virtual address range cannot be mapped to the host address
      51             :    space completely and/or (when applicable) vaddr is not appropriately
      52             :    aligned, this will cause the caller to return FD_VM_SYSCALL_ERR_SEGFAULT.
      53             :    This macro is robust.  This is meant to be used by syscall
      54             :    implementations and strictly conforms with the vm-syscall ABI
      55             :    interface.
      56             : 
      57             :    FD_VM_MEM_HADDR_ST returns a read-write pointer but is otherwise
      58             :    identical to FD_VM_MEM_HADDR_LD.
      59             : 
      60             :    FD_VM_MEM_HADDR_LD_FAST and FD_VM_HADDR_ST_FAST are for use when the
      61             :    corresponding vaddr region it known to correctly resolve (e.g.  a
      62             :    syscall has already done preflight checks on them).
      63             : 
      64             :    These macros intentionally don't support multi region loads/stores.
      65             :    The load/store macros are used by vm syscalls and mirror the use
      66             :    of translate_slice{_mut}. However, this check does not allow for 
      67             :    multi region accesses. So if there is an attempt at a multi region
      68             :    translation, an error will be returned. 
      69             : 
      70             :    FD_VM_MEM_HADDR_ST_UNCHECKED has all of the checks of a load or a 
      71             :    store, but intentionally omits the is_writable checks for the 
      72             :    input region that are done during memory translation. 
      73             :    
      74             :    FD_VM_MEM_HADDR_ST_NO_SZ_CHECK does all of the checks of a load,
      75             :    except for a check on the validity of the size of a load. It only
      76             :    checks that the specific vaddr that is being translated is valid. */
      77             : 
      78       39945 : #define FD_VM_MEM_HADDR_LD( vm, vaddr, align, sz ) (__extension__({                                         \
      79       39945 :     fd_vm_t const * _vm       = (vm);                                                                       \
      80       39945 :     uchar           _is_multi = 0;                                                                          \
      81       39945 :     ulong           _vaddr    = (vaddr);                                                                    \
      82       39945 :     ulong           _haddr    = fd_vm_mem_haddr( vm, _vaddr, (sz), _vm->region_haddr, _vm->region_ld_sz, 0, 0UL, &_is_multi ); \
      83       39945 :     int             _sigbus   = fd_vm_is_check_align_enabled( vm ) & (!fd_ulong_is_aligned( _haddr, (align) )); \
      84       39945 :     if ( FD_UNLIKELY( sz > LONG_MAX ) ) {                                                                   \
      85           0 :       FD_VM_ERR_FOR_LOG_SYSCALL( _vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );                                   \
      86           0 :       return FD_VM_SYSCALL_ERR_SEGFAULT;                                                                             \
      87           0 :     }                                                                                                       \
      88       39945 :     if( FD_UNLIKELY( (!_haddr) | _is_multi) ) {                                                             \
      89         195 :       FD_VM_ERR_FOR_LOG_EBPF( _vm, FD_VM_ERR_EBPF_ACCESS_VIOLATION );                                       \
      90         195 :       return FD_VM_SYSCALL_ERR_SEGFAULT;                                                                             \
      91         195 :     }                                                                                                       \
      92       39945 :     if ( FD_UNLIKELY( _sigbus ) ) {                                                                         \
      93          24 :       FD_VM_ERR_FOR_LOG_SYSCALL( _vm, FD_VM_SYSCALL_ERR_UNALIGNED_POINTER );                                \
      94          24 :       return FD_VM_SYSCALL_ERR_SEGFAULT;                                                                             \
      95          24 :     }                                                                                                       \
      96       39750 :     (void const *)_haddr;                                                                                   \
      97       39726 :   }))
      98             : 
      99             : #define FD_VM_MEM_HADDR_LD_UNCHECKED( vm, vaddr, align, sz ) (__extension__({                               \
     100             :     fd_vm_t const * _vm       = (vm);                                                                       \
     101             :     uchar           _is_multi = 0;                                                                          \
     102             :     ulong           _vaddr    = (vaddr);                                                                    \
     103             :     ulong           _haddr    = fd_vm_mem_haddr( vm, _vaddr, (sz), _vm->region_haddr, _vm->region_ld_sz, 0, 0UL, &_is_multi ); \
     104             :     (void const *)_haddr;                                                                                   \
     105             :   }))
     106             : 
     107             : 
     108         141 : #define FD_VM_MEM_HADDR_LD_NO_SZ_CHECK( vm, vaddr, align ) (__extension__({ \
     109         141 :   FD_VM_MEM_HADDR_LD( vm, vaddr, align, 1UL );                              \
     110         141 :   }))
     111             : 
     112             : static inline void *
     113       25866 : FD_VM_MEM_HADDR_ST_( fd_vm_t const *vm, ulong vaddr, ulong align, ulong sz, int *err ) {
     114       25866 :   fd_vm_t const * _vm       = (vm);
     115       25866 :   uchar           _is_multi = 0;
     116       25866 :   ulong           _vaddr    = (vaddr);
     117       25866 :   ulong           _haddr    = fd_vm_mem_haddr( vm, _vaddr, (sz), _vm->region_haddr, _vm->region_st_sz, 1, 0UL, &_is_multi );
     118       25866 :   int             _sigbus   = fd_vm_is_check_align_enabled( vm ) & (!fd_ulong_is_aligned( _haddr, (align) ));
     119       25866 :   if ( FD_UNLIKELY( sz > LONG_MAX ) ) {
     120           0 :     FD_VM_ERR_FOR_LOG_SYSCALL( _vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );
     121           0 :     *err = FD_VM_SYSCALL_ERR_SEGFAULT;
     122           0 :     return 0;
     123           0 :   }
     124       25866 :   if( FD_UNLIKELY( (!_haddr) | _is_multi) ) {
     125         507 :     FD_VM_ERR_FOR_LOG_EBPF( _vm, FD_VM_ERR_EBPF_ACCESS_VIOLATION );
     126         507 :     *err = FD_VM_SYSCALL_ERR_SEGFAULT;
     127         507 :     return 0;
     128         507 :   }
     129       25359 :   if ( FD_UNLIKELY( _sigbus ) ) {
     130           0 :     FD_VM_ERR_FOR_LOG_SYSCALL( _vm, FD_VM_SYSCALL_ERR_UNALIGNED_POINTER );
     131           0 :     *err = FD_VM_SYSCALL_ERR_SEGFAULT;
     132           0 :     return 0;
     133           0 :   }
     134       25359 :   return (void *)_haddr;
     135       25359 : }
     136             : 
     137       25287 : #define FD_VM_MEM_HADDR_ST( vm, vaddr, align, sz ) (__extension__({                                         \
     138       25287 :     int _err = 0;                                                                                           \
     139       25287 :     void * ret = FD_VM_MEM_HADDR_ST_( vm, vaddr, align, sz, &_err );                                        \
     140       25287 :     if ( FD_UNLIKELY( 0 != _err ))                                                                          \
     141       25287 :       return _err;                                                                                          \
     142       25287 :     ret;                                                                                                    \
     143       24825 : }))
     144             : 
     145             : #define FD_VM_MEM_HADDR_ST_UNCHECKED( vm, vaddr, align, sz ) (__extension__({                               \
     146             :     fd_vm_t const * _vm       = (vm);                                                                       \
     147             :     uchar           _is_multi = 0;                                                                          \
     148             :     ulong           _vaddr    = (vaddr);                                                                    \
     149             :     ulong           _haddr    = fd_vm_mem_haddr( vm, _vaddr, (sz), _vm->region_haddr, _vm->region_st_sz, 1, 0UL, &_is_multi ); \
     150             :     (void const *)_haddr;                                                                                   \
     151             :   }))
     152             : 
     153           0 : #define FD_VM_MEM_HADDR_ST_WRITE_UNCHECKED( vm, vaddr, align, sz ) (__extension__({                         \
     154           0 :     fd_vm_t const * _vm       = (vm);                                                                       \
     155           0 :     uchar           _is_multi = 0;                                                                          \
     156           0 :     ulong           _vaddr    = (vaddr);                                                                    \
     157           0 :     ulong           _haddr    = fd_vm_mem_haddr( vm, _vaddr, (sz), _vm->region_haddr, _vm->region_st_sz, 0, 0UL, &_is_multi ); \
     158           0 :     int             _sigbus   = fd_vm_is_check_align_enabled( vm ) & (!fd_ulong_is_aligned( _haddr, (align) )); \
     159           0 :     if ( FD_UNLIKELY( sz > LONG_MAX ) ) {                                                                   \
     160           0 :       FD_VM_ERR_FOR_LOG_SYSCALL( _vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );                                   \
     161           0 :       return FD_VM_SYSCALL_ERR_SEGFAULT;                                                                             \
     162           0 :     }                                                                                                       \
     163           0 :     if( FD_UNLIKELY( (!_haddr) | _is_multi ) ) {                                                            \
     164           0 :       FD_VM_ERR_FOR_LOG_EBPF( _vm, FD_VM_ERR_EBPF_ACCESS_VIOLATION );                                       \
     165           0 :       return FD_VM_SYSCALL_ERR_SEGFAULT;                                                                             \
     166           0 :     }                                                                                                       \
     167           0 :     if ( FD_UNLIKELY( _sigbus ) ) {                                                                         \
     168           0 :       FD_VM_ERR_FOR_LOG_SYSCALL( _vm, FD_VM_SYSCALL_ERR_UNALIGNED_POINTER );                                \
     169           0 :       return FD_VM_SYSCALL_ERR_SEGFAULT;                                                                             \
     170           0 :     }                                                                                                       \
     171           0 :     (void *)_haddr;                                                                                         \
     172           0 :   }))
     173             : 
     174             : 
     175         288 : #define FD_VM_MEM_HADDR_ST_NO_SZ_CHECK( vm, vaddr, align ) (__extension__({                                 \
     176         288 :     int _err = 0;                                                                                           \
     177         288 :     void * ret = FD_VM_MEM_HADDR_ST_( vm, vaddr, align, 1UL, &_err );                                       \
     178         288 :     if ( FD_UNLIKELY( 0 != _err ))                                                                          \
     179         288 :       return _err;                                                                                          \
     180         288 :     ret;                                                                                                    \
     181         246 : }))
     182             : 
     183             : 
     184             : #define FD_VM_MEM_HADDR_LD_FAST( vm, vaddr ) ((void const *)fd_vm_mem_haddr_fast( (vm), (vaddr), (vm)->region_haddr ))
     185          51 : #define FD_VM_MEM_HADDR_ST_FAST( vm, vaddr ) ((void       *)fd_vm_mem_haddr_fast( (vm), (vaddr), (vm)->region_haddr ))
     186             : 
     187             : /* FD_VM_MEM_HADDR_AND_REGION_IDX_FROM_INPUT_REGION_CHECKED simply converts a vaddr within the input memory region
     188             :    into an haddr. The sets the region_idx and haddr. */
     189         489 : #define FD_VM_MEM_HADDR_AND_REGION_IDX_FROM_INPUT_REGION_CHECKED( _vm, _offset, _out_region_idx, _out_haddr ) (__extension__({                \
     190         489 :   _out_region_idx = fd_vm_get_input_mem_region_idx( _vm, _offset );                                                                             \
     191         489 :   if( FD_UNLIKELY( _offset>=vm->input_mem_regions[ _out_region_idx ].vaddr_offset+vm->input_mem_regions[ _out_region_idx ].region_sz ) ) {                    \
     192          12 :     FD_VM_ERR_FOR_LOG_EBPF( vm, FD_VM_ERR_EBPF_ACCESS_VIOLATION );                                                                              \
     193          12 :     return FD_VM_SYSCALL_ERR_SEGFAULT;                                                                                                                   \
     194          12 :   }                                                                                                                                             \
     195         489 :   _out_haddr      = (uchar*)_vm->input_mem_regions[ _out_region_idx ].haddr + _offset - _vm->input_mem_regions[ _out_region_idx ].vaddr_offset; \
     196         477 : }))
     197             : 
     198             : /* FD_VM_MEM_SLICE_HADDR_[LD, ST] macros return an arbitrary value if sz == 0. This is because
     199             :    Agave's translate_slice function returns an empty array if the sz == 0.
     200             : 
     201             :    Users of this macro should be aware that they should never access the returned value if sz==0.
     202             : 
     203             :    https://github.com/solana-labs/solana/blob/767d24e5c10123c079e656cdcf9aeb8a5dae17db/programs/bpf_loader/src/syscalls/mod.rs#L560 
     204             : 
     205             :    LONG_MAX check: https://github.com/anza-xyz/agave/blob/dc4b9dcbbf859ff48f40d00db824bde063fdafcc/programs/bpf_loader/src/syscalls/mod.rs#L580
     206             :    Technically, the check in Agave is against
     207             :    "pointer-sized signed integer type ... The size of this primitive is
     208             :     how many bytes it takes to reference any location in memory. For
     209             :     example, on a 32 bit target, this is 4 bytes and on a 64 bit target,
     210             :     this is 8 bytes."
     211             :    Realistically, given the amount of memory that a validator consumes,
     212             :    no one is going to be running on a 32 bit target. So, we don't bother
     213             :    with conditionally compiling in an INT_MAX check. We just assume
     214             :    LONG_MAX. */
     215       25974 : #define FD_VM_MEM_SLICE_HADDR_LD( vm, vaddr, align, sz ) (__extension__({                                       \
     216       25974 :     if ( FD_UNLIKELY( sz > LONG_MAX ) ) {                                                                       \
     217           6 :       FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );                                        \
     218           6 :       return FD_VM_SYSCALL_ERR_INVALID_LENGTH;                                                                  \
     219           6 :     }                                                                                                           \
     220       25974 :     void const * haddr = 0UL;                                                                                   \
     221       25968 :     if ( FD_LIKELY( (ulong)sz > 0UL ) ) {                                                                       \
     222       72135 :       haddr = FD_VM_MEM_HADDR_LD( vm, vaddr, align, sz );                                                       \
     223       72135 :     }                                                                                                           \
     224       25968 :     haddr;                                                                                                      \
     225       25893 : }))
     226             : 
     227             : 
     228             : /* This is the same as the above function but passes in a size of 1 to support
     229             :    loads with no size bounding support. */
     230          27 : #define FD_VM_MEM_SLICE_HADDR_LD_SZ_UNCHECKED( vm, vaddr, align ) (__extension__({                              \
     231          27 :     if ( FD_UNLIKELY( sz > LONG_MAX ) ) {                                                                       \
     232           0 :       FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );                                        \
     233           0 :       return FD_VM_SYSCALL_ERR_INVALID_LENGTH;                                                                  \
     234           0 :     }                                                                                                           \
     235          27 :     void const * haddr = 0UL;                                                                                   \
     236          27 :     if ( FD_LIKELY( (ulong)sz > 0UL ) ) {                                                                       \
     237          81 :       haddr = FD_VM_MEM_HADDR_LD( vm, vaddr, align, 1UL );                                                      \
     238          81 :     }                                                                                                           \
     239          27 :     haddr;                                                                                                      \
     240          27 : }))
     241             : 
     242       10956 : #define FD_VM_MEM_SLICE_HADDR_ST( vm, vaddr, align, sz ) (__extension__({                                       \
     243       10956 :     if ( FD_UNLIKELY( sz > LONG_MAX ) ) {                                                                       \
     244           0 :       FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_LENGTH );                                        \
     245           0 :       return FD_VM_SYSCALL_ERR_INVALID_LENGTH;                                                                  \
     246           0 :     }                                                                                                           \
     247       10956 :     void * haddr = 0UL;                                                                                         \
     248       10956 :     if ( FD_LIKELY( (ulong)sz > 0UL ) ) {                                                                       \
     249       10767 :       haddr = FD_VM_MEM_HADDR_ST( vm, vaddr, align, sz );                                                       \
     250       10386 :     }                                                                                                           \
     251       10956 :     haddr;                                                                                                      \
     252       10575 : }))
     253             : 
     254             : /* FIXME: use overlap logic from runtime? */
     255        6048 : #define FD_VM_MEM_CHECK_NON_OVERLAPPING( vm, vaddr0, sz0, vaddr1, sz1 ) do {                                    \
     256        6048 :   if( FD_UNLIKELY( ((vaddr0> vaddr1) && ((vaddr0-vaddr1)<sz1)) ||                                               \
     257        6048 :                    ((vaddr1>=vaddr0) && ((vaddr1-vaddr0)<sz0)) ) ) {                                            \
     258         129 :     FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_COPY_OVERLAPPING );                                        \
     259         129 :     return FD_VM_SYSCALL_ERR_COPY_OVERLAPPING;                                                                  \
     260         129 :   }                                                                                                             \
     261        6048 : } while(0)
     262             : 
     263             : #endif /* HEADER_fd_src_flamenco_vm_syscall_fd_vm_syscall_macros_h */

Generated by: LCOV version 1.14