Line data Source code
1 : #include "fd_vm_test.h"
2 : #include "fd_exec_instr_test.h"
3 : #include "generated/vm.pb.h"
4 :
5 : int
6 : fd_vm_syscall_noop( void * _vm,
7 : ulong arg0,
8 : ulong arg1,
9 : ulong arg2,
10 : ulong arg3,
11 : ulong arg4,
12 30 : ulong* _ret){
13 : /* TODO: have input message determine CUs to deduct?
14 : fd_vm_t * vm = (fd_vm_t *) _vm;
15 : vm->cu = vm->cu - 5;
16 : */
17 :
18 30 : (void) _vm;
19 30 : (void) arg0;
20 30 : (void) arg1;
21 30 : (void) arg2;
22 30 : (void) arg3;
23 30 : (void) arg4;
24 30 : *_ret = 0;
25 30 : return 0;
26 30 : }
27 :
28 : void
29 : fd_setup_vm_acc_region_metas( fd_vm_acc_region_meta_t * acc_regions_meta,
30 : fd_vm_t * vm,
31 333126 : fd_exec_instr_ctx_t * instr_ctx ) {
32 : /* cur_region is used to figure out what acc region index the account
33 : corresponds to. */
34 333126 : uint cur_region = 0UL;
35 361800 : for( ulong i=0UL; i<instr_ctx->instr->acct_cnt; i++ ) {
36 28674 : cur_region++;
37 28674 : fd_borrowed_account_t const * acc = instr_ctx->instr->borrowed_accounts[i];
38 28674 : acc_regions_meta[i].region_idx = cur_region;
39 28674 : acc_regions_meta[i].has_data_region = acc->const_meta->dlen>0UL;
40 28674 : acc_regions_meta[i].has_resizing_region = !vm->is_deprecated;
41 28674 : if( acc->const_meta->dlen>0UL ) {
42 24822 : cur_region++;
43 24822 : }
44 28674 : if( vm->is_deprecated ) {
45 9531 : cur_region--;
46 9531 : }
47 28674 : }
48 333126 : }
49 :
50 : ulong
51 : fd_exec_vm_interp_test_run( fd_exec_instr_test_runner_t * runner,
52 : void const * input_,
53 : void ** output_,
54 : void * output_buf,
55 320415 : ulong output_bufsz ) {
56 320415 : fd_exec_test_syscall_context_t const * input = fd_type_pun_const( input_ );
57 320415 : fd_exec_test_syscall_effects_t ** output = fd_type_pun( output_ );
58 :
59 : /* Create execution context */
60 320415 : const fd_exec_test_instr_context_t * input_instr_ctx = &input->instr_ctx;
61 320415 : fd_exec_instr_ctx_t instr_ctx[1];
62 320415 : if( !fd_exec_test_instr_context_create( runner, instr_ctx, input_instr_ctx, true /* is_syscall avoids certain checks we don't want */ ) ) {
63 0 : fd_exec_test_instr_context_destroy( runner, instr_ctx );
64 0 : return 0UL;
65 0 : }
66 :
67 320415 : if( !( input->has_vm_ctx ) ) {
68 0 : fd_exec_test_instr_context_destroy( runner, instr_ctx );
69 0 : return 0UL;
70 0 : }
71 :
72 320415 : fd_valloc_t valloc = fd_scratch_virtual();
73 320415 : fd_spad_t * spad = fd_exec_instr_test_runner_get_spad( runner );
74 :
75 : /* Create effects */
76 320415 : ulong output_end = (ulong) output_buf + output_bufsz;
77 320415 : FD_SCRATCH_ALLOC_INIT( l, output_buf );
78 320415 : fd_exec_test_syscall_effects_t * effects =
79 320415 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_syscall_effects_t),
80 320415 : sizeof (fd_exec_test_syscall_effects_t) );
81 320415 : *effects = (fd_exec_test_syscall_effects_t) FD_EXEC_TEST_SYSCALL_EFFECTS_INIT_ZERO;
82 :
83 320415 : if( FD_UNLIKELY( _l > output_end ) ) {
84 0 : fd_exec_test_instr_context_destroy( runner, instr_ctx );
85 0 : return 0UL;
86 0 : }
87 :
88 320415 : do{
89 : /* Setup regions */
90 320415 : if ( !input->vm_ctx.rodata ) {
91 0 : break;
92 0 : }
93 320415 : ulong rodata_sz = input->vm_ctx.rodata->size;
94 320415 : uchar * rodata = fd_spad_alloc_debug( spad, 8UL, rodata_sz );
95 320415 : memcpy( rodata, input->vm_ctx.rodata->bytes, rodata_sz );
96 :
97 : /* Load input data regions */
98 320415 : fd_vm_input_region_t * input_regions = fd_spad_alloc_debug( spad, alignof(fd_vm_input_region_t), sizeof(fd_vm_input_region_t) * input->vm_ctx.input_data_regions_count );
99 320415 : uint input_regions_cnt = fd_setup_vm_input_regions( input_regions, input->vm_ctx.input_data_regions, input->vm_ctx.input_data_regions_count, spad );
100 :
101 320415 : if (input->vm_ctx.heap_max > FD_VM_HEAP_DEFAULT) {
102 0 : break;
103 0 : }
104 :
105 : /* Setup calldests from call_whitelist.
106 : Alloc calldests with the expected size (1 bit per ix, rounded up to ulong) */
107 320415 : ulong max_pc = (rodata_sz + 7) / 8;
108 320415 : ulong calldests_sz = ((max_pc + 63) / 64) * 8;
109 320415 : ulong * calldests = fd_valloc_malloc( valloc, fd_sbpf_calldests_align(), calldests_sz );
110 320415 : memset( calldests, 0, calldests_sz );
111 320415 : if( input->vm_ctx.call_whitelist && input->vm_ctx.call_whitelist->size > 0 ) {
112 294 : memcpy( calldests, input->vm_ctx.call_whitelist->bytes, input->vm_ctx.call_whitelist->size );
113 : /* Make sure bits over max_pc are all 0s. */
114 294 : ulong mask = (1UL << (max_pc % 64)) - 1UL;
115 294 : calldests[ max_pc / 64 ] &= mask;
116 294 : }
117 320415 : ulong entry_pc = fd_ulong_min( input->vm_ctx.entry_pc, rodata_sz / 8UL - 1UL );
118 320415 : if( input->vm_ctx.sbpf_version >= FD_SBPF_V3 ) {
119 : /* in v3 we have to enable the entrypoint */
120 0 : calldests[ entry_pc / 64UL ] |= ( 1UL << ( entry_pc % 64UL ) );
121 0 : }
122 :
123 : /* Setup syscalls. Have them all be no-ops */
124 320415 : fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new( fd_valloc_malloc( valloc, fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ) );
125 320415 : fd_vm_syscall_register_slot( syscalls, instr_ctx->slot_ctx, 0 );
126 :
127 41333535 : for( ulong i=0; i< fd_sbpf_syscalls_slot_cnt(); i++ ){
128 41013120 : fd_sbpf_syscalls_t * syscall = fd_sbpf_syscalls_query( syscalls, syscalls[i].key, NULL );
129 41013120 : if ( !syscall ) {
130 32361906 : continue;
131 32361906 : }
132 8651214 : syscall->func = fd_vm_syscall_noop;
133 8651214 : }
134 :
135 : /* Setup trace */
136 320415 : const uint DUMP_TRACE = 0; // Set to 1 to dump trace to stdout
137 320415 : uint tracing_enabled = input->vm_ctx.tracing_enabled;
138 320415 : fd_vm_trace_t * trace = NULL;
139 320415 : ulong event_max = 1UL<<20;
140 320415 : ulong event_data_max = 2048UL;
141 :
142 320415 : if (!!tracing_enabled) {
143 3 : trace = fd_vm_trace_new( fd_valloc_malloc( valloc, fd_vm_trace_align(), fd_vm_trace_footprint( event_max, event_data_max ) ), event_max, event_data_max );
144 3 : }
145 :
146 : /* Setup vm */
147 320415 : fd_vm_t * vm = fd_vm_join( fd_vm_new( fd_valloc_malloc( valloc, fd_vm_align(), fd_vm_footprint() ) ) );
148 320415 : FD_TEST( vm );
149 :
150 : /* Enable direct_mapping for SBPF version >= v1 */
151 320415 : if( input->vm_ctx.sbpf_version >= FD_SBPF_V1 ) {
152 213603 : ((fd_exec_epoch_ctx_t *)(instr_ctx->epoch_ctx))->features.bpf_account_data_direct_mapping = 0UL;
153 213603 : }
154 :
155 320415 : fd_vm_init(
156 320415 : vm,
157 320415 : instr_ctx,
158 320415 : input->vm_ctx.heap_max,
159 320415 : input->has_instr_ctx ? input->instr_ctx.cu_avail : 0,
160 320415 : rodata,
161 320415 : rodata_sz,
162 320415 : (ulong *) rodata, /* text*, same as rodata */
163 320415 : rodata_sz / 8, /* text_cnt */
164 320415 : 0, /* text_off */
165 320415 : rodata_sz, /* text_sz */
166 320415 : entry_pc,
167 320415 : calldests,
168 320415 : input->vm_ctx.sbpf_version,
169 320415 : syscalls,
170 320415 : trace, /* trace */
171 320415 : NULL, /* sha */
172 320415 : input_regions,
173 320415 : input_regions_cnt,
174 320415 : NULL, /* vm_acc_region_meta*/
175 320415 : 0, /* is deprecated */
176 : FD_FEATURE_ACTIVE( instr_ctx->slot_ctx, bpf_account_data_direct_mapping ) /* direct mapping */
177 320415 : );
178 :
179 : /* Setup registers.
180 : r1, r10, r11 are initialized by EbpfVm::new (r10) or EbpfVm::execute_program (r1, r11),
181 : or equivalently by fd_vm_init and fd_vm_setup_state_for_execution.
182 : Modifying them will most like break execution.
183 : In syscalls we allow override them (especially r1) because that simulates the fact
184 : that a program partially executed before reaching the syscall.
185 : Here we want to test what happens when the program starts from the beginning. */
186 320415 : vm->reg[0] = input->vm_ctx.r0;
187 : // vm->reg[1] = input->vm_ctx.r1; // do not override
188 320415 : vm->reg[2] = input->vm_ctx.r2;
189 320415 : vm->reg[3] = input->vm_ctx.r3;
190 320415 : vm->reg[4] = input->vm_ctx.r4;
191 320415 : vm->reg[5] = input->vm_ctx.r5;
192 320415 : vm->reg[6] = input->vm_ctx.r6;
193 320415 : vm->reg[7] = input->vm_ctx.r7;
194 320415 : vm->reg[8] = input->vm_ctx.r8;
195 320415 : vm->reg[9] = input->vm_ctx.r9;
196 : // vm->reg[10] = input->vm_ctx.r10; // do not override
197 : // vm->reg[11] = input->vm_ctx.r11; // do not override
198 :
199 : // Propagate the acc_regions_meta to the vm
200 320415 : vm->acc_region_metas = fd_valloc_malloc( valloc, alignof(fd_vm_acc_region_meta_t), sizeof(fd_vm_acc_region_meta_t) * input->vm_ctx.input_data_regions_count );
201 320415 : fd_setup_vm_acc_region_metas( vm->acc_region_metas, vm, vm->instr_ctx );
202 :
203 : // Validate the vm
204 320415 : if ( fd_vm_validate( vm ) != FD_VM_SUCCESS ) {
205 : // custom error, avoid -1 because we use it for "unknown error" in solfuzz-agave
206 213570 : effects->error = -2;
207 213570 : break;
208 213570 : }
209 :
210 106845 : if( input->syscall_invocation.stack_prefix ) {
211 5568 : uchar * stack = input->syscall_invocation.stack_prefix->bytes;
212 5568 : ulong stack_sz = fd_ulong_min(input->syscall_invocation.stack_prefix->size, FD_VM_STACK_MAX);
213 5568 : fd_memcpy( vm->stack, stack, stack_sz );
214 5568 : }
215 :
216 106845 : if( input->syscall_invocation.heap_prefix ) {
217 5565 : uchar * heap = input->syscall_invocation.heap_prefix->bytes;
218 5565 : ulong heap_sz = fd_ulong_min(input->syscall_invocation.heap_prefix->size, FD_VM_HEAP_MAX);
219 5565 : fd_memcpy( vm->heap, heap, heap_sz );
220 5565 : }
221 :
222 : /* Run vm */
223 106845 : int exec_res = 0;
224 106845 : if (!!tracing_enabled) {
225 3 : exec_res = fd_vm_exec_trace( vm );
226 3 : if( DUMP_TRACE ) fd_vm_trace_printf( trace, syscalls );
227 3 : fd_vm_trace_delete( fd_vm_trace_leave( trace ) );
228 106842 : } else {
229 106842 : exec_res = fd_vm_exec_notrace( vm );
230 106842 : }
231 :
232 : /* Agave does not have a SIGCALL error, and instead throws SIGILL */
233 106845 : if( exec_res == FD_VM_ERR_SIGCALL ) exec_res = FD_VM_ERR_SIGILL;
234 106845 : effects->error = -1 * exec_res;
235 :
236 : /* Capture outputs */
237 106845 : effects->cu_avail = vm->cu;
238 106845 : effects->frame_count = vm->frame_cnt;
239 106845 : /* Only capture registers if no error */;
240 106845 : effects->r0 = exec_res ? 0 : vm->reg[0];
241 106845 : effects->r1 = exec_res ? 0 : vm->reg[1];
242 106845 : effects->r2 = exec_res ? 0 : vm->reg[2];
243 106845 : effects->r3 = exec_res ? 0 : vm->reg[3];
244 106845 : effects->r4 = exec_res ? 0 : vm->reg[4];
245 106845 : effects->r5 = exec_res ? 0 : vm->reg[5];
246 106845 : effects->r6 = exec_res ? 0 : vm->reg[6];
247 106845 : effects->r7 = exec_res ? 0 : vm->reg[7];
248 106845 : effects->r8 = exec_res ? 0 : vm->reg[8];
249 106845 : effects->r9 = exec_res ? 0 : vm->reg[9];
250 106845 : effects->r10 = exec_res ? 0 : vm->reg[10];
251 :
252 : /* skip logs since syscalls are stubbed */
253 :
254 : /* CU error is difficult to properly compare as there may have been
255 : valid writes to the memory regions prior to capturing the error. And
256 : the pc might be well past (by an arbitrary amount) the instruction
257 : where the CU error occurred. */
258 106845 : if( exec_res == FD_VM_ERR_SIGCOST ) break;
259 :
260 106842 : effects->pc = vm->pc;
261 :
262 106842 : if( vm->heap_max > 0 ) {
263 5565 : effects->heap = FD_SCRATCH_ALLOC_APPEND(
264 5565 : l, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( vm->heap_max ) );
265 0 : effects->heap->size = (uint)vm->heap_max;
266 5565 : fd_memcpy( effects->heap->bytes, vm->heap, vm->heap_max );
267 5565 : }
268 :
269 : /* Compress stack by removing right-most 0s.
270 : This reduces the total size of effects/fixtures when stack is not used,
271 : otherwise each would waste 256kB. */
272 106842 : int rtrim_sz;
273 28007759865 : for( rtrim_sz=FD_VM_STACK_MAX-1; rtrim_sz>=0; rtrim_sz-- ) {
274 28007658588 : if( vm->stack[rtrim_sz] != 0 ) break;
275 28007658588 : }
276 106842 : if( rtrim_sz > 0 || (vm->stack[0] != 0) ) {
277 5565 : effects->stack = FD_SCRATCH_ALLOC_APPEND(
278 5565 : l, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( FD_VM_STACK_MAX ) );
279 0 : effects->stack->size = (uint)rtrim_sz+1;
280 5565 : fd_memcpy( effects->stack->bytes, vm->stack, (ulong)rtrim_sz+1 );
281 5565 : }
282 :
283 106842 : effects->rodata = FD_SCRATCH_ALLOC_APPEND(
284 106842 : l, alignof(pb_bytes_array_t), PB_BYTES_ARRAY_T_ALLOCSIZE( rodata_sz ) );
285 0 : effects->rodata->size = (uint)rodata_sz;
286 106842 : fd_memcpy( effects->rodata->bytes, rodata, rodata_sz );
287 :
288 : /* Capture input data regions */
289 106842 : ulong tmp_end = FD_SCRATCH_ALLOC_FINI(l, 1UL);
290 106842 : ulong input_data_regions_size = load_from_vm_input_regions( vm->input_mem_regions,
291 106842 : vm->input_mem_regions_cnt,
292 106842 : &effects->input_data_regions,
293 106842 : &effects->input_data_regions_count,
294 106842 : (void *) tmp_end,
295 106842 : fd_ulong_sat_sub( output_end, tmp_end) );
296 106842 : FD_SCRATCH_ALLOC_APPEND( l, 1UL, input_data_regions_size );
297 :
298 :
299 106842 : } while(0);
300 :
301 320415 : ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
302 320415 : *output = effects;
303 320415 : fd_exec_test_instr_context_destroy( runner, instr_ctx );
304 320415 : return actual_end - (ulong)output_buf;
305 320415 : }
306 :
307 :
308 : uint
309 : fd_setup_vm_input_regions( fd_vm_input_region_t * input,
310 : fd_exec_test_input_data_region_t const * test_input,
311 : ulong test_input_count,
312 322629 : fd_spad_t * spad ) {
313 322629 : ulong offset = 0UL;
314 322629 : uint input_idx = 0UL;
315 368181 : for( ulong i=0; i<test_input_count; i++ ) {
316 45552 : fd_exec_test_input_data_region_t const * region = &test_input[i];
317 45552 : pb_bytes_array_t * array = region->content;
318 45552 : if( !array ) {
319 318 : continue; /* skip empty regions https://github.com/anza-xyz/agave/blob/3072c1a72b2edbfa470ca869f1ea891dfb6517f2/programs/bpf_loader/src/serialization.rs#L136 */
320 318 : }
321 :
322 45234 : uchar * haddr = fd_spad_alloc_debug( spad, 8UL, array->size );
323 45234 : fd_memcpy( haddr, array->bytes, array->size );
324 45234 : input[input_idx].vaddr_offset = offset;
325 45234 : input[input_idx].haddr = (ulong)haddr;
326 45234 : input[input_idx].region_sz = array->size;
327 45234 : input[input_idx].is_writable = region->is_writable;
328 :
329 45234 : input_idx++;
330 45234 : offset += array->size;
331 45234 : }
332 322629 : return input_idx; /* return the number of populated regions */
333 322629 : }
334 :
335 :
336 : ulong
337 : load_from_vm_input_regions( fd_vm_input_region_t const * input,
338 : uint input_count,
339 : fd_exec_test_input_data_region_t ** output,
340 : pb_size_t * output_count,
341 : void * output_buf,
342 119553 : ulong output_bufsz ) {
343 : /* pre-flight checks on output buffer size*/
344 119553 : ulong input_regions_total_sz = 0;
345 162012 : for( ulong i=0; i<input_count; i++ ) {
346 42459 : input_regions_total_sz += input[i].region_sz;
347 42459 : }
348 :
349 119553 : if( FD_UNLIKELY( input_regions_total_sz == 0
350 119553 : || output_bufsz < input_regions_total_sz ) ) {
351 111774 : *output = NULL;
352 111774 : *output_count = 0;
353 111774 : return 0;
354 111774 : }
355 :
356 7779 : FD_SCRATCH_ALLOC_INIT( l, output_buf );
357 7779 : *output = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_input_data_region_t),
358 7779 : input_count * sizeof (fd_exec_test_input_data_region_t) );
359 7779 : FD_TEST( *output );
360 7779 : *output_count = input_count;
361 :
362 50238 : for( ulong i=0; i<input_count; i++ ) {
363 42459 : fd_vm_input_region_t const * vm_region = &input[i];
364 42459 : fd_exec_test_input_data_region_t * out_region = &(*output)[i];
365 42459 : out_region->is_writable = vm_region->is_writable;
366 42459 : out_region->offset = vm_region->vaddr_offset;
367 :
368 42459 : out_region->content = FD_SCRATCH_ALLOC_APPEND( l, alignof(pb_bytes_array_t),
369 42459 : PB_BYTES_ARRAY_T_ALLOCSIZE(vm_region->region_sz) );
370 42459 : FD_TEST( out_region->content );
371 42459 : out_region->content->size = vm_region->region_sz;
372 42459 : fd_memcpy( out_region->content->bytes, (void *)vm_region->haddr, vm_region->region_sz );
373 42459 : }
374 :
375 7779 : ulong end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
376 7779 : return end - (ulong)output_buf; /* return the number of bytes written */
377 7779 : }
|