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 */
|