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 :
6 : /* A fd_vm_t is an opaque handle of a virtual machine that can execute
7 : sBPF programs. */
8 :
9 : struct fd_vm;
10 : typedef struct fd_vm fd_vm_t;
11 :
12 : /**********************************************************************/
13 : /* FIXME: MOVE TO FD_VM_PRIVATE WHEN CONSTRUCTORS READY */
14 :
15 : /* A fd_vm_shadow_t holds stack frame information not accessible from
16 : within a program. */
17 :
18 : struct fd_vm_shadow { ulong r6; ulong r7; ulong r8; ulong r9; ulong r10; ulong pc; };
19 : typedef struct fd_vm_shadow fd_vm_shadow_t;
20 :
21 : /* fd_vm_input_region_t holds information about fragmented memory regions
22 : within the larger input region. */
23 :
24 : struct __attribute__((aligned(8UL))) fd_vm_input_region {
25 : ulong vaddr_offset; /* Represents offset from the start of the input region. */
26 : ulong haddr; /* Host address corresponding to the start of the mem region. */
27 : uint region_sz; /* Size of the memory region. */
28 : uint is_writable; /* If the region can be written to or is read-only */
29 : };
30 : typedef struct fd_vm_input_region fd_vm_input_region_t;
31 :
32 : /* fd_vm_acc_region_meta_t holds metadata about a given account. An array of these
33 : structs will map an instruction account index to its respective input memory
34 : region location. */
35 :
36 : struct __attribute((aligned(8UL))) fd_vm_acc_region_meta {
37 : uint region_idx;
38 : uchar has_data_region;
39 : uchar has_resizing_region;
40 : /* offset of the accounts metadata region, relative to the start of the input region.
41 : importantly, this excludes any duplicate account markers at the beginning of the "full" metadata region. */
42 : ulong metadata_region_offset;
43 : };
44 : typedef struct fd_vm_acc_region_meta fd_vm_acc_region_meta_t;
45 :
46 : /* In Agave, all the regions are 16-byte aligned in host address space. There is then an alignment check
47 : which is done inside each syscall memory translation, checking if the data is aligned in host address
48 : space. This is a layering violation, as it leaks the host address layout into the consensus model.
49 :
50 : In the future we will change this alignment check in the vm to purely operate on the virtual address space,
51 : taking advantage of the fact that Agave regions are known to be aligned. For now, we align our regions to
52 : either 8 or 16 bytes, as there are no 16-byte alignment translations in the syscalls currently:
53 : stack: 16 byte aligned
54 : heap: 16 byte aligned
55 : input: 8 byte aligned
56 : rodata: 8 byte aligned
57 :
58 : https://github.com/solana-labs/rbpf/blob/cd19a25c17ec474e6fa01a3cc3efa325f44cd111/src/ebpf.rs#L39-L40 */
59 333126 : #define FD_VM_HOST_REGION_ALIGN (16UL)
60 :
61 : struct __attribute__((aligned(FD_VM_HOST_REGION_ALIGN))) fd_vm {
62 :
63 : /* VM configuration */
64 :
65 : /* FIXME: suspect these three should be replaced by some kind of VM
66 : enabled feature struct (though syscalls do seem to make additional
67 : non-trivial use of instr_ctx). */
68 :
69 : fd_exec_instr_ctx_t * instr_ctx; /* FIXME: DOCUMENT */
70 :
71 : /* FIXME: frame_max should be run time configurable by compute budget.
72 : If there is no reasonable upper bound on this, shadow and stack
73 : will need to be provided by users. */
74 :
75 : //ulong frame_max; /* Maximum number of stack frames, in [0,FD_VM_STACK_FRAME_MAX] */
76 : ulong heap_max; /* Maximum amount of heap in bytes, in [0,FD_VM_HEAP_MAX] */
77 : ulong entry_cu; /* Initial number of compute units for this program, in [0,FD_VM_COMPUTE_UNIT_LIMIT] */
78 :
79 : /* FIXME: The below are practically an exact match to the
80 : fields of an fd_sbpf_program_t (sans ELF info) */
81 :
82 : uchar const * rodata; /* Program read only data, indexed [0,rodata_sz), aligned 8 */
83 : ulong rodata_sz; /* Program read only data size in bytes, FIXME: BOUNDS? */
84 : ulong const * text; /* Program sBPF words, indexed [0,text_cnt), aligned 8 */
85 : ulong text_cnt; /* Program sBPF word count, all text words are inside the rodata */
86 : ulong text_off; /* ==(ulong)text - (ulong)rodata, relocation offset in bytes we must apply to indirect calls
87 : (callx/CALL_REGs), IMPORTANT SAFETY TIP! THIS IS IN BYTES, NOT WORDS! */
88 : ulong text_sz; /* Program sBPF size in bytes, == text_cnt*8 */
89 :
90 : ulong entry_pc; /* Initial program counter, in [0,text_cnt)
91 : FIXME: MAKE SURE NOT INTO MW INSTRUCTION, MAKE SURE VALID CALLDEST? */
92 : ulong const * calldests; /* Bit vector of local functions that can be called into, bit indexed in [0,text_cnt) */
93 : /* FIXME: ADD BIT VECTOR OF FORBIDDEN BRANCH TARGETS (E.G.
94 : INTO THE MIDDLE OF A MULTIWORD INSTRUCTION) */
95 :
96 : fd_sbpf_syscalls_t const * syscalls; /* The map of syscalls (sharable over multiple concurrently running vm) */
97 :
98 : fd_vm_trace_t * trace; /* Location to stream traces (no tracing if NULL) */
99 :
100 : /* VM execution and syscall state */
101 :
102 : /* These are used to communicate the execution and syscall state to
103 : users and syscalls. These are initialized based on the above when
104 : a program starts executing. When program halts or faults, these
105 : provide precise execution diagnostics to the user (and potential
106 : breakpoint/continue functionality in the future). When the vm
107 : makes a syscall, the vm will set these precisely and, when a
108 : syscall returns, the vm will update its internal execution state
109 : appropriately. */
110 :
111 : /* IMPORTANT SAFETY TIP! THE BEHAVIOR OF THE SYSCALL ALLOCATOR FOR
112 : HEAP_SZ MUST EXACTLY MATCH THE SOLANA VALIDATOR ALLOCATOR:
113 :
114 : https://github.com/solana-labs/solana/blob/v1.17.23/program-runtime/src/invoke_context.rs#L122-L148
115 :
116 : BIT-FOR-BIT AND BUG-FOR-BUG. SEE THE SYSCALL_ALLOC_FREE FOR MORE
117 : DETAILS. */
118 :
119 : ulong pc; /* The current instruction, in [0,text_cnt) in normal execution, may be out of bounds in a fault */
120 : ulong ic; /* The number of instructions which have been executed */
121 : ulong cu; /* The remaining CUs left for the transaction, positive in normal execution, may be zero in a fault */
122 : ulong frame_cnt; /* The current number of stack frames pushed, in [0,frame_max] */
123 :
124 : ulong heap_sz; /* Heap size in bytes, in [0,heap_max] */
125 :
126 : /* VM memory */
127 :
128 : /* The vm classifies the 64-bit vm address space into 6 regions:
129 :
130 : 0 - unmapped lo
131 : 1 - program -> [FD_VM_MEM_MAP_PROGRAM_REGION_START,FD_VM_MEM_MAP_PROGRAM_REGION_START+4GiB)
132 : 2 - stack -> [FD_VM_MEM_MAP_STACK_REGION_START, FD_VM_MEM_MAP_STACK_REGION_START +4GiB)
133 : 3 - heap -> [FD_VM_MEM_MAP_HEAP_REGION_START, FD_VM_MEM_MAP_HEAP_REGION_START +4GiB)
134 : 4 - input -> [FD_VM_MEM_MAP_INPUT_REGION_START, FD_VM_MEM_MAP_INPUT_REGION_START +4GiB)
135 : 5 - unmapped hi
136 :
137 : These mappings are encoded in a software TLB consisting of three
138 : 6-element arrays: region_haddr, region_ld_sz and region_st_sz.
139 :
140 : region_haddr[i] gives the location in host address space of the
141 : first byte in region i. region_{ld,st}_sz[i] gives the number of
142 : mappable bytes in this region for {loads,stores}. Note that
143 : region_{ld,st}_sz[i]<2^32. Further note that
144 : [region_haddr[i],region_haddr[i]+region_{ld,st}_sz[i]) does not
145 : wrap around in host address space and does not overlap with any
146 : other usages.
147 :
148 : region_{ld,st}_sz[0] and region_{ld,st}_sz[5] are zero such that
149 : requests to access data from a positive sz range in these regions
150 : will fail, making regions 0 and 5 unreadable and unwritable. As
151 : such, region_haddr[0] and region_haddr[5] are arbitrary; NULL is
152 : used as the obvious default.
153 :
154 : region_st_sz[1] is also zero such that requests to store data to
155 : any positive sz range in this region will fail, making region 1
156 : unwritable.
157 :
158 : When the direct mapping feature is enabled, the input region will
159 : no longer be a contigious buffer of host memory. Instead
160 : it will compose of several fragmented regions of memory each with
161 : its own read/write privleges and size. Address translation to the
162 : input region will now have to rely on a binary search lookup of the
163 : start of the appropriate area of physical memory. It also involves
164 : doing a check against if the region can be written to. */
165 :
166 : /* FIXME: If accessing memory beyond the end of the current heap
167 : region is not allowed, sol_alloc_free will need to update the tlb
168 : arrays during program execution (this is trivial). At the same
169 : time, given sol_alloc_free is deprecated, this is unlikely to be
170 : the case. */
171 :
172 : ulong region_haddr[6];
173 : uint region_ld_sz[6];
174 : uint region_st_sz[6];
175 :
176 : /* fd_vm_input_region_t and fd_vm_acc_to_mem arrays are passed in by the bpf
177 : loaders into fd_vm_init.
178 : TODO: It might make more sense to allocate space for these in the VM. */
179 : fd_vm_input_region_t * input_mem_regions; /* An array of input mem regions represent the input region.
180 : The virtual addresses of each region are contigiuous and
181 : strictly increasing. */
182 : uint input_mem_regions_cnt;
183 : fd_vm_acc_region_meta_t * acc_region_metas; /* Represents a mapping from the instruction account indicies
184 : from the instruction context to the input memory region index
185 : of the account's data region in the input space. */
186 : uchar is_deprecated; /* The vm requires additional checks in certain CPIs if the
187 : vm's current instance was initialized by a deprecated program. */
188 :
189 : ulong reg [ FD_VM_REG_MAX ]; /* registers, indexed [0,FD_VM_REG_CNT). Note that FD_VM_REG_MAX>FD_VM_REG_CNT.
190 : As such, malformed instructions, which can have src/dst reg index in
191 : [0,FD_VM_REG_MAX), cannot access info outside reg. Aligned 8. */
192 : fd_vm_shadow_t shadow[ FD_VM_STACK_FRAME_MAX ]; /* shadow stack, indexed [0,frame_cnt), if frame_cnt>0, 0/frame_cnt-1 is
193 : bottom/top. Aligned 16. */
194 : uchar stack [ FD_VM_STACK_MAX ]; /* stack, indexed [0,FD_VM_STACK_MAX). Divided into FD_VM_STACK_FRAME_MAX
195 : frames. Each frame has a FD_VM_STACK_GUARD_SZ region followed by a
196 : FD_VM_STACK_FRAME_SZ region. reg[10] gives the offset of the start of the
197 : current stack frame. Aligned 16. */
198 : uchar heap [ FD_VM_HEAP_MAX ]; /* syscall heap, [0,heap_sz) used, [heap_sz,heap_max) free. Aligned 8. */
199 :
200 : fd_sha256_t * sha; /* Pre-joined SHA instance. This should be re-initialised before every use. */
201 :
202 : ulong magic; /* ==FD_VM_MAGIC */
203 :
204 : int direct_mapping; /* If direct mapping is enabled or not */
205 : ulong stack_frame_size; /* Size of a stack frame (varies depending on direct mapping being enabled or not) */
206 :
207 : /* Agave reports different error codes (for developers to understand the failure cause) if direct mapping is
208 : enabled AND we halt on a segfault caused by a store on an invalid vaddr. */
209 : ulong segv_store_vaddr;
210 :
211 : ulong sbpf_version; /* SBPF version, SIMD-0161 */
212 : };
213 :
214 : /* FIXME: MOVE ABOVE INTO PRIVATE WHEN CONSTRUCTORS READY */
215 : /**********************************************************************/
216 :
217 : FD_PROTOTYPES_BEGIN
218 :
219 : /* FIXME: FD_VM_T NEEDS PROPER CONSTRUCTORS */
220 :
221 : /* FD_VM_{ALIGN,FOOTPRINT} describe the alignment and footprint needed
222 : for a memory region to hold a fd_vm_t. ALIGN is a positive
223 : integer power of 2. FOOTPRINT is a multiple of align.
224 : These are provided to facilitate compile time declarations. */
225 333126 : #define FD_VM_ALIGN FD_VM_HOST_REGION_ALIGN
226 333126 : #define FD_VM_FOOTPRINT (527808UL)
227 :
228 : /* fd_vm_{align,footprint} give the needed alignment and footprint
229 : of a memory region suitable to hold an fd_vm_t.
230 : Declaration / aligned_alloc / fd_alloca friendly (e.g. a memory
231 : region declared as "fd_vm_t _vm[1];", or created by
232 : "aligned_alloc(alignof(fd_vm_t),sizeof(fd_vm_t))" or created
233 : by "fd_alloca(alignof(fd_vm_t),sizeof(fd_vm_t))" will all
234 : automatically have the needed alignment and footprint).
235 : fd_vm_{align,footprint} return the same value as
236 : FD_VM_{ALIGN,FOOTPRINT}. */
237 : FD_FN_CONST ulong
238 : fd_vm_align( void );
239 :
240 : FD_FN_CONST ulong
241 : fd_vm_footprint( void );
242 :
243 336444 : #define FD_VM_MAGIC (0xF17EDA2CEF0) /* FIREDANCE SBPF V0 */
244 :
245 : /* fd_vm_new formats memory region with suitable alignment and
246 : footprint suitable for holding a fd_vm_t. Assumes
247 : shmem points on the caller to the first byte of the memory region
248 : owned by the caller to use. Returns shmem on success and NULL on
249 : failure (logs details). The memory region will be owned by the state
250 : on successful return. The caller is not joined on return. */
251 :
252 : void *
253 : fd_vm_new( void * shmem );
254 :
255 : /* fd_vm_join joins the caller to a vm.
256 : Assumes shmem points to the first byte of the memory region holding
257 : the vm. Returns a local handle to the join on success (this is
258 : not necessarily a simple cast of the address) and NULL on failure
259 : (logs details). */
260 : fd_vm_t *
261 : fd_vm_join( void * shmem );
262 :
263 : /* fd_vm_init initializes the given fd_vm_t struct, checking that it is
264 : not null and has the correct magic value.
265 :
266 : It modifies the vm object and also returns the object for convenience.
267 :
268 : FIXME: we should split out the memory mapping setup from this function
269 : to handle those errors separately. */
270 : fd_vm_t *
271 : fd_vm_init(
272 : fd_vm_t * vm,
273 : fd_exec_instr_ctx_t *instr_ctx,
274 : ulong heap_max,
275 : ulong entry_cu,
276 : uchar const * rodata,
277 : ulong rodata_sz,
278 : ulong const * text,
279 : ulong text_cnt,
280 : ulong text_off,
281 : ulong text_sz,
282 : ulong entry_pc,
283 : ulong * calldests,
284 : ulong sbpf_version,
285 : fd_sbpf_syscalls_t * syscalls,
286 : fd_vm_trace_t * trace,
287 : fd_sha256_t * sha,
288 : fd_vm_input_region_t * mem_regions,
289 : uint mem_regions_cnt,
290 : fd_vm_acc_region_meta_t * acc_region_metas,
291 : uchar is_deprecated,
292 : int direct_mapping );
293 :
294 : /* fd_vm_leave leaves the caller's current local join to a vm.
295 : Returns a pointer to the memory region holding the vm on success
296 : (this is not necessarily a simple cast of the
297 : address) and NULL on failure (logs details). The caller is not
298 : joined on successful return. */
299 : void *
300 : fd_vm_leave( fd_vm_t * vm );
301 :
302 : /* fd_vm_delete unformats a memory region that holds a vm.
303 : Assumes shmem points on the caller to the first
304 : byte of the memory region holding the state and that nobody is
305 : joined. Returns a pointer to the memory region on success and NULL
306 : on failure (logs details). The caller has ownership of the memory
307 : region on successful return. */
308 : void *
309 : fd_vm_delete( void * shmem );
310 :
311 : /* fd_vm_validate validates the sBPF program in the given vm. Returns
312 : success or an error code. Called before executing a sBPF program.
313 : FIXME: DOCUMENT BETTER */
314 :
315 : FD_FN_PURE int
316 : fd_vm_validate( fd_vm_t const * vm );
317 :
318 : /* fd_vm_is_check_align_enabled returns 1 if the vm should check alignment
319 : when doing memory translation. */
320 : FD_FN_PURE static inline int
321 65124 : fd_vm_is_check_align_enabled( fd_vm_t const * vm ) {
322 65124 : return !vm->is_deprecated;
323 65124 : }
324 :
325 : /* fd_vm_is_check_size_enabled returns 1 if the vm should check size
326 : when doing memory translation. */
327 : FD_FN_PURE static inline int
328 0 : fd_vm_is_check_size_enabled( fd_vm_t const * vm ) {
329 0 : return !vm->is_deprecated;
330 0 : }
331 :
332 : /* FIXME: make this trace-aware, and move into fd_vm_init
333 : This is a temporary hack to make the fuzz harness work. */
334 : int
335 : fd_vm_setup_state_for_execution( fd_vm_t * vm ) ;
336 :
337 : /* fd_vm_exec runs vm from program start to program halt or program
338 : fault, appending an execution trace if vm is attached to a trace.
339 :
340 : Since this is running from program start, this will init r1 and r10,
341 : pop all stack frames and free all heap allocations.
342 :
343 : IMPORTANT SAFETY TIP! This currently does not zero out any other
344 : registers, the user stack region or the user heap. (FIXME: SHOULD
345 : IT??)
346 :
347 : Returns FD_VM_SUCCESS (0) on success and an FD_VM_ERR code (negative)
348 : on failure. Reasons for failure include:
349 :
350 : INVAL - NULL vm (or, for fd_vm_exec_trace, the vm is not
351 : attached to trace). FIXME: ADD OTHER INPUT ARG CHECKS?
352 :
353 : SIGTEXT - A jump/call set the program counter outside the text
354 : region or the program counter incremented beyond the
355 : text region. pc will be at the out of bounds location.
356 : ic and cu will not include the out of bounds location.
357 : For a call, the call stack frame was allocated.
358 :
359 : SIGSPLIT - A jump/call set the program counter into the middle of
360 : a multiword instruction or a multiword instruction went
361 : past the text region end. pc will be at the split. ic
362 : and cu will not include the split. For a call, the
363 : call stack frame was allocated.
364 :
365 : SIGCALL - A call set the program counter to a non-function
366 : location. pc will be at the non-function location. ic
367 : and cu will include the call but not include the
368 : non-function location. The call stack frame was
369 : allocated.
370 :
371 : SIGSTACK - The call depth limit was exceeded. pc will be at the
372 : call. ic and cu will include the call but not the call
373 : target. The call stack frame was not allocated.
374 :
375 : SIGILL - An invalid instruction was encountered (including an
376 : invalid opcode and an endian swap with an invalid bit
377 : width). pc will be at the invalid instruction. ic and
378 : cu will not include the invalid instruction.
379 :
380 : SIGSEGV - An invalid memory access (outside the program memory
381 : map) was encountered. pc will be at the faulting
382 : instruction. ic and cu will not include the faulting
383 : instruction.
384 :
385 : SIGBUS - An unaligned memory access was encountered. pc will be
386 : at the faulting instruction. ic and cu will not
387 : include the faulting instruction. (Note: currently
388 : mapped to SIGSEGV and then only if check_align is
389 : enabled.)
390 :
391 : SIGRDONLY - A write to read-only memory address was encountered.
392 : pc will be at the faulting instruction. ic and cu will
393 : not include the faulting instruction. (Note: currently
394 : mapped to SIGSEGV.)
395 :
396 : SIGCOST - The compute limit was exceeded. pc will be at the
397 : first non-executed instruction (if pc is a syscall, the
398 : syscall might have been partially executed when it ran
399 : out of budget .. see safety tip below). ic will cover
400 : all executed instructions. cu will be zero.
401 :
402 : This will considers any error returned by a syscall as a fault and
403 : returns the syscall error code here. See syscall documentation for
404 : details here. When a syscall faults, pc will be at the syscall, ic
405 : will include the syscall and cu will include the syscall and any
406 : additional costs the syscall might have incurred up to that point of
407 : the fault.
408 :
409 : IMPORTANT SAFETY TIP! Ideally, a syscall should only modify vm's
410 : state when it knows its overall syscall will be successful.
411 : Unfortunately, this is often not practical (e.g. a syscall starts
412 : processing a list of user provided commands and discovers an error
413 : condition late in the command list that did not exist at syscall
414 : start because the error condition was created by successfully
415 : executed commands earlier in the list). As such, vm's state on a
416 : faulting syscall may not be clean.
417 :
418 : FIXME: SINCE MOST SYSCALLS CAN BE IMPLEMENTED TO HAVE CLEAN FAULTING
419 : BEHAVIOR, PROVIDE A MECHANISM SO USERS CAN EASILY DETECT UNCLEAN
420 : SYSCALL FAULTS?
421 :
422 : For SIGCOST, note that the vm can speculate ahead when processing
423 : instructions. This makes it is possible to have a situation where
424 : a vm faults with, for example, SIGSEGV from a speculatively
425 : executed memory access while a non-speculative execution would have
426 : faulted with SIGCOST on an earlier instruction. In these situations,
427 : pc will be at the faulting speculatively executed instruction, ic
428 : will include all the speculatively executed instructions, cu will be
429 : zero and vm's state will include the impact of all the speculation.
430 :
431 : IMPORTANT SAFETY TIP! While different vm implementations can
432 : disagree on why a program faulted (e.g. SIGCOST versus SIGSEGV in the
433 : example above), they cannot disagree on whether or not a program
434 : faulted. As a result, the specific fault reason must never be
435 : allowed to be part of consensus.
436 :
437 : fd_vm_exec_trace runs with tracing and requires vm to be attached to
438 : a trace. fd_vm_exec_notrace runs without without tracing even if vm
439 : is attached to a trace. */
440 :
441 : int
442 : fd_vm_exec_trace( fd_vm_t * vm );
443 :
444 : int
445 : fd_vm_exec_notrace( fd_vm_t * vm );
446 :
447 : static inline int
448 1215 : fd_vm_exec( fd_vm_t * vm ) {
449 1215 : if( FD_UNLIKELY( vm->trace ) ) return fd_vm_exec_trace ( vm );
450 1215 : else return fd_vm_exec_notrace( vm );
451 1215 : }
452 :
453 : FD_PROTOTYPES_END
454 :
455 : #endif /* HEADER_fd_src_flamenco_vm_fd_vm_h */
|