LCOV - code coverage report
Current view: top level - flamenco/vm - fd_vm.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 11 14 78.6 %
Date: 2026-11-11 12:40:43 Functions: 8 180 4.4 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_vm_fd_vm_h
       2             : #define HEADER_fd_src_flamenco_vm_fd_vm_h
       3             : 
       4             : #include "fd_vm_base.h"
       5             : #include "../../ballet/sha256/fd_sha256.h"
       6             : 
       7             : /* A fd_vm_t is an opaque handle of a virtual machine that can execute
       8             :    sBPF programs. */
       9             : 
      10             : struct fd_vm;
      11             : typedef struct fd_vm fd_vm_t;
      12             : 
      13             : /**********************************************************************/
      14             : /* FIXME: MOVE TO FD_VM_PRIVATE WHEN CONSTRUCTORS READY */
      15             : 
      16             : /* A fd_vm_shadow_t holds stack frame information not accessible from
      17             :    within a program. */
      18             : 
      19             : struct fd_vm_shadow { ulong r6; ulong r7; ulong r8; ulong r9; ulong r10; ulong pc; };
      20             : typedef struct fd_vm_shadow fd_vm_shadow_t;
      21             : 
      22             : /* fd_vm_input_region_t holds information about fragmented memory regions
      23             :    within the larger input region. */
      24             : 
      25             : struct __attribute__((aligned(8UL))) fd_vm_input_region {
      26             :    ulong vaddr_offset;           /* Represents offset from the start of the input region. */
      27             :    ulong haddr;                  /* Host address corresponding to the start of the mem region. */
      28             :    uint  region_sz;              /* Size of the memory region. */
      29             :    ulong address_space_reserved; /* The amount of address space reserved for the region. */
      30             :    uchar is_writable;            /* If the region can be written to or is read-only */
      31             :    ulong acc_region_meta_idx;    /* Index of the acc_region_meta_t struct for the account corresponding to this region. */
      32             : };
      33             : typedef struct fd_vm_input_region fd_vm_input_region_t;
      34             : 
      35             : /* fd_vm_acc_region_meta_t holds metadata about a given account.  An array of these
      36             :    structs will map an instruction account index to its respective input memory
      37             :    region location. */
      38             : 
      39             : struct __attribute((aligned(8UL))) fd_vm_acc_region_meta {
      40             :    /* Metadata region index, or UINT_MAX without virtual address space
      41             :       adjustments.  The data region follows at region_idx+1 if address
      42             :       space is reserved for account data. */
      43             :    uint       region_idx;
      44             :    /* FIXME: We can get rid of this field once DM is activated.  This is
      45             :       only a hack to make the non-DM code path happy.  When DM is
      46             :       activated, we could query the input_mem_region array for the
      47             :       original data len. */
      48             :    ulong      original_data_len;
      49             :    /* The transaction account corresponding to this account. */
      50             :    fd_acc_t * acc;
      51             :    /* The transaction's touched flag for this account. */
      52             :    uchar *    touched;
      53             : 
      54             :    /* The expected virtual addresses of the serialized pubkey, lamports, owner,
      55             :       and data for this account in VM address space.
      56             :       Used for CPI security checks. */
      57             :    ulong      vm_addr;
      58             :    ulong      vm_key_addr;
      59             :    ulong      vm_lamports_addr;
      60             :    ulong      vm_owner_addr;
      61             :    ulong      vm_data_addr;
      62             : };
      63             : typedef struct fd_vm_acc_region_meta fd_vm_acc_region_meta_t;
      64             : 
      65             : /* In Agave, all the regions are 16-byte aligned in host address space. There is then an alignment check
      66             :    which is done inside each syscall memory translation, checking if the data is aligned in host address
      67             :    space. This is a layering violation, as it leaks the host address layout into the consensus model.
      68             : 
      69             :    In the future we will change this alignment check in the vm to purely operate on the virtual address space,
      70             :    taking advantage of the fact that Agave regions are known to be aligned. For now, we align our regions to
      71             :    either 8 or 16 bytes, as there are no 16-byte alignment translations in the syscalls currently:
      72             :    stack:  16 byte aligned
      73             :    heap:   16 byte aligned
      74             :    input:  8 byte aligned
      75             :    rodata: 8 byte aligned
      76             : 
      77             :     https://github.com/solana-labs/rbpf/blob/cd19a25c17ec474e6fa01a3cc3efa325f44cd111/src/ebpf.rs#L39-L40  */
      78       39684 : #define FD_VM_HOST_REGION_ALIGN                   (16UL)
      79             : 
      80             : struct __attribute__((aligned(FD_VM_HOST_REGION_ALIGN))) fd_vm {
      81             : 
      82             :   /* VM configuration */
      83             : 
      84             :   /* FIXME: suspect these three should be replaced by some kind of VM
      85             :      enabled feature struct (though syscalls do seem to make additional
      86             :      non-trivial use of instr_ctx). */
      87             : 
      88             :   fd_exec_instr_ctx_t * instr_ctx;   /* FIXME: DOCUMENT */
      89             : 
      90             :   /* FIXME: frame_max should be run time configurable by compute budget.
      91             :      If there is no reasonable upper bound on this, shadow and stack
      92             :      will need to be provided by users. */
      93             : 
      94             : //ulong frame_max; /* Maximum number of stack frames, in [0,FD_VM_STACK_FRAME_MAX] */
      95             :   ulong heap_max;  /* Maximum amount of heap in bytes, in [0,FD_VM_HEAP_MAX] */
      96             :   ulong entry_cu;  /* Initial number of compute units for this program, in [0,FD_VM_COMPUTE_UNIT_LIMIT] */
      97             : 
      98             :   /* FIXME: The below are practically an exact match to the
      99             :      fields of an fd_sbpf_program_t (sans ELF info) */
     100             : 
     101             :   uchar const * rodata;    /* Program read only data, indexed [0,rodata_sz), aligned 8 */
     102             :   ulong         rodata_sz; /* Program read only data size in bytes, FIXME: BOUNDS? */
     103             :   ulong const * text;      /* Program sBPF words, indexed [0,text_cnt), aligned 8 */
     104             :   ulong         text_cnt;  /* Program sBPF word count, all text words are inside the rodata */
     105             :   ulong         text_off;  /* CALLX virtual address offset in bytes (NOT words).
     106             :                               SBPF V0-V2: ==(ulong)text - (ulong)rodata (file offset of text within ELF).
     107             :                               SBPF V3:    ==0 (bytecode starts at vaddr 0x100000000 exactly) */
     108             :   ulong         text_sz;   /* Program sBPF size in bytes, == text_cnt*8 */
     109             : 
     110             :   ulong         entry_pc;  /* Initial program counter, in [0,text_cnt)
     111             :                               FIXME: MAKE SURE NOT INTO MW INSTRUCTION, MAKE SURE VALID CALLDEST? */
     112             :   ulong const * calldests; /* Bit vector of local functions that can be called into, bit indexed in [0,text_cnt) */
     113             :   /* FIXME: ADD BIT VECTOR OF FORBIDDEN BRANCH TARGETS (E.G.
     114             :      INTO THE MIDDLE OF A MULTIWORD INSTRUCTION) */
     115             : 
     116             :   fd_sbpf_syscalls_t const * syscalls; /* The map of syscalls (sharable over multiple concurrently running vm) */
     117             : 
     118             :   fd_vm_trace_t * trace; /* Location to stream traces (no tracing if NULL) */
     119             : 
     120             :   /* VM execution and syscall state */
     121             : 
     122             :   /* These are used to communicate the execution and syscall state to
     123             :      users and syscalls.  These are initialized based on the above when
     124             :      a program starts executing.  When program halts or faults, these
     125             :      provide precise execution diagnostics to the user (and potential
     126             :      breakpoint/continue functionality in the future).  When the vm
     127             :      makes a syscall, the vm will set these precisely and, when a
     128             :      syscall returns, the vm will update its internal execution state
     129             :      appropriately. */
     130             : 
     131             :   /* IMPORTANT SAFETY TIP!  THE BEHAVIOR OF THE SYSCALL ALLOCATOR FOR
     132             :      HEAP_SZ MUST EXACTLY MATCH THE SOLANA VALIDATOR ALLOCATOR:
     133             : 
     134             :      https://github.com/solana-labs/solana/blob/v1.17.23/program-runtime/src/invoke_context.rs#L122-L148
     135             : 
     136             :      BIT-FOR-BIT AND BUG-FOR-BUG.  SEE THE SYSCALL_ALLOC_FREE FOR MORE
     137             :      DETAILS. */
     138             : 
     139             :   ulong pc;        /* The current instruction, in [0,text_cnt) in normal execution, may be out of bounds in a fault */
     140             :   ulong ic;        /* The number of instructions which have been executed */
     141             :   ulong cu;        /* The remaining CUs left for the transaction */
     142             :   ulong frame_cnt; /* The current number of stack frames pushed, in [0,frame_max] */
     143             : 
     144             :   ulong heap_sz;     /* Heap size in bytes, in [0,heap_max] */
     145             :   ulong heap_clean;  /* Initialized part of the heap  in bytes, in [0,heap_max] */
     146             :   ulong stack_clean; /* Initialized part of the stack in bytes, in [0,FD_VM_STACK_MAX] */
     147             : 
     148             :   /* VM memory */
     149             : 
     150             :   /* The vm classifies the 64-bit vm address space into 6 regions:
     151             : 
     152             :        0 - unmapped lo
     153             :        1 - program  -> [FD_VM_MEM_MAP_PROGRAM_REGION_START,FD_VM_MEM_MAP_PROGRAM_REGION_START+4GiB)
     154             :        2 - stack    -> [FD_VM_MEM_MAP_STACK_REGION_START,  FD_VM_MEM_MAP_STACK_REGION_START  +4GiB)
     155             :        3 - heap     -> [FD_VM_MEM_MAP_HEAP_REGION_START,   FD_VM_MEM_MAP_HEAP_REGION_START   +4GiB)
     156             :        4 - input    -> [FD_VM_MEM_MAP_INPUT_REGION_START,  FD_VM_MEM_MAP_INPUT_REGION_START  +4GiB)
     157             :        5 - unmapped hi
     158             : 
     159             :      These mappings are encoded in a software TLB consisting of three
     160             :      6-element arrays: region_haddr, region_ld_sz and region_st_sz.
     161             : 
     162             :      region_haddr[i] gives the location in host address space of the
     163             :      first byte in region i.  region_{ld,st}_sz[i] gives the number of
     164             :      mappable bytes in this region for {loads,stores}.  Note that
     165             :      region_{ld,st}_sz[i]<2^32.  Further note that
     166             :      [region_haddr[i],region_haddr[i]+region_{ld,st}_sz[i]) does not
     167             :      wrap around in host address space and does not overlap with any
     168             :      other usages.
     169             : 
     170             :      region_{ld,st}_sz[0] and region_{ld,st}_sz[5] are zero such that
     171             :      requests to access data from a positive sz range in these regions
     172             :      will fail, making regions 0 and 5 unreadable and unwritable.  As
     173             :      such, region_haddr[0] and region_haddr[5] are arbitrary; NULL is
     174             :      used as the obvious default.
     175             : 
     176             :      region_st_sz[1] is also zero such that requests to store data to
     177             :      any positive sz range in this region will fail, making region 1
     178             :      unwritable.
     179             : 
     180             :      Input addresses are translated through input_mem_regions, not these
     181             :      arrays.  The entries at FD_VM_INPUT_REGION are unused. */
     182             : 
     183             :    /* FIXME: If accessing memory beyond the end of the current heap
     184             :       region is not allowed, sol_alloc_free will need to update the tlb
     185             :       arrays during program execution (this is trivial).  At the same
     186             :       time, given sol_alloc_free is deprecated, this is unlikely to be
     187             :       the case. */
     188             : 
     189             :   ulong region_haddr[6];
     190             :   uint  region_ld_sz[6];
     191             :   uint  region_st_sz[6];
     192             : 
     193             :   /* fd_vm_input_region_t and fd_vm_acc_to_mem arrays are passed in by the bpf
     194             :      loaders into fd_vm_init.
     195             :      TODO: It might make more sense to allocate space for these in the VM. */
     196             :   fd_vm_input_region_t *    input_mem_regions;               /* An array of input mem regions represent the input region.
     197             :                                                                 The virtual addresses of each region are contiguous and
     198             :                                                                 strictly increasing. */
     199             :   uint                      input_mem_regions_cnt;
     200             :   fd_vm_acc_region_meta_t * acc_region_metas;                /* Represents a mapping from the instruction account indices
     201             :                                                                 from the instruction context to the input memory region index
     202             :                                                                 of the account's metadata region in the input space. */
     203             :   uchar                     is_deprecated;                   /* The vm requires additional checks in certain CPIs if the
     204             :                                                                 vm's current instance was initialized by a deprecated program. */
     205             : 
     206             :   ulong                     reg   [ FD_VM_REG_MAX         ]; /* registers, indexed [0,FD_VM_REG_CNT).  Note that FD_VM_REG_MAX>FD_VM_REG_CNT.
     207             :                                                                 As such, malformed instructions, which can have src/dst reg index in
     208             :                                                                 [0,FD_VM_REG_MAX), cannot access info outside reg.  Aligned 8. */
     209             :   fd_sha256_t * sha; /* Pre-joined SHA instance. This should be re-initialised before every use. */
     210             : 
     211             :   ulong magic;    /* ==FD_VM_MAGIC */
     212             : 
     213             :   int   direct_mapping;                         /* If direct mapping feature flag is enabled */
     214             :   int   syscall_parameter_address_restrictions; /* If syscall_parameter_address_restrictions feature flag is enabled */
     215             :   int   virtual_address_space_adjustments;      /* If virtual_address_space_adjustments feature flag is enabled */
     216             : 
     217             :   ulong stack_frame_sz;                  /* Stack frame size in bytes; 0 for manually adjusted stacks */
     218             :   ulong stack_push_frame_count;          /* The number of stack frames to adjust the stack by on every stack push */
     219             : 
     220             :   /* Agave uses the segv vaddr in several different cases, including:
     221             :      - Determining whether or not to return a regular or stack access violation
     222             :      - (If direct mapping is enabled) determining the instruction error
     223             :        code to return on store operations. */
     224             :   ulong segv_vaddr;
     225             :   ulong segv_access_len;
     226             :   uchar segv_access_type;
     227             : 
     228             :   ulong sbpf_version;     /* SBPF version, SIMD-0161 */
     229             : 
     230             :   int dump_syscall_to_pb; /* If true, syscalls will be dumped to the specified output directory */
     231             : 
     232             :   /* fd_vm_new zero-initializes up to this point only */
     233             : 
     234             :   /* shadow stack, indexed [0,frame_cnt), if frame_cnt>0, 0/frame_cnt-1 is
     235             :      bottom/top.  Lazily initialized. */
     236             :   fd_vm_shadow_t shadow[ FD_VM_STACK_FRAME_MAX ] __attribute__((aligned(16)));
     237             : 
     238             :   /* Stack storage, indexed [0,FD_VM_STACK_MAX).  Fixed stack frames
     239             :      occupy FD_VM_STACK_FRAME_SZ bytes each.  When enabled, guard gaps
     240             :      exist only in the VM address space, not in this host buffer.
     241             :      reg[10] holds a VM stack address, not an offset into this array. */
     242             :   uchar stack[ FD_VM_STACK_MAX ] __attribute__((aligned(FD_VM_HOST_REGION_ALIGN)));
     243             : 
     244             :   /* syscall heap, [0,heap_sz) used, [heap_sz,heap_max) free. */
     245             :   uchar heap[ FD_VM_HEAP_MAX ] __attribute__((aligned(FD_VM_HOST_REGION_ALIGN)));
     246             : };
     247             : 
     248             : /* FIXME: MOVE ABOVE INTO PRIVATE WHEN CONSTRUCTORS READY */
     249             : /**********************************************************************/
     250             : 
     251             : FD_PROTOTYPES_BEGIN
     252             : 
     253             : /* FIXME: FD_VM_T NEEDS PROPER CONSTRUCTORS */
     254             : 
     255             : /* FD_VM_{ALIGN,FOOTPRINT} describe the alignment and footprint needed
     256             :    for a memory region to hold a fd_vm_t.  ALIGN is a positive
     257             :    integer power of 2.  FOOTPRINT is a multiple of align.
     258             :    These are provided to facilitate compile time declarations. */
     259       39684 : #define FD_VM_ALIGN     FD_VM_HOST_REGION_ALIGN
     260        7540 : #define FD_VM_FOOTPRINT (527872UL)
     261             : 
     262             : /* fd_vm_{align,footprint} give the needed alignment and footprint
     263             :    of a memory region suitable to hold an fd_vm_t.
     264             :    Declaration / aligned_alloc / fd_alloca friendly (e.g. a memory
     265             :    region declared as "fd_vm_t _vm[1];", or created by
     266             :    "aligned_alloc(alignof(fd_vm_t),sizeof(fd_vm_t))" or created
     267             :    by "fd_alloca(alignof(fd_vm_t),sizeof(fd_vm_t))" will all
     268             :    automatically have the needed alignment and footprint).
     269             :    fd_vm_{align,footprint} return the same value as
     270             :    FD_VM_{ALIGN,FOOTPRINT}. */
     271             : FD_FN_CONST ulong
     272             : fd_vm_align( void );
     273             : 
     274             : FD_FN_CONST ulong
     275             : fd_vm_footprint( void );
     276             : 
     277       16097 : #define FD_VM_MAGIC (0xF17EDA2CEF0) /* FIREDANCE SBPF V0 */
     278             : 
     279             : /* fd_vm_new formats memory region with suitable alignment and
     280             :    footprint suitable for holding a fd_vm_t.  Assumes
     281             :    shmem points on the caller to the first byte of the memory region
     282             :    owned by the caller to use.  Returns shmem on success and NULL on
     283             :    failure (logs details).  The memory region will be owned by the state
     284             :    on successful return.  The caller is not joined on return. */
     285             : 
     286             : void *
     287             : fd_vm_new( void * shmem );
     288             : 
     289             : /* fd_vm_join joins the caller to a vm.
     290             :    Assumes shmem points to the first byte of the memory region holding
     291             :    the vm.  Returns a local handle to the join on success (this is
     292             :    not necessarily a simple cast of the address) and NULL on failure
     293             :    (logs details). */
     294             : fd_vm_t *
     295             : fd_vm_join( void * shmem );
     296             : 
     297             : /* fd_vm_init initializes the given fd_vm_t struct, checking that it is
     298             :    not null and has the correct magic value.
     299             : 
     300             :    It modifies the vm object and also returns the object for convenience.
     301             : 
     302             :    FIXME: we should split out the memory mapping setup from this function
     303             :           to handle those errors separately. */
     304             : fd_vm_t *
     305             : fd_vm_init(
     306             :    fd_vm_t *                 vm,
     307             :    fd_exec_instr_ctx_t *     instr_ctx,
     308             :    ulong                     heap_max,
     309             :    ulong                     entry_cu,
     310             :    uchar const *             rodata,
     311             :    ulong                     rodata_sz,
     312             :    ulong const *             text,
     313             :    ulong                     text_cnt,
     314             :    ulong                     text_off,
     315             :    ulong                     text_sz,
     316             :    ulong                     entry_pc,
     317             :    ulong const *             calldests,
     318             :    ulong                     sbpf_version,
     319             :    fd_sbpf_syscalls_t *      syscalls,
     320             :    fd_vm_trace_t *           trace,
     321             :    fd_sha256_t *             sha,
     322             :    fd_vm_input_region_t *    mem_regions,
     323             :    uint                      mem_regions_cnt,
     324             :    fd_vm_acc_region_meta_t * acc_region_metas,
     325             :    uchar                     is_deprecated,
     326             :    int                       direct_mapping,
     327             :    int                       syscall_parameter_address_restrictions,
     328             :    int                       virtual_address_space_adjustments,
     329             :    int                       dump_syscall_to_pb,
     330             :    ulong                     r2_initial_value );
     331             : 
     332             : /* fd_vm_leave leaves the caller's current local join to a vm.
     333             :    Returns a pointer to the memory region holding the vm on success
     334             :    (this is not necessarily a simple cast of the
     335             :    address) and NULL on failure (logs details).  The caller is not
     336             :    joined on successful return. */
     337             : void *
     338             : fd_vm_leave( fd_vm_t * vm );
     339             : 
     340             : /* fd_vm_delete unformats a memory region that holds a vm.
     341             :    Assumes shmem points on the caller to the first
     342             :    byte of the memory region holding the state and that nobody is
     343             :    joined.  Returns a pointer to the memory region on success and NULL
     344             :    on failure (logs details).  The caller has ownership of the memory
     345             :    region on successful return. */
     346             : void *
     347             : fd_vm_delete( void * shmem );
     348             : 
     349             : /* fd_vm_validate validates the sBPF program in the given vm.  Returns
     350             :    success or an error code.  Called before executing a sBPF program.
     351             :    FIXME: DOCUMENT BETTER */
     352             : 
     353             : FD_FN_PURE int
     354             : fd_vm_validate( fd_vm_t const * vm );
     355             : 
     356             : /* fd_vm_is_check_align_enabled returns 1 if the vm should check alignment
     357             :    when doing memory translation. */
     358             : FD_FN_PURE static inline int
     359       66162 : fd_vm_is_check_align_enabled( fd_vm_t const * vm ) {
     360       66162 :    return !vm->is_deprecated;
     361       66162 : }
     362             : 
     363             : /* fd_vm_is_check_size_enabled returns 1 if the vm should check size
     364             :    when doing memory translation. */
     365             : FD_FN_PURE static inline int
     366           0 : fd_vm_is_check_size_enabled( fd_vm_t const * vm ) {
     367           0 :    return !vm->is_deprecated;
     368           0 : }
     369             : 
     370             : /* fd_vm_exec runs vm from program start to program halt or program
     371             :    fault, appending an execution trace if vm is attached to a trace.
     372             :    vm must be successfully initialized with fd_vm_init before each call.
     373             : 
     374             :    Returns FD_VM_SUCCESS on success and an FD_VM_ERR_EBPF_* code on
     375             :    failure.  A syscall failure returns FD_VM_ERR_EBPF_SYSCALL_ERROR.
     376             : 
     377             :    On failure, pc, ic and cu reflect the interpreter halt path.  A
     378             :    compute-budget failure sets cu to zero.  The reported ic need not
     379             :    count every instruction attempted before a budget failure.  Syscalls
     380             :    may have partially modified VM state before returning an error.
     381             : 
     382             :    fd_vm_exec_trace runs with tracing and requires vm to be attached to
     383             :    a trace.  fd_vm_exec_notrace runs without tracing even if vm
     384             :    is attached to a trace. */
     385             : 
     386             : int
     387             : fd_vm_exec_trace( fd_vm_t * vm );
     388             : 
     389             : int
     390             : fd_vm_exec_notrace( fd_vm_t * vm );
     391             : 
     392             : static inline int
     393        4231 : fd_vm_exec( fd_vm_t * vm ) {
     394        4231 :   if( FD_UNLIKELY( vm->trace ) ) return fd_vm_exec_trace  ( vm );
     395        4231 :   else                           return fd_vm_exec_notrace( vm );
     396        4231 : }
     397             : 
     398             : FD_PROTOTYPES_END
     399             : 
     400             : #endif /* HEADER_fd_src_flamenco_vm_fd_vm_h */

Generated by: LCOV version 1.14