Line data Source code
1 : #include "fd_solfuzz_private.h"
2 : #include "fd_instr_harness.h"
3 : #include "../fd_executor.h"
4 : #include "../fd_runtime.h"
5 : #include "../program/fd_bpf_loader_program.h"
6 : #include "../program/fd_precompiles.h"
7 : #include "../fd_system_ids.h"
8 : #include "../../progcache/fd_progcache_admin.h"
9 : #include "../../log_collector/fd_log_collector.h"
10 :
11 : void
12 : fd_solfuzz_pb_instr_ctx_create( fd_solfuzz_runner_t * runner,
13 : fd_exec_instr_ctx_t * ctx,
14 0 : fd_exec_test_instr_context_t const * test_ctx ) {
15 :
16 0 : memset( ctx, 0, sizeof(fd_exec_instr_ctx_t) );
17 :
18 : /* Create temporary fork for account loading */
19 :
20 0 : runner->bank->progcache_fork_id = fd_progcache_attach_child( runner->progcache->join, fd_progcache_fork_id_initial() );
21 0 : runner->bank->accdb_fork_id = fd_accdb_attach_child( runner->accdb, runner->root_fork_id );
22 0 : runner->bank->parent_accdb_fork_id = runner->bank->accdb_fork_id;
23 :
24 0 : fd_txn_in_t * txn_in = fd_spad_alloc( runner->spad, alignof(fd_txn_in_t), sizeof(fd_txn_in_t) );
25 0 : fd_txn_out_t * txn_out = fd_spad_alloc( runner->spad, alignof(fd_txn_out_t), sizeof(fd_txn_out_t) );
26 :
27 0 : fd_log_collector_t * log = fd_spad_alloc( runner->spad, alignof(fd_log_collector_t), sizeof(fd_log_collector_t) );
28 :
29 0 : fd_runtime_t * runtime = runner->runtime;
30 :
31 0 : runtime->log.log_collector = log;
32 :
33 0 : ctx->txn_out = txn_out;
34 0 : ctx->txn_in = txn_in;
35 :
36 0 : fd_memset( txn_out->accounts.keys, 0, sizeof(fd_pubkey_t)*MAX_TX_ACCOUNT_LOCKS );
37 0 : fd_memset( runtime->accounts.account, 0, sizeof(fd_acc_t)*MAX_TX_ACCOUNT_LOCKS );
38 0 : fd_memset( runtime->accounts.executable, 0, sizeof(fd_acc_t)*MAX_TX_ACCOUNT_LOCKS );
39 0 : for( ulong j=0UL; j<MAX_TX_ACCOUNT_LOCKS; j++ ) {
40 0 : txn_out->accounts.account[ j ] = &runtime->accounts.account[ j ];
41 0 : txn_out->accounts.executable[ j ] = &runtime->accounts.executable[ j ];
42 0 : }
43 0 : txn_out->accounts.executable_cnt = 0UL;
44 0 : fd_memset( txn_out->accounts.executable_from_parent, 0, sizeof(txn_out->accounts.executable_from_parent) );
45 0 : fd_memset( txn_out->accounts.executable_pd_write, 0, sizeof(txn_out->accounts.executable_pd_write) );
46 0 : txn_out->accounts.executable_skipped_cnt = 0;
47 :
48 : /* Bank manager */
49 0 : fd_banks_clear_bank( runner->banks, runner->bank, 4UL );
50 :
51 : /* Restore features */
52 0 : FD_TEST( test_ctx->has_features );
53 0 : fd_features_t * features = &runner->bank->f.features;
54 0 : fd_exec_test_feature_set_t const * feature_set = &test_ctx->features;
55 0 : FD_TEST( fd_solfuzz_pb_restore_features( features, feature_set ) );
56 :
57 : /* Blockhash queue init */
58 0 : ulong blockhash_seed; FD_TEST( fd_rng_secure( &blockhash_seed, sizeof(ulong) ) );
59 0 : fd_blockhashes_t * blockhashes = fd_blockhashes_init( &runner->bank->f.block_hash_queue, blockhash_seed );
60 0 : fd_memset( fd_blockhash_deq_push_tail_nocopy( blockhashes->d.deque ), 0, sizeof(fd_hash_t) );
61 :
62 : /* Set up mock txn descriptor and payload
63 : FIXME: More fields may need to be initialized. This seems to be
64 : the minimal set of fields needed to retain full context for
65 : precompile execution. */
66 0 : fd_txn_p_t * txn = fd_spad_alloc_check( runner->spad, alignof(fd_txn_p_t), sizeof(fd_txn_p_t) );
67 0 : fd_txn_t * txn_descriptor = TXN( txn );
68 0 : if( test_ctx->data ) {
69 0 : memcpy( txn->payload, test_ctx->data->bytes, test_ctx->data->size );
70 0 : txn->payload_sz = test_ctx->data->size;
71 0 : } else {
72 0 : txn->payload_sz = 0;
73 0 : }
74 0 : txn_descriptor->transaction_version = FD_TXN_VLEGACY;
75 0 : txn_descriptor->acct_addr_cnt = (ushort)test_ctx->accounts_count;
76 0 : txn_descriptor->instr_cnt = 1;
77 0 : txn_descriptor->instr[0] = (fd_txn_instr_t) {
78 0 : .acct_cnt = (ushort)test_ctx->accounts_count,
79 0 : .data_off = 0,
80 0 : .data_sz = (ushort)txn->payload_sz,
81 0 : };
82 :
83 0 : runtime->log.enable_log_collector = 0;
84 :
85 0 : fd_compute_budget_details_new( &txn_out->details.compute_budget );
86 0 : runtime->instr.stack_sz = 0;
87 0 : txn_out->accounts.cnt = 0UL;
88 0 : txn_out->accounts.executable_cnt = 0UL;
89 :
90 0 : txn_out->details.loaded_accounts_data_size = 0UL;
91 0 : txn_out->details.accounts_resize_delta = 0L;
92 :
93 0 : memset( txn_out->details.return_data.program_id.key, 0, sizeof(fd_pubkey_t) );
94 0 : txn_out->details.return_data.len = 0;
95 :
96 0 : runtime->log.capture_ctx = NULL;
97 0 : runtime->log.dump_proto_ctx = NULL;
98 0 : runtime->log.txn_dump_ctx = NULL;
99 :
100 0 : runtime->instr.trace_length = 1UL;
101 :
102 0 : txn_out->err.exec_err = 0;
103 0 : txn_out->err.exec_err_kind = FD_EXECUTOR_ERR_KIND_NONE;
104 0 : runtime->instr.current_idx = 0;
105 :
106 0 : txn_in->txn = txn;
107 0 : txn_out->details.compute_budget.compute_unit_limit = test_ctx->cu_avail;
108 0 : txn_out->details.compute_budget.compute_meter = test_ctx->cu_avail;
109 0 : runtime->log.enable_vm_tracing = runner->enable_vm_tracing;
110 0 : runtime->log.tracing_mem = runner->enable_vm_tracing ?
111 0 : fd_spad_alloc_check( runner->spad, FD_RUNTIME_VM_TRACE_STATIC_ALIGN, FD_RUNTIME_VM_TRACE_STATIC_FOOTPRINT * FD_MAX_INSTRUCTION_STACK_DEPTH ) :
112 0 : NULL;
113 :
114 : /* Set up instruction context */
115 0 : fd_instr_info_t * info = &runtime->instr.trace[ 0UL ];
116 0 : memset( info, 0, sizeof(fd_instr_info_t) );
117 0 : info->stack_height = 1;
118 :
119 0 : if( test_ctx->data ) {
120 0 : if( FD_UNLIKELY( test_ctx->data->size>FD_INSTR_DATA_MAX ) ) {
121 0 : FD_LOG_ERR(( "invariant violation: instr data sz is too large %u > %lu", test_ctx->data->size, FD_INSTR_DATA_MAX ));
122 0 : }
123 0 : info->data_sz = (ushort)test_ctx->data->size;
124 0 : memcpy( info->data, test_ctx->data->bytes, info->data_sz );
125 0 : }
126 :
127 : /* Prepare borrowed account table (correctly handles aliasing) */
128 :
129 0 : if( FD_UNLIKELY( test_ctx->accounts_count > MAX_TX_ACCOUNT_LOCKS ) ) {
130 0 : FD_LOG_ERR(( "invariant violation: too many accounts (%lu > %lu)",
131 0 : (ulong)test_ctx->accounts_count, (ulong)MAX_TX_ACCOUNT_LOCKS ));
132 0 : }
133 :
134 : /* Load accounts from input */
135 :
136 : /* Mimic Agave's mock_compile_message: put the program + referenced
137 : instruction accounts in the first txn slots, the rest after. */
138 0 : uchar account_in_message[ MAX_TX_ACCOUNT_LOCKS ] = {0};
139 0 : uint input_txn_idx [ MAX_TX_ACCOUNT_LOCKS ];
140 :
141 0 : for( ulong i=0UL; i<test_ctx->instr_accounts_count; i++ ) {
142 0 : uint index = test_ctx->instr_accounts[ i ].index;
143 0 : if( FD_UNLIKELY( index>=test_ctx->accounts_count ) ) {
144 0 : FD_LOG_ERR(( "invariant violation: instruction account index out of range (%u >= %u)",
145 0 : index, test_ctx->accounts_count ));
146 0 : }
147 0 : account_in_message[ index ] = 1;
148 0 : }
149 :
150 0 : ulong program_idx = ULONG_MAX;
151 0 : for( ulong i=0UL; i<test_ctx->accounts_count; i++ ) {
152 0 : if( !memcmp( test_ctx->accounts[ i ].address, test_ctx->program_id, sizeof(fd_pubkey_t) ) ) {
153 0 : account_in_message[ i ] = 1;
154 0 : program_idx = i;
155 0 : break;
156 0 : }
157 0 : }
158 :
159 : /* Ensure the program id is in the set of accounts */
160 0 : FD_TEST( program_idx!=ULONG_MAX );
161 :
162 : /* Compile the message by filling the transaction accounts with
163 : only accounts referenced by the instruction. */
164 0 : uint message_account_cnt = 0U;
165 0 : uint tail_txn_idx = test_ctx->accounts_count;
166 0 : for( ulong i=0UL; i<test_ctx->accounts_count; i++ ) {
167 0 : input_txn_idx[ i ] = account_in_message[ i ] ? message_account_cnt++ : --tail_txn_idx;
168 0 : }
169 :
170 0 : info->program_id = (uchar)input_txn_idx[ program_idx ];
171 0 : txn_out->accounts.cnt = message_account_cnt;
172 :
173 0 : for( ulong j=0UL; j < test_ctx->accounts_count; j++ ) {
174 0 : if( !account_in_message[j] ) continue;
175 :
176 0 : ulong txn_idx = input_txn_idx[j];
177 0 : fd_pubkey_t * acc_key = (fd_pubkey_t *)test_ctx->accounts[j].address;
178 :
179 0 : uint dlen = test_ctx->accounts[j].data ? test_ctx->accounts[j].data->size : 0U;
180 0 : uchar * data_buf = fd_spad_alloc( runner->spad, FD_ACCOUNT_REC_ALIGN, FD_RUNTIME_ACC_SZ_MAX );
181 0 : if( dlen ) {
182 0 : fd_memcpy( data_buf, test_ctx->accounts[j].data->bytes, dlen );
183 0 : }
184 :
185 : /* Initialize entry with in-memory account data (no DB backing) */
186 0 : fd_acc_t * acc = txn_out->accounts.account[txn_idx];
187 0 : memcpy( acc->pubkey, acc_key->key, 32 );
188 0 : memcpy( acc->owner, test_ctx->accounts[j].owner, 32 );
189 0 : acc->lamports = test_ctx->accounts[j].lamports;
190 0 : acc->executable = test_ctx->accounts[j].executable;
191 0 : acc->data_len = dlen;
192 0 : acc->data = data_buf;
193 0 : acc->_writable = 1;
194 0 : acc->commit = 0;
195 :
196 0 : txn_out->accounts.is_writable[txn_idx] = 1U;
197 0 : runtime->accounts.refcnt[txn_idx] = 0UL;
198 0 : txn_out->accounts.keys[txn_idx] = *acc_key;
199 0 : }
200 :
201 : /* Load in executable accounts */
202 0 : for( ulong i=0UL; i<test_ctx->accounts_count; i++ ) {
203 0 : fd_exec_test_acct_state_t const * prog = &test_ctx->accounts[i];
204 0 : fd_pubkey_t const * owner = fd_type_pun_const( prog->owner );
205 :
206 0 : if( !fd_executor_pubkey_is_bpf_loader( owner ) ) {
207 0 : continue;
208 0 : }
209 :
210 0 : if( FD_UNLIKELY( !memcmp( owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
211 0 : if( FD_UNLIKELY( !prog->data ) ) continue;
212 :
213 0 : fd_bpf_state_t program_loader_state[1];
214 0 : int err = fd_bpf_loader_program_get_state2( prog->data->bytes, prog->data->size, program_loader_state );
215 0 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
216 0 : continue;
217 0 : }
218 :
219 0 : if( program_loader_state->discriminant!=FD_BPF_STATE_PROGRAM ) {
220 0 : continue;
221 0 : }
222 :
223 0 : fd_pubkey_t * programdata_acc = &program_loader_state->inner.program.programdata_address;
224 :
225 0 : fd_exec_test_acct_state_t const * pd = NULL;
226 0 : for( ulong j=0UL; j<test_ctx->accounts_count; j++ ) {
227 0 : if( !memcmp( test_ctx->accounts[j].address, programdata_acc, sizeof(fd_pubkey_t) ) ) {
228 0 : pd = &test_ctx->accounts[j];
229 0 : break;
230 0 : }
231 0 : }
232 0 : if( FD_UNLIKELY( pd==NULL || !pd->data ) ) {
233 0 : continue;
234 0 : }
235 :
236 0 : FD_TEST( txn_out->accounts.executable_cnt < MAX_TX_ACCOUNT_LOCKS );
237 0 : fd_acc_t * exe = txn_out->accounts.executable[ txn_out->accounts.executable_cnt ];
238 0 : memcpy( exe->pubkey, programdata_acc->key, 32 );
239 0 : memcpy( exe->owner, pd->owner, sizeof(fd_pubkey_t) );
240 : /* Agave loads a program into its ProgramCache from the
241 : programdata bytes independently of the on-chain lamports
242 : snapshot. An instruction fixture may capture a programdata
243 : account with zero lamports (a real 0-lamport account would
244 : otherwise have all its other fields cleared between
245 : transactions), and still expect the program to execute. The
246 : runtime treats an executable account as "deployed" only when it
247 : exists, so present the programdata as existing whenever it
248 : carries program data. This executable account is read-only on
249 : the invoke path and its lamports are never consumed, only used
250 : as an existence gate. */
251 0 : exe->lamports = ( !pd->lamports && pd->data->size ) ? 1UL : pd->lamports;
252 0 : exe->executable = pd->executable;
253 0 : exe->data_len = pd->data->size;
254 0 : exe->data = (uchar *)pd->data->bytes;
255 0 : txn_out->accounts.executable_cnt++;
256 0 : }
257 0 : }
258 :
259 : /* Load instruction accounts */
260 :
261 0 : if( FD_UNLIKELY( test_ctx->instr_accounts_count > FD_INSTR_ACCT_MAX ) ) {
262 0 : FD_LOG_ERR(( "invariant violation: too many instruction accounts (%lu > %lu)",
263 0 : (ulong)test_ctx->instr_accounts_count, (ulong)FD_INSTR_ACCT_MAX ));
264 0 : }
265 :
266 : /* Restore sysvar cache */
267 0 : fd_sysvar_cache_t * sysvar_cache = &runner->bank->f.sysvar_cache;
268 0 : ctx->sysvar_cache = sysvar_cache;
269 0 : for( ulong i=0UL; i<test_ctx->accounts_count; i++ ) {
270 0 : fd_exec_test_acct_state_t const * account = &test_ctx->accounts[i];
271 0 : if( FD_UNLIKELY( !account->data ) ) continue;
272 :
273 0 : fd_pubkey_t const * address = fd_type_pun_const( account->address );
274 0 : fd_sysvar_cache_restore_one( sysvar_cache,
275 0 : address,
276 0 : account->lamports,
277 0 : account->data->bytes,
278 0 : account->data->size );
279 0 : }
280 :
281 0 : ctx->runtime = runtime;
282 :
283 0 : fd_sol_sysvar_clock_t clock_[1];
284 0 : fd_sol_sysvar_clock_t * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, clock_ );
285 0 : FD_TEST( clock );
286 0 : runner->bank->f.slot = clock->slot;
287 :
288 0 : runner->bank->progcache_fork_id = fd_progcache_attach_child( runner->progcache->join, runner->bank->progcache_fork_id );
289 :
290 0 : fd_epoch_schedule_t epoch_schedule_[1];
291 0 : fd_epoch_schedule_t * epoch_schedule = fd_sysvar_cache_epoch_schedule_read( ctx->sysvar_cache, epoch_schedule_ );
292 0 : FD_TEST( epoch_schedule );
293 0 : runner->bank->f.epoch_schedule = *epoch_schedule;
294 :
295 0 : fd_rent_t rent_[1];
296 0 : fd_rent_t * rent = fd_sysvar_cache_rent_read( ctx->sysvar_cache, rent_ );
297 0 : FD_TEST( rent );
298 0 : runner->bank->f.rent = *rent;
299 :
300 0 : if( !fd_sysvar_cache_recent_hashes_is_empty( sysvar_cache ) ) {
301 0 : uchar const * rbh_data = sysvar_cache->bin_recent_hashes;
302 0 : ulong rbh_len = FD_LOAD( ulong, rbh_data );
303 0 : ulong entry_off = sizeof(ulong) + ((rbh_len - 1UL) * 40UL);
304 0 : uchar const * entry = rbh_data + entry_off;
305 0 : FD_TEST( entry_off+40UL <= sysvar_cache->desc[ FD_SYSVAR_recent_hashes_IDX ].data_sz );
306 :
307 0 : fd_blockhashes_t * blockhashes = &runner->bank->f.block_hash_queue;
308 0 : fd_blockhashes_pop_new( blockhashes );
309 0 : fd_hash_t hash = FD_LOAD( fd_hash_t, entry );
310 0 : fd_blockhash_info_t * info = fd_blockhashes_push_new( blockhashes, &hash );
311 0 : info->lamports_per_signature = runner->bank->f.rbh_lamports_per_sig =
312 0 : FD_LOAD( ulong, entry+32UL );
313 0 : }
314 :
315 : /* Agave compiles the instruction into a message, OR-ing duplicate
316 : accounts' writable/signer flags. */
317 0 : uchar instr_is_writable[ FD_TXN_ACCT_ADDR_MAX ] = {0};
318 0 : uchar instr_is_signer [ FD_TXN_ACCT_ADDR_MAX ] = {0};
319 0 : int bpf_upgradeable_present = !memcmp( test_ctx->program_id, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) );
320 0 : for( ulong j=0UL; j < test_ctx->instr_accounts_count; j++ ) {
321 0 : uint index = test_ctx->instr_accounts[j].index;
322 :
323 0 : instr_is_writable[index] = (uchar)( instr_is_writable[index] | test_ctx->instr_accounts[j].is_writable );
324 0 : instr_is_signer[index] = (uchar)( instr_is_signer[index] | test_ctx->instr_accounts[j].is_signer );
325 0 : if( !memcmp( test_ctx->accounts[index].address, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) {
326 0 : bpf_upgradeable_present = 1;
327 0 : }
328 0 : }
329 :
330 0 : uchar acc_idx_seen[ FD_TXN_ACCT_ADDR_MAX ] = {0};
331 0 : for( ulong j=0UL; j < test_ctx->instr_accounts_count; j++ ) {
332 0 : uint index = test_ctx->instr_accounts[j].index;
333 :
334 : /* A program account is demoted to read-only unless the upgradeable
335 : loader is present. */
336 0 : uchar is_writable = instr_is_writable[index];
337 0 : if( !bpf_upgradeable_present && !memcmp( test_ctx->accounts[index].address, test_ctx->program_id, sizeof(fd_pubkey_t) ) ) {
338 0 : is_writable = 0;
339 0 : }
340 :
341 : /* Setup instruction accounts */
342 0 : fd_instr_info_setup_instr_account( info,
343 0 : acc_idx_seen,
344 0 : (ushort)input_txn_idx[index],
345 0 : (ushort)j,
346 0 : (ushort)j,
347 0 : is_writable,
348 0 : instr_is_signer[index] );
349 0 : }
350 0 : info->acct_cnt = (ushort)test_ctx->instr_accounts_count;
351 :
352 0 : ctx->instr = info;
353 0 : ctx->runtime->progcache = runner->progcache;
354 0 : ctx->runtime->accdb = runner->accdb;
355 :
356 0 : runtime->log.enable_log_collector = 0;
357 :
358 0 : fd_log_collector_init( ctx->runtime->log.log_collector, 0 );
359 0 : fd_base58_encode_32( txn_out->accounts.keys[ ctx->instr->program_id ].uc, NULL, ctx->program_id_base58 );
360 0 : }
361 :
362 : void
363 : fd_solfuzz_pb_instr_ctx_destroy( fd_solfuzz_runner_t * runner,
364 0 : fd_exec_instr_ctx_t * ctx ) {
365 0 : if( !ctx ) return;
366 :
367 0 : fd_progcache_reset( runner->progcache->join );
368 :
369 : /* Purge the fork attached in ctx_create so the accdb fork pool slot
370 : is released back for reuse. Without this, repeated harness
371 : invocations (e.g. under a fuzzer) exhaust max_live_slots. */
372 0 : fd_accdb_purge( runner->accdb, runner->bank->accdb_fork_id );
373 0 : int charge_busy = 0;
374 0 : fd_accdb_background( runner->accdb, &charge_busy );
375 :
376 : /* Compact the progcache allocator so empty superblocks are returned
377 : to the workspace. Required for the leak check to pass. */
378 0 : fd_alloc_compact( runner->progcache->join->alloc );
379 0 : }
380 :
381 : /* Txn index for addr among the compiled-message accounts [0,cnt).
382 : Returns ULONG_MAX if not found. */
383 : static ulong
384 0 : instr_harness_idx_of( fd_txn_out_t const * txn_out, uchar const * addr ) {
385 0 : for( ulong i=0UL; i<txn_out->accounts.cnt; i++ ) {
386 0 : if( !memcmp( txn_out->accounts.keys[i].key, addr, sizeof(fd_pubkey_t) ) ) return i;
387 0 : }
388 0 : return ULONG_MAX;
389 0 : }
390 :
391 : ulong
392 : fd_solfuzz_pb_instr_run( fd_solfuzz_runner_t * runner,
393 : void const * input_,
394 : void ** output_,
395 : void * output_buf,
396 0 : ulong output_bufsz ) {
397 0 : fd_exec_test_instr_context_t const * input = fd_type_pun_const( input_ );
398 0 : fd_exec_test_instr_effects_t ** output = fd_type_pun( output_ );
399 :
400 : /* Convert the Protobuf inputs to a fd_exec context */
401 0 : fd_exec_instr_ctx_t ctx[1];
402 0 : fd_solfuzz_pb_instr_ctx_create( runner, ctx, input );
403 :
404 0 : fd_instr_info_t * instr = (fd_instr_info_t *) ctx->instr;
405 :
406 : /* Execute the test */
407 0 : int exec_result = fd_execute_instr( ctx->runtime, runner->bank, ctx->txn_in, ctx->txn_out, instr );
408 :
409 : /* Allocate space to capture outputs */
410 0 : ulong output_end = (ulong)output_buf + output_bufsz;
411 0 : FD_SCRATCH_ALLOC_INIT( l, output_buf );
412 :
413 0 : fd_exec_test_instr_effects_t * effects =
414 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_instr_effects_t),
415 0 : sizeof (fd_exec_test_instr_effects_t) );
416 0 : if( FD_UNLIKELY( _l > output_end ) ) {
417 0 : fd_solfuzz_pb_instr_ctx_destroy( runner, ctx );
418 0 : return 0UL;
419 0 : }
420 0 : fd_memset( effects, 0, sizeof(fd_exec_test_instr_effects_t) );
421 :
422 : /* Capture error code */
423 :
424 0 : effects->result = -exec_result;
425 0 : effects->cu_avail = ctx->txn_out->details.compute_budget.compute_meter;
426 :
427 : /* Don't capture custom error codes if the program is a precompile */
428 0 : if( FD_LIKELY( effects->result ) ) {
429 0 : int program_id_idx = ctx->instr[ 0UL ].program_id;
430 0 : if( exec_result==FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR &&
431 0 : fd_executor_lookup_native_precompile_program( &ctx->txn_out->accounts.keys[ program_id_idx ] )==NULL ) {
432 0 : effects->custom_err = ctx->txn_out->err.custom_err;
433 0 : }
434 0 : }
435 :
436 : /* Allocate space for captured accounts */
437 0 : ulong modified_acct_cnt = input->accounts_count;
438 :
439 0 : fd_exec_test_acct_state_t * modified_accts =
440 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_exec_test_acct_state_t),
441 0 : sizeof (fd_exec_test_acct_state_t) * modified_acct_cnt );
442 0 : if( FD_UNLIKELY( _l > output_end ) ) {
443 0 : fd_solfuzz_pb_instr_ctx_destroy( runner, ctx );
444 0 : return 0;
445 0 : }
446 0 : effects->modified_accounts = modified_accts;
447 0 : effects->modified_accounts_count = 0UL;
448 :
449 : /* Capture any accounts which may have changed */
450 :
451 0 : for( ulong j=0UL; j < input->accounts_count; j++ ) {
452 0 : fd_exec_test_acct_state_t const * in_acct = &input->accounts[j];
453 :
454 0 : ulong lamports;
455 0 : uchar const * data;
456 0 : ulong data_len;
457 0 : int executable;
458 0 : uchar const * owner;
459 :
460 : /* Capture the account from the message in case it was modified,
461 : or just report the input account otherwise. */
462 0 : ulong txn_idx = instr_harness_idx_of( ctx->txn_out, in_acct->address );
463 0 : if( txn_idx!=ULONG_MAX ) {
464 0 : fd_acc_t const * acc = ctx->txn_out->accounts.account[txn_idx];
465 0 : lamports = acc->lamports;
466 0 : data = acc->data;
467 0 : data_len = acc->data_len;
468 0 : executable = acc->executable;
469 0 : owner = acc->owner;
470 0 : } else {
471 0 : lamports = in_acct->lamports;
472 0 : data = in_acct->data ? in_acct->data->bytes : NULL;
473 0 : data_len = in_acct->data ? in_acct->data->size : 0UL;
474 0 : executable = in_acct->executable;
475 0 : owner = in_acct->owner;
476 0 : }
477 :
478 0 : ulong modified_idx = effects->modified_accounts_count;
479 0 : if( FD_UNLIKELY( modified_idx >= modified_acct_cnt ) ) {
480 0 : FD_LOG_CRIT(( "invalid modified account index" ));
481 0 : }
482 :
483 0 : fd_exec_test_acct_state_t * out_acct = &effects->modified_accounts[ modified_idx ];
484 0 : memset( out_acct, 0, sizeof(fd_exec_test_acct_state_t) );
485 :
486 : /* Copy over account content */
487 0 : memcpy( out_acct->address, in_acct->address, sizeof(fd_pubkey_t) );
488 0 : out_acct->lamports = lamports;
489 0 : if( data_len>0UL ) {
490 0 : out_acct->data =
491 0 : FD_SCRATCH_ALLOC_APPEND( l, alignof(pb_bytes_array_t),
492 0 : PB_BYTES_ARRAY_T_ALLOCSIZE( data_len ) );
493 0 : if( FD_UNLIKELY( _l > output_end ) ) {
494 0 : fd_solfuzz_pb_instr_ctx_destroy( runner, ctx );
495 0 : return 0UL;
496 0 : }
497 0 : out_acct->data->size = (pb_size_t)data_len;
498 0 : fd_memcpy( out_acct->data->bytes, data, data_len );
499 0 : }
500 :
501 0 : out_acct->executable = executable;
502 0 : memcpy( out_acct->owner, owner, sizeof(fd_pubkey_t) );
503 :
504 0 : effects->modified_accounts_count++;
505 0 : }
506 :
507 0 : fd_solfuzz_direct_mapping_handle_cu_exhaustion(
508 0 : runner, effects->cu_avail, effects->result,
509 0 : effects->modified_accounts, (pb_size_t)effects->modified_accounts_count );
510 :
511 : /* Capture return data */
512 0 : fd_txn_return_data_t * return_data = &ctx->txn_out->details.return_data;
513 0 : if( return_data->len>0UL ) {
514 0 : effects->return_data = FD_SCRATCH_ALLOC_APPEND(l, alignof(pb_bytes_array_t),
515 0 : PB_BYTES_ARRAY_T_ALLOCSIZE( return_data->len ) );
516 0 : if( FD_UNLIKELY( _l > output_end ) ) {
517 0 : fd_solfuzz_pb_instr_ctx_destroy( runner, ctx );
518 0 : return 0UL;
519 0 : }
520 0 : effects->return_data->size = (pb_size_t)return_data->len;
521 0 : fd_memcpy( effects->return_data->bytes, return_data->data, return_data->len );
522 0 : }
523 :
524 0 : ulong actual_end = FD_SCRATCH_ALLOC_FINI( l, 1UL );
525 0 : fd_solfuzz_pb_instr_ctx_destroy( runner, ctx );
526 :
527 0 : *output = effects;
528 0 : return actual_end - (ulong)output_buf;
529 0 : }
|