Line data Source code
1 : /* fd_solfuzz_exec.c contains internal executors */
2 :
3 : #include "fd_solfuzz_private.h"
4 : #include "generated/block.pb.h"
5 : #include "generated/invoke.pb.h"
6 : #include "generated/txn.pb.h"
7 : #include "generated/vm.pb.h"
8 : #if FD_HAS_FLATCC
9 : #include "flatbuffers/generated/elf_reader.h"
10 : #endif
11 :
12 : #include "../fd_executor_err.h"
13 : #include <assert.h>
14 :
15 : /*
16 : * fixtures
17 : */
18 :
19 : static int
20 : sol_compat_cmp_binary_strict( void const * effects,
21 : void const * expected,
22 : pb_msgdesc_t const * encode_type,
23 0 : fd_spad_t * spad ) {
24 0 : #define MAX_SZ 32*1024*1024
25 0 : FD_SPAD_FRAME_BEGIN( spad ) {
26 0 : if( effects==NULL ) {
27 0 : FD_LOG_WARNING(( "No output effects" ));
28 0 : return 0;
29 0 : }
30 :
31 : /* Note: Most likely this spad allocation won't fail. If it does, you may need to bump
32 : the allocated spad memory amount in fd_exec_sol_compat.c. */
33 0 : ulong out_sz = MAX_SZ;
34 0 : uchar * out = fd_spad_alloc( spad, 1UL, out_sz );
35 0 : if( !sol_compat_encode( out, &out_sz, effects, encode_type ) ) {
36 0 : FD_LOG_WARNING(( "Error encoding effects" ));
37 0 : return 0;
38 0 : }
39 :
40 0 : ulong exp_sz = MAX_SZ;
41 0 : uchar * exp = fd_spad_alloc( spad, 1UL, exp_sz );
42 0 : if( !sol_compat_encode( exp, &exp_sz, expected, encode_type ) ) {
43 0 : FD_LOG_WARNING(( "Error encoding expected" ));
44 0 : return 0;
45 0 : }
46 :
47 0 : if( out_sz!=exp_sz ) {
48 0 : FD_LOG_WARNING(( "Binary cmp failed: different size. out_sz=%lu exp_sz=%lu", out_sz, exp_sz ));
49 0 : return 0;
50 0 : }
51 0 : if( !fd_memeq( out, exp, out_sz ) ) {
52 0 : FD_LOG_WARNING(( "Binary cmp failed: different values." ));
53 0 : return 0;
54 0 : }
55 :
56 0 : return 1;
57 0 : } FD_SPAD_FRAME_END;
58 0 : #undef MAX_SIZE
59 0 : }
60 :
61 : static int
62 : _diff_txn_acct( fd_exec_test_acct_state_t * expected,
63 0 : fd_exec_test_acct_state_t * actual ) {
64 : /* AcctState -> address (This must hold true when calling this function!) */
65 0 : assert( fd_memeq( expected->address, actual->address, sizeof(fd_pubkey_t) ) );
66 :
67 : /* AcctState -> lamports */
68 0 : if( expected->lamports != actual->lamports ) {
69 0 : FD_LOG_WARNING(( "Lamports mismatch: expected=%lu actual=%lu", expected->lamports, actual->lamports ));
70 0 : return 0;
71 0 : }
72 :
73 : /* AcctState -> data */
74 0 : if( expected->data != NULL || actual->data != NULL ) {
75 0 : if( expected->data == NULL ) {
76 0 : FD_LOG_WARNING(( "Expected account data is NULL, actual is non-NULL" ));
77 0 : return 0;
78 0 : }
79 :
80 0 : if( actual->data == NULL ) {
81 0 : FD_LOG_WARNING(( "Expected account data is NULL, actual is non-NULL" ));
82 0 : return 0;
83 0 : }
84 :
85 0 : if( expected->data->size != actual->data->size ) {
86 0 : FD_LOG_WARNING(( "Account data size mismatch: expected=%u actual=%u", expected->data->size, actual->data->size ));
87 0 : return 0;
88 0 : }
89 :
90 0 : if( !fd_memeq( expected->data->bytes, actual->data->bytes, expected->data->size ) ) {
91 0 : FD_LOG_WARNING(( "Account data mismatch" ));
92 0 : return 0;
93 0 : }
94 0 : }
95 :
96 : /* AcctState -> executable */
97 0 : if( expected->executable != actual->executable ) {
98 0 : FD_LOG_WARNING(( "Executable mismatch: expected=%d actual=%d", expected->executable, actual->executable ));
99 0 : return 0;
100 0 : }
101 :
102 : /* AcctState -> owner */
103 0 : if( !fd_memeq( expected->owner, actual->owner, sizeof(fd_pubkey_t) ) ) {
104 0 : char a[ FD_BASE58_ENCODED_32_SZ ];
105 0 : char b[ FD_BASE58_ENCODED_32_SZ ];
106 0 : FD_LOG_WARNING(( "Owner mismatch: expected=%s, actual=%s", fd_acct_addr_cstr( a, expected->owner ), fd_acct_addr_cstr( b, actual->owner ) ));
107 0 : return 0;
108 0 : }
109 :
110 0 : return 1;
111 0 : }
112 :
113 :
114 : static int
115 : _diff_accounts( fd_exec_test_acct_state_t * expected,
116 : pb_size_t expected_count,
117 : fd_exec_test_acct_state_t * actual,
118 0 : pb_size_t actual_count ) {
119 : // Verify that the number of accounts are the same
120 0 : if( expected_count != actual_count ) {
121 0 : FD_LOG_WARNING(( "Account states count mismatch: expected=%u actual=%u", expected_count, actual_count ));
122 0 : return 0;
123 0 : }
124 :
125 : // Verify that the account states are the same
126 0 : for( ulong i = 0; i < expected_count; ++i ) {
127 0 : uchar found = 0;
128 0 : for( ulong j = 0; j < actual_count; ++j ) {
129 0 : if( fd_memeq( expected[i].address, actual[j].address, sizeof(fd_pubkey_t) ) ) {
130 0 : found = 1;
131 0 : if( !_diff_txn_acct( &expected[i], &actual[j] ) ) {
132 0 : return 0;
133 0 : }
134 0 : break;
135 0 : }
136 0 : }
137 0 : if( !found ) {
138 0 : char a[ FD_BASE58_ENCODED_32_SZ ];
139 0 : FD_LOG_WARNING(( "Account state not found in actual: expected=%s", fd_acct_addr_cstr( a, expected[i].address ) ));
140 0 : return 0;
141 0 : }
142 0 : }
143 :
144 0 : return 1;
145 0 : }
146 :
147 : static int
148 : sol_compat_cmp_txn( fd_exec_test_txn_result_t * expected,
149 0 : fd_exec_test_txn_result_t * actual ) {
150 : /* TxnResult -> executed */
151 0 : if( expected->executed != actual->executed ) {
152 0 : FD_LOG_WARNING(( "Executed mismatch: expected=%d actual=%d", expected->executed, actual->executed ));
153 0 : return 0;
154 0 : }
155 :
156 : /* TxnResult -> sanitization_error */
157 0 : if( expected->sanitization_error != actual->sanitization_error ) {
158 0 : FD_LOG_WARNING(( "Sanitization error mismatch: expected=%d actual=%d", expected->sanitization_error, actual->sanitization_error ));
159 0 : return 0;
160 0 : }
161 :
162 : /* TxnResult -> modified_accounts */
163 0 : if( !_diff_accounts( expected->modified_accounts, expected->modified_accounts_count, actual->modified_accounts, actual->modified_accounts_count ) ) {
164 0 : return 0;
165 0 : }
166 :
167 : /* TxnResult -> rollback_accounts */
168 0 : if( !_diff_accounts( expected->rollback_accounts, expected->rollback_accounts_count, actual->rollback_accounts, actual->rollback_accounts_count ) ) {
169 0 : return 0;
170 0 : }
171 :
172 : /* TxnResult -> is_ok */
173 0 : if( expected->is_ok != actual->is_ok ) {
174 0 : FD_LOG_WARNING(( "Is ok mismatch: expected=%d actual=%d", expected->is_ok, actual->is_ok ));
175 0 : return 0;
176 0 : }
177 :
178 : /* TxnResult -> status */
179 0 : if( expected->status != actual->status ) {
180 0 : FD_LOG_WARNING(( "Status mismatch: expected=%u actual=%u", expected->status, actual->status ));
181 0 : return 0;
182 0 : }
183 :
184 : /* TxnResult -> instruction_error */
185 0 : if( expected->instruction_error != actual->instruction_error ) {
186 0 : FD_LOG_WARNING(( "Instruction error mismatch: expected=%u actual=%u", expected->instruction_error, actual->instruction_error ));
187 0 : return 0;
188 0 : }
189 :
190 0 : if( expected->instruction_error ) {
191 : /* TxnResult -> instruction_error_index */
192 0 : if( expected->instruction_error_index != actual->instruction_error_index ) {
193 0 : FD_LOG_WARNING(( "Instruction error index mismatch: expected=%u actual=%u", expected->instruction_error_index, actual->instruction_error_index ));
194 0 : return 0;
195 0 : }
196 :
197 : /* TxnResult -> custom_error */
198 0 : if( expected->instruction_error == (ulong) -FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR && expected->custom_error != actual->custom_error ) {
199 0 : FD_LOG_WARNING(( "Custom error mismatch: expected=%u actual=%u", expected->custom_error, actual->custom_error ));
200 0 : return 0;
201 0 : }
202 0 : }
203 :
204 : /* TxnResult -> return_data */
205 0 : if( expected->return_data != NULL || actual->return_data != NULL ) {
206 0 : if( expected->return_data == NULL ) {
207 0 : FD_LOG_WARNING(( "Expected return data is NULL, actual is non-NULL" ));
208 0 : return 0;
209 0 : }
210 :
211 0 : if( actual->return_data == NULL ) {
212 0 : FD_LOG_WARNING(( "Expected return data is NULL, actual is non-NULL" ));
213 0 : return 0;
214 0 : }
215 :
216 0 : if( expected->return_data->size != actual->return_data->size ) {
217 0 : FD_LOG_WARNING(( "Return data size mismatch: expected=%u actual=%u", expected->return_data->size, actual->return_data->size ));
218 0 : return 0;
219 0 : }
220 :
221 0 : if( !fd_memeq( expected->return_data->bytes, actual->return_data->bytes, expected->return_data->size ) ) {
222 0 : FD_LOG_WARNING(( "Return data mismatch" ));
223 0 : return 0;
224 0 : }
225 0 : }
226 :
227 : /* TxnResult -> executed_units */
228 0 : if( expected->executed_units != actual->executed_units ) {
229 0 : FD_LOG_WARNING(( "Executed units mismatch: expected=%lu actual=%lu", expected->executed_units, actual->executed_units ));
230 0 : return 0;
231 0 : }
232 :
233 : /* TxnResult -> fee_details */
234 0 : if( expected->has_fee_details != actual->has_fee_details ) {
235 0 : FD_LOG_WARNING(( "Has fee details mismatch: expected=%d actual=%d", expected->has_fee_details, actual->has_fee_details ));
236 0 : return 0;
237 0 : }
238 :
239 0 : if( expected->has_fee_details ) {
240 0 : if( expected->fee_details.transaction_fee != actual->fee_details.transaction_fee ) {
241 0 : FD_LOG_WARNING(( "Transaction fee mismatch: expected=%lu actual=%lu", expected->fee_details.transaction_fee, actual->fee_details.transaction_fee ));
242 0 : return 0;
243 0 : }
244 :
245 0 : if( expected->fee_details.prioritization_fee != actual->fee_details.prioritization_fee ) {
246 0 : FD_LOG_WARNING(( "Priority fee mismatch: expected=%lu actual=%lu", expected->fee_details.prioritization_fee, actual->fee_details.prioritization_fee ));
247 0 : return 0;
248 0 : }
249 0 : }
250 :
251 : /* TxnResult -> loaded_accounts_data_size */
252 0 : if( expected->loaded_accounts_data_size != actual->loaded_accounts_data_size ) {
253 0 : FD_LOG_WARNING(( "Loaded accounts data size mismatch: expected=%lu actual=%lu", expected->loaded_accounts_data_size, actual->loaded_accounts_data_size ));
254 0 : return 0;
255 0 : }
256 :
257 0 : return 1;
258 0 : }
259 :
260 : int
261 : fd_solfuzz_pb_instr_fixture( fd_solfuzz_runner_t * runner,
262 : uchar const * in,
263 0 : ulong in_sz ) {
264 : // Decode fixture
265 0 : fd_exec_test_instr_fixture_t fixture[1] = {0};
266 0 : void * res = sol_compat_decode_lenient( &fixture, in, in_sz, &fd_exec_test_instr_fixture_t_msg );
267 0 : if( !res ) {
268 0 : FD_LOG_WARNING(( "Invalid instr fixture." ));
269 0 : return 0;
270 0 : }
271 :
272 0 : int ok = 0;
273 : // Execute
274 0 : void * output = NULL;
275 0 : fd_solfuzz_pb_execute_wrapper( runner, &fixture->input, &output, fd_solfuzz_pb_instr_run );
276 :
277 : // Compare effects
278 0 : ok = sol_compat_cmp_binary_strict( output, &fixture->output, &fd_exec_test_instr_effects_t_msg, runner->spad );
279 :
280 : // Cleanup
281 0 : pb_release( &fd_exec_test_instr_fixture_t_msg, fixture );
282 0 : return ok;
283 0 : }
284 :
285 : int
286 : fd_solfuzz_pb_txn_fixture( fd_solfuzz_runner_t * runner,
287 : uchar const * in,
288 0 : ulong in_sz ) {
289 : // Decode fixture
290 0 : fd_exec_test_txn_fixture_t fixture[1] = {0};
291 0 : void * res = sol_compat_decode_lenient( &fixture, in, in_sz, &fd_exec_test_txn_fixture_t_msg );
292 0 : if( !res ) {
293 0 : FD_LOG_WARNING(( "Invalid txn fixture." ));
294 0 : return 0;
295 0 : }
296 :
297 0 : fd_spad_push( runner->spad );
298 0 : int ok = 0;
299 0 : void * output = NULL;
300 0 : fd_solfuzz_pb_execute_wrapper( runner, &fixture->input, &output, fd_solfuzz_pb_txn_run );
301 0 : if( FD_LIKELY( output ) ) {
302 : // Compare effects
303 0 : fd_exec_test_txn_result_t * effects = output;
304 0 : ok = sol_compat_cmp_txn( &fixture->output, effects );
305 0 : }
306 0 : fd_spad_pop( runner->spad );
307 :
308 : // Cleanup
309 0 : pb_release( &fd_exec_test_txn_fixture_t_msg, fixture );
310 0 : return ok;
311 0 : }
312 :
313 : int
314 : fd_solfuzz_pb_block_fixture( fd_solfuzz_runner_t * runner,
315 : uchar const * in,
316 0 : ulong in_sz ) {
317 : // Decode fixture
318 0 : fd_exec_test_block_fixture_t fixture[1] = {0};
319 0 : void * res = sol_compat_decode_lenient( &fixture, in, in_sz, &fd_exec_test_block_fixture_t_msg );
320 0 : if( !res ) {
321 0 : FD_LOG_WARNING(( "Invalid block fixture" ));
322 0 : return 0;
323 0 : }
324 :
325 0 : fd_spad_push( runner->spad );
326 0 : void * output = NULL;
327 0 : fd_solfuzz_pb_execute_wrapper( runner, &fixture->input, &output, fd_solfuzz_pb_block_run );
328 0 : int ok = sol_compat_cmp_binary_strict( output, &fixture->output, &fd_exec_test_block_effects_t_msg, runner->spad );
329 0 : fd_spad_pop( runner->spad );
330 :
331 : // Cleanup
332 0 : pb_release( &fd_exec_test_block_fixture_t_msg, fixture );
333 0 : return ok;
334 0 : }
335 :
336 : int
337 : fd_solfuzz_pb_syscall_fixture( fd_solfuzz_runner_t * runner,
338 : uchar const * in,
339 0 : ulong in_sz ) {
340 : // Decode fixture
341 0 : fd_exec_test_syscall_fixture_t fixture[1] = {0};
342 0 : if( !sol_compat_decode_lenient( &fixture, in, in_sz, &fd_exec_test_syscall_fixture_t_msg ) ) {
343 0 : FD_LOG_WARNING(( "Invalid syscall fixture." ));
344 0 : return 0;
345 0 : }
346 :
347 0 : fd_spad_push( runner->spad );
348 0 : void * output = NULL;
349 0 : fd_solfuzz_pb_execute_wrapper( runner, &fixture->input, &output, fd_solfuzz_pb_syscall_run );
350 0 : int ok = sol_compat_cmp_binary_strict( output, &fixture->output, &fd_exec_test_syscall_effects_t_msg, runner->spad );
351 0 : fd_spad_pop( runner->spad );
352 :
353 : // Cleanup
354 0 : pb_release( &fd_exec_test_syscall_fixture_t_msg, fixture );
355 0 : return ok;
356 0 : }
357 :
358 : #if FD_HAS_FLATCC
359 :
360 : /* Flatbuffers */
361 : static int
362 : sol_compat_fb_cmp_elf_loader( SOL_COMPAT_NS(ELFLoaderEffects_table_t) expected,
363 0 : SOL_COMPAT_NS(ELFLoaderEffects_table_t) actual ) {
364 : /* Compare err_code */
365 0 : if( FD_UNLIKELY( SOL_COMPAT_NS(ELFLoaderEffects_err_code( expected ))!=SOL_COMPAT_NS(ELFLoaderEffects_err_code( actual )) ) ) {
366 0 : FD_LOG_WARNING(( "Err code mismatch: expected=%u actual=%u", SOL_COMPAT_NS(ELFLoaderEffects_err_code( expected )), SOL_COMPAT_NS(ELFLoaderEffects_err_code( actual )) ));
367 0 : return 0;
368 0 : }
369 :
370 : /* Compare rodata_hash */
371 0 : SOL_COMPAT_NS(XXHash_struct_t) exp_rodata_hash = SOL_COMPAT_NS(ELFLoaderEffects_rodata_hash( expected ));
372 0 : SOL_COMPAT_NS(XXHash_struct_t) act_rodata_hash = SOL_COMPAT_NS(ELFLoaderEffects_rodata_hash( actual ));
373 :
374 0 : if( (!exp_rodata_hash && !act_rodata_hash) ) {
375 : // Both are NULL, considered matching
376 0 : } else if( FD_UNLIKELY( (exp_rodata_hash && !act_rodata_hash) || (!exp_rodata_hash && act_rodata_hash) ) ) {
377 0 : FD_LOG_WARNING(( "Rodata hash presence mismatch: expected=%p actual=%p", (void*)exp_rodata_hash, (void*)act_rodata_hash ));
378 0 : return 0;
379 0 : } else if( FD_UNLIKELY( memcmp( &exp_rodata_hash->hash, &act_rodata_hash->hash, sizeof(exp_rodata_hash->hash) ) ) ) {
380 0 : FD_LOG_WARNING(( "Rodata hash mismatch: expected=%lu actual=%lu", *((ulong*)exp_rodata_hash->hash), *((ulong*)act_rodata_hash->hash) ));
381 0 : return 0;
382 0 : }
383 :
384 : /* Compare text_cnt */
385 0 : if( FD_UNLIKELY( SOL_COMPAT_NS(ELFLoaderEffects_text_cnt( expected ))!=SOL_COMPAT_NS(ELFLoaderEffects_text_cnt( actual )) ) ) {
386 0 : FD_LOG_WARNING(( "Text cnt mismatch: expected=%lu actual=%lu",
387 0 : SOL_COMPAT_NS(ELFLoaderEffects_text_cnt( expected )),
388 0 : SOL_COMPAT_NS(ELFLoaderEffects_text_cnt( actual )) ));
389 0 : return 0;
390 0 : }
391 :
392 : /* Compare text_off */
393 0 : if( FD_UNLIKELY( SOL_COMPAT_NS(ELFLoaderEffects_text_off( expected ))!=SOL_COMPAT_NS(ELFLoaderEffects_text_off( actual )) ) ) {
394 0 : FD_LOG_WARNING(( "Text off mismatch: expected=%lu actual=%lu",
395 0 : SOL_COMPAT_NS(ELFLoaderEffects_text_off( expected )),
396 0 : SOL_COMPAT_NS(ELFLoaderEffects_text_off( actual )) ));
397 0 : return 0;
398 0 : }
399 :
400 : /* Compare entry_pc */
401 0 : if( FD_UNLIKELY( SOL_COMPAT_NS(ELFLoaderEffects_entry_pc( expected )) != SOL_COMPAT_NS(ELFLoaderEffects_entry_pc( actual )) ) ) {
402 0 : FD_LOG_WARNING(( "Entry pc mismatch: expected=%lu actual=%lu",
403 0 : SOL_COMPAT_NS(ELFLoaderEffects_entry_pc( expected )),
404 0 : SOL_COMPAT_NS(ELFLoaderEffects_entry_pc( actual )) ));
405 0 : return 0;
406 0 : }
407 :
408 : /* Compare calldests_hash */
409 0 : SOL_COMPAT_NS(XXHash_struct_t) exp_calldests_hash = SOL_COMPAT_NS(ELFLoaderEffects_calldests_hash( expected ));
410 0 : SOL_COMPAT_NS(XXHash_struct_t) act_calldests_hash = SOL_COMPAT_NS(ELFLoaderEffects_calldests_hash( actual ));
411 :
412 0 : if( (!exp_calldests_hash && !act_calldests_hash) ) {
413 : // Both are NULL, considered matching
414 0 : } else if( FD_UNLIKELY( (exp_calldests_hash && !act_calldests_hash) || (!exp_calldests_hash && act_calldests_hash) ) ) {
415 0 : FD_LOG_WARNING(( "Calldests hash presence mismatch: expected=%p actual=%p", (void*)exp_calldests_hash, (void*)act_calldests_hash ));
416 0 : return 0;
417 0 : } else if( FD_UNLIKELY( memcmp( &exp_calldests_hash->hash, &act_calldests_hash->hash, sizeof(exp_calldests_hash->hash) ) ) ) {
418 0 : FD_LOG_WARNING(( "Calldests hash mismatch: expected=%lu actual=%lu", *((ulong*)exp_calldests_hash->hash), *((ulong*)act_calldests_hash->hash) ));
419 0 : return 0;
420 0 : }
421 :
422 0 : return 1;
423 0 : }
424 :
425 : int
426 : fd_solfuzz_fb_elf_loader_fixture( fd_solfuzz_runner_t * runner,
427 0 : uchar const * in ) {
428 : /* Decode */
429 0 : SOL_COMPAT_NS(ELFLoaderFixture_table_t) fixture = SOL_COMPAT_NS(ELFLoaderFixture_as_root( in ));
430 0 : if( FD_UNLIKELY( !fixture ) ) return 0;
431 :
432 : /* Execute */
433 0 : SOL_COMPAT_NS(ELFLoaderCtx_table_t) input = SOL_COMPAT_NS(ELFLoaderFixture_input( fixture ));
434 0 : if( FD_UNLIKELY( !input ) ) return 0;
435 :
436 0 : int err = fd_solfuzz_fb_execute_wrapper( runner, input, fd_solfuzz_fb_elf_loader_run );
437 0 : if( FD_UNLIKELY( err==SOL_COMPAT_V2_FAILURE ) ) return err;
438 :
439 : /* Compare */
440 0 : FD_SPAD_FRAME_BEGIN( runner->spad ) {
441 0 : ulong buffer_sz = flatcc_builder_get_buffer_size( runner->fb_builder );
442 0 : uchar * actual_buf = fd_spad_alloc( runner->spad, 1UL, buffer_sz );
443 0 : flatcc_builder_copy_buffer( runner->fb_builder, actual_buf, buffer_sz );
444 :
445 0 : SOL_COMPAT_NS(ELFLoaderEffects_table_t) expected = SOL_COMPAT_NS(ELFLoaderEffects_as_root( actual_buf ));
446 0 : SOL_COMPAT_NS(ELFLoaderEffects_table_t) actual = SOL_COMPAT_NS(ELFLoaderFixture_output( fixture ));
447 0 : if( FD_UNLIKELY( !expected || !actual ) ) return 0;
448 :
449 0 : return sol_compat_fb_cmp_elf_loader( expected, actual );
450 0 : } FD_SPAD_FRAME_END;
451 0 : }
452 :
453 : #endif /* FD_HAS_FLATCC */
|