Line data Source code
1 : #include "fd_bpf_loader_program.h"
2 : #include "fd_system_program.h"
3 :
4 : /* For additional context see https://solana.com/docs/programs/deploying#state-accounts */
5 :
6 : #include "../../progcache/fd_progcache_user.h"
7 : #include "../fd_runtime_helpers.h"
8 : #include "../fd_executor.h"
9 : #include "../tests/fd_dump_pb.h"
10 : #include "../sysvar/fd_sysvar.h"
11 : #include "../fd_pubkey_utils.h"
12 : #include "../fd_borrowed_account.h"
13 : #include "../fd_system_ids.h"
14 : #include "fd_bpf_loader_serialization.h"
15 : #include "fd_builtin_programs.h"
16 : #include "fd_native_cpi.h"
17 :
18 : /* https://github.com/anza-xyz/agave/blob/ced98f1ebe73f7e9691308afa757323003ff744f/sdk/program/src/program_error.rs#L290-L335 */
19 : static inline int
20 : program_error_to_instr_error( ulong err,
21 47 : uint * custom_err ) {
22 47 : switch( err ) {
23 2 : case CUSTOM_ZERO:
24 2 : *custom_err = 0;
25 2 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
26 0 : case INVALID_ARGUMENT:
27 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
28 2 : case INVALID_INSTRUCTION_DATA:
29 2 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
30 0 : case INVALID_ACCOUNT_DATA:
31 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
32 0 : case ACCOUNT_DATA_TOO_SMALL:
33 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
34 0 : case INSUFFICIENT_FUNDS:
35 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
36 0 : case INCORRECT_PROGRAM_ID:
37 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_PROGRAM_ID;
38 0 : case MISSING_REQUIRED_SIGNATURES:
39 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
40 0 : case ACCOUNT_ALREADY_INITIALIZED:
41 0 : return FD_EXECUTOR_INSTR_ERR_ACC_ALREADY_INITIALIZED;
42 0 : case UNINITIALIZED_ACCOUNT:
43 0 : return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT;
44 6 : case NOT_ENOUGH_ACCOUNT_KEYS:
45 6 : return FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS;
46 0 : case ACCOUNT_BORROW_FAILED:
47 0 : return FD_EXECUTOR_INSTR_ERR_ACC_BORROW_FAILED;
48 0 : case MAX_SEED_LENGTH_EXCEEDED:
49 0 : return FD_EXECUTOR_INSTR_ERR_MAX_SEED_LENGTH_EXCEEDED;
50 0 : case INVALID_SEEDS:
51 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_SEEDS;
52 0 : case BORSH_IO_ERROR:
53 0 : return FD_EXECUTOR_INSTR_ERR_BORSH_IO_ERROR;
54 0 : case ACCOUNT_NOT_RENT_EXEMPT:
55 0 : return FD_EXECUTOR_INSTR_ERR_ACC_NOT_RENT_EXEMPT;
56 0 : case UNSUPPORTED_SYSVAR:
57 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
58 0 : case ILLEGAL_OWNER:
59 0 : return FD_EXECUTOR_INSTR_ERR_ILLEGAL_OWNER;
60 0 : case MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED:
61 0 : return FD_EXECUTOR_INSTR_ERR_MAX_ACCS_DATA_ALLOCS_EXCEEDED;
62 0 : case INVALID_ACCOUNT_DATA_REALLOC:
63 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_REALLOC;
64 0 : case MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED:
65 0 : return FD_EXECUTOR_INSTR_ERR_MAX_INSN_TRACE_LENS_EXCEEDED;
66 0 : case BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS:
67 0 : return FD_EXECUTOR_INSTR_ERR_BUILTINS_MUST_CONSUME_CUS;
68 1 : case INVALID_ACCOUNT_OWNER:
69 1 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
70 0 : case ARITHMETIC_OVERFLOW:
71 0 : return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
72 0 : case IMMUTABLE:
73 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
74 0 : case INCORRECT_AUTHORITY:
75 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
76 36 : default:
77 36 : if( err>>BUILTIN_BIT_SHIFT == 0 ) {
78 36 : *custom_err = (uint)err;
79 36 : return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
80 36 : }
81 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ERR;
82 47 : }
83 47 : }
84 :
85 : /* https://github.com/anza-xyz/agave/blob/9b22f28104ec5fd606e4bb39442a7600b38bb671/programs/bpf_loader/src/lib.rs#L216-L229 */
86 : static ulong
87 4231 : calculate_heap_cost( ulong heap_size, ulong heap_cost ) {
88 8462 : #define KIBIBYTE_MUL_PAGES (1024UL * 32UL)
89 4231 : #define KIBIBYTE_MUL_PAGES_SUB_1 (KIBIBYTE_MUL_PAGES - 1UL)
90 :
91 4231 : heap_size = fd_ulong_sat_add( heap_size, KIBIBYTE_MUL_PAGES_SUB_1 );
92 :
93 4231 : heap_size = fd_ulong_sat_mul( fd_ulong_sat_sub( heap_size / KIBIBYTE_MUL_PAGES, 1UL ), heap_cost );
94 4231 : return heap_size;
95 :
96 4231 : #undef KIBIBYTE_MUL_PAGES
97 4231 : #undef KIBIBYTE_MUL_PAGES_SUB_1
98 4231 : }
99 :
100 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L105-L171
101 :
102 : Our arguments to deploy_program are different from the Agave version because
103 : we handle the caching of deployed programs differently. In Firedancer we
104 : lack the concept of ProgramCacheEntryType entirely.
105 : https://github.com/anza-xyz/agave/blob/114d94a25e9631f9bf6349c4b833d7900ef1fb1c/program-runtime/src/loaded_programs.rs#L158
106 :
107 : In Agave there is a separate caching structure that is used to store the
108 : deployed programs. In Firedancer the deployed, validated program is stored as
109 : metadata for the account.
110 :
111 : See https://github.com/firedancer-io/firedancer/blob/9c1df680b3f38bebb0597e089766ec58f3b41e85/src/flamenco/runtime/program/fd_bpf_loader_v3_program.c#L1640
112 : for how we handle the concept of 'LoadedProgramType::DelayVisibility' in Firedancer.
113 :
114 : As a concrete example, our version of deploy_program does not have the
115 : 'account_size' argument because we do not update the record here. */
116 :
117 : struct fd_bpf_deploy_scratch {
118 : uchar rodata [ FD_RUNTIME_ACC_SZ_MAX ] __attribute__((aligned(FD_SBPF_PROG_RODATA_ALIGN)));
119 : uchar sbpf_footprint[ FD_SBPF_PROGRAM_FOOTPRINT ] __attribute__((aligned(alignof(fd_sbpf_program_t))));
120 : uchar programdata [ FD_RUNTIME_ACC_SZ_MAX ] __attribute__((aligned(FD_ACCOUNT_REC_ALIGN)));
121 : };
122 : typedef struct fd_bpf_deploy_scratch fd_bpf_deploy_scratch_t;
123 :
124 : FD_STATIC_ASSERT( sizeof (fd_bpf_deploy_scratch_t)<=BPF_LOADER_SERIALIZATION_FOOTPRINT, bpf_deploy_scratch_sz );
125 : FD_STATIC_ASSERT( alignof(fd_bpf_deploy_scratch_t)<=FD_BPF_FRAMES_ALIGN, bpf_deploy_scratch_align );
126 :
127 : static int
128 : deploy_program( fd_exec_instr_ctx_t * instr_ctx,
129 : uchar const * programdata,
130 : ulong programdata_size,
131 : int disable_sbpf_v0_v1_v2_deployment,
132 248 : fd_bpf_deploy_scratch_t * scratch ) {
133 248 : int deploy_mode = 1;
134 248 : int direct_mapping = FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, account_data_direct_mapping );
135 248 : int syscall_parameter_address_restrictions = FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, syscall_parameter_address_restrictions );
136 248 : int virtual_address_space_adjustments = FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, virtual_address_space_adjustments );
137 :
138 248 : uchar syscalls_mem[ FD_SBPF_SYSCALLS_FOOTPRINT ] __attribute__((aligned(FD_SBPF_SYSCALLS_ALIGN)));
139 248 : fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_join( fd_sbpf_syscalls_new( syscalls_mem ) );
140 248 : if( FD_UNLIKELY( !syscalls ) ) {
141 : //TODO: full log including err
142 0 : fd_log_collector_msg_literal( instr_ctx, "Failed to register syscalls" );
143 0 : return FD_EXECUTOR_INSTR_ERR_PROGRAM_ENVIRONMENT_SETUP_FAILURE;
144 0 : }
145 :
146 : /* Agave uses the feature set from the next slot for deployment
147 : verification (DELAY_VISIBILITY_SLOT_OFFSET = 1). This matters at
148 : epoch boundaries where features activate: a deployment at the last
149 : slot of an epoch should see features that activate at the boundary.
150 : https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank.rs#L3280-L3295 */
151 248 : ulong deploy_slot = instr_ctx->bank->f.slot+1UL;
152 :
153 248 : fd_vm_syscall_register_slot( syscalls,
154 248 : deploy_slot,
155 248 : &instr_ctx->bank->f.features,
156 248 : 1 );
157 :
158 : /* Load executable */
159 248 : fd_sbpf_elf_info_t elf_info[ 1UL ];
160 248 : fd_prog_versions_t versions = fd_prog_versions( &instr_ctx->bank->f.features, deploy_slot );
161 :
162 : /* SIMD-0500: when active, restrict program deployment to SBPF v3+.
163 : Older SBPF versions remain executable.
164 : TODO: fix link when 4.1 is released
165 : https://github.com/anza-xyz/agave/blob/v4.1.0-alpha.0/program-runtime/src/deploy.rs#L30-L32 */
166 248 : if( disable_sbpf_v0_v1_v2_deployment ) {
167 0 : versions.min_sbpf_version = FD_SBPF_V3;
168 0 : }
169 :
170 248 : fd_sbpf_loader_config_t config = { 0 };
171 248 : config.elf_deploy_checks = deploy_mode;
172 248 : config.sbpf_min_version = versions.min_sbpf_version;
173 248 : config.sbpf_max_version = versions.max_sbpf_version;
174 :
175 248 : if( FD_UNLIKELY( fd_sbpf_elf_peek( elf_info, programdata, programdata_size, &config )<0 ) ) {
176 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
177 0 : }
178 :
179 248 : fd_sbpf_program_t * prog = fd_sbpf_program_new( scratch->sbpf_footprint, elf_info, scratch->rodata );
180 248 : if( FD_UNLIKELY( !prog ) ) {
181 0 : FD_LOG_ERR(( "fd_sbpf_program_new() failed" ));
182 0 : }
183 :
184 : /* Load program */
185 248 : int err = fd_sbpf_program_load( prog, programdata, programdata_size, syscalls, &config, scratch->programdata, programdata_size );
186 248 : if( FD_UNLIKELY( err ) ) {
187 71 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
188 71 : }
189 :
190 : /* Validate the program */
191 177 : fd_vm_t _vm[ 1UL ];
192 177 : fd_vm_t * vm = fd_vm_join( fd_vm_new( _vm ) );
193 :
194 177 : vm = fd_vm_init(
195 177 : /* vm */ vm,
196 177 : /* instr_ctx */ instr_ctx,
197 177 : /* heap_max */ instr_ctx->txn_out->details.compute_budget.heap_size,
198 177 : /* entry_cu */ instr_ctx->txn_out->details.compute_budget.compute_meter,
199 177 : /* rodata */ prog->rodata,
200 177 : /* rodata_sz */ prog->rodata_sz,
201 177 : /* text */ prog->text,
202 177 : /* text_cnt */ prog->info.text_cnt,
203 177 : /* text_off */ prog->info.text_off, /* FIXME: What if text_off is not multiple of 8 */
204 177 : /* text_sz */ prog->info.text_sz,
205 177 : /* entry_pc */ prog->entry_pc,
206 177 : /* calldests */ prog->calldests,
207 177 : /* sbpf_version */ elf_info->sbpf_version,
208 177 : /* syscalls */ syscalls,
209 : /* trace */ NULL,
210 : /* sha */ NULL,
211 : /* mem_regions */ NULL,
212 177 : /* mem_regions_cnt */ 0,
213 : /* mem_region_accs */ NULL,
214 177 : /* is_deprecated */ 0,
215 177 : /* direct mapping */ direct_mapping,
216 177 : /* syscall_parameter_address_restrictions */ syscall_parameter_address_restrictions,
217 177 : /* virtual_address_space_adjustments */ virtual_address_space_adjustments,
218 177 : /* dump_syscall_to_pb */ 0,
219 177 : /* r2_initial_value */ 0UL );
220 177 : if ( FD_UNLIKELY( vm == NULL ) ) {
221 0 : FD_LOG_WARNING(( "NULL vm" ));
222 0 : return FD_EXECUTOR_INSTR_ERR_PROGRAM_ENVIRONMENT_SETUP_FAILURE;
223 0 : }
224 :
225 177 : int validate_result = fd_vm_validate( vm );
226 177 : if( FD_UNLIKELY( validate_result!=FD_VM_SUCCESS ) ) {
227 1 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
228 1 : }
229 :
230 176 : return FD_EXECUTOR_INSTR_SUCCESS;
231 177 : }
232 :
233 : int
234 : fd_deploy_program( fd_exec_instr_ctx_t * instr_ctx,
235 : uchar const * programdata,
236 : ulong programdata_size,
237 248 : int disable_sbpf_v0_v1_v2_deployment ) {
238 : /* The loader runs as a builtin, so its stack level holds no frame and
239 : can borrow one under the same rules as fd_bpf_execute. */
240 248 : fd_runtime_t * runtime = instr_ctx->runtime;
241 248 : uchar * frame = fd_bpf_frames_acquire( runtime->bpf_frames, fd_progcache_spill_held( runtime->progcache ) );
242 248 : int err = deploy_program( instr_ctx, programdata, programdata_size, disable_sbpf_v0_v1_v2_deployment, (fd_bpf_deploy_scratch_t *)frame );
243 248 : fd_bpf_frames_release( runtime->bpf_frames, frame );
244 248 : return err;
245 248 : }
246 :
247 : /* SIMD-0500 finalize gate: when the feature is active and the ELF
248 : embedded in `programdata` parses as SBPFv0/v1/v2, reject;
249 : otherwise accept (including the short-programdata case, which
250 : mirrors the let-chain short-circuit in agave). See the call site
251 : in `process_loader_upgradeable_instruction` for the SetAuthority
252 : handler.
253 : https://github.com/anza-xyz/agave/blob/v4.1.0-alpha.0/programs/bpf_loader/src/lib.rs#L580-L591 */
254 : int
255 : fd_bpf_loader_finalize_v3_check( int feature_active,
256 : uchar const * programdata,
257 12 : ulong programdata_len ) {
258 12 : ulong const off = PROGRAMDATA_METADATA_SIZE + 48UL; /* ELF64 e_flags */
259 12 : if( !feature_active ) return FD_EXECUTOR_INSTR_SUCCESS;
260 0 : if( programdata_len < off + sizeof(uint) ) return FD_EXECUTOR_INSTR_SUCCESS;
261 0 : if( FD_LOAD( uint, programdata+off )<FD_SBPF_V3 ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
262 0 : return FD_EXECUTOR_INSTR_SUCCESS;
263 0 : }
264 :
265 : /* SIMD-0433 ProgramData sizing for the Loader V3 Upgrade instruction.
266 : https://github.com/anza-xyz/agave/blob/v4.4.0-alpha.6/programs/bpf_loader/src/lib.rs#L443-L468 */
267 : int
268 : fd_bpf_loader_v3_upgrade_sizing( int set_programdata_to_elf_len,
269 : ulong programdata_current_len,
270 : ulong buffer_data_len,
271 : fd_rent_t const * rent,
272 : ulong * out_programdata_len,
273 249 : ulong * out_balance_required ) {
274 249 : ulong programdata_len;
275 249 : if( set_programdata_to_elf_len ) {
276 0 : programdata_len = fd_ulong_sat_add( PROGRAMDATA_METADATA_SIZE, buffer_data_len );
277 0 : *out_programdata_len = programdata_len;
278 0 : if( FD_UNLIKELY( programdata_len>MAX_PERMITTED_DATA_LENGTH ) ) {
279 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
280 0 : }
281 249 : } else {
282 249 : programdata_len = programdata_current_len;
283 249 : *out_programdata_len = programdata_len;
284 249 : if( FD_UNLIKELY( programdata_len<fd_ulong_sat_add( PROGRAMDATA_METADATA_SIZE, buffer_data_len ) ) ) {
285 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
286 0 : }
287 249 : }
288 249 : *out_balance_required = fd_ulong_max( 1UL, fd_rent_exempt_minimum_balance( rent, programdata_len ) );
289 249 : return FD_EXECUTOR_INSTR_SUCCESS;
290 249 : }
291 :
292 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L195-L218 */
293 : static int
294 : write_program_data( fd_exec_instr_ctx_t * instr_ctx,
295 : ushort instr_acc_idx,
296 : ulong program_data_offset,
297 : uchar const * bytes,
298 260 : ulong bytes_len ) {
299 260 : int err;
300 :
301 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L202 */
302 260 : fd_guarded_borrowed_account_t program = {0};
303 260 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, instr_acc_idx, &program );
304 :
305 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L203 */
306 260 : uchar * data = NULL;
307 260 : ulong dlen = 0UL;
308 260 : err = fd_borrowed_account_get_data_mut( &program, &data, &dlen );
309 260 : if( FD_UNLIKELY( err ) ) {
310 1 : return err;
311 1 : }
312 :
313 259 : ulong write_offset = fd_ulong_sat_add( program_data_offset, bytes_len );
314 259 : if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &program )<write_offset ) ) {
315 : /* Max msg_sz: 24 - 6 + 2*20 = 58 < 127 => we can use printf */
316 0 : fd_log_collector_printf_dangerous_max_127( instr_ctx,
317 0 : "Write overflow: %lu < %lu", fd_borrowed_account_get_data_len( &program ), write_offset );
318 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
319 0 : }
320 :
321 259 : if( FD_UNLIKELY( program_data_offset>dlen ) ) {
322 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
323 0 : }
324 :
325 259 : if( FD_LIKELY( bytes_len ) ) {
326 259 : fd_memcpy( data+program_data_offset, bytes, bytes_len );
327 259 : }
328 :
329 259 : return FD_EXECUTOR_INSTR_SUCCESS;
330 259 : }
331 :
332 : int
333 : fd_bpf_loader_program_get_state( fd_acc_t const * acc,
334 23812 : fd_bpf_state_t * state ) {
335 23812 : if( FD_UNLIKELY( fd_bpf_state_decode( state, acc->data, acc->data_len ) ) ) {
336 632 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
337 632 : }
338 23180 : return FD_EXECUTOR_INSTR_SUCCESS;
339 23812 : }
340 :
341 : int
342 : fd_bpf_loader_program_get_state2( uchar const * data,
343 : ulong data_len,
344 28272 : fd_bpf_state_t * state ) {
345 28272 : if( FD_UNLIKELY( fd_bpf_state_decode( state, data, data_len ) ) ) {
346 4034 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
347 4034 : }
348 24238 : return FD_EXECUTOR_INSTR_SUCCESS;
349 28272 : }
350 :
351 : /* Mirrors solana_sdk::transaction_context::BorrowedAccount::set_state()
352 : https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L973 */
353 : int
354 : fd_bpf_loader_v3_program_set_state( fd_borrowed_account_t * borrowed_acct,
355 1350 : fd_bpf_state_t * state ) {
356 1350 : ulong state_size = fd_bpf_state_size( state );
357 :
358 1350 : uchar * data = NULL;
359 1350 : ulong dlen = 0UL;
360 :
361 1350 : int err = fd_borrowed_account_get_data_mut( borrowed_acct, &data, &dlen );
362 1350 : if( FD_UNLIKELY( err ) ) {
363 0 : return err;
364 0 : }
365 :
366 1350 : if( FD_UNLIKELY( state_size>dlen ) ) {
367 297 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
368 297 : }
369 :
370 1053 : ulong out_sz = 0UL;
371 1053 : if( FD_UNLIKELY( fd_bpf_state_encode( state, data, state_size, &out_sz ) ) ) {
372 0 : return FD_EXECUTOR_INSTR_ERR_GENERIC_ERR;
373 0 : }
374 :
375 1053 : return FD_EXECUTOR_INSTR_SUCCESS;
376 1053 : }
377 :
378 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L1299-L1331 */
379 : static int
380 : common_close_account( fd_pubkey_t * authority_address,
381 : fd_exec_instr_ctx_t * instr_ctx,
382 161 : fd_bpf_state_t * state ) {
383 161 : int err;
384 :
385 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L1307 */
386 161 : if( FD_UNLIKELY( !authority_address ) ) {
387 0 : fd_log_collector_msg_literal( instr_ctx, "Account is immutable" );
388 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
389 0 : }
390 :
391 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L1312-L1313 */
392 161 : fd_pubkey_t const * acc_key = NULL;
393 161 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 2UL, &acc_key );
394 161 : if( FD_UNLIKELY( err ) ) {
395 0 : return err;
396 0 : }
397 :
398 161 : if( FD_UNLIKELY( memcmp( authority_address, acc_key, sizeof(fd_pubkey_t) ) ) ) {
399 15 : fd_log_collector_msg_literal( instr_ctx, "Incorrect authority provided" );
400 15 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
401 15 : }
402 :
403 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L1319-L1322 */
404 146 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 2UL, &err ) ) ) {
405 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
406 5 : if( FD_UNLIKELY( !!err ) ) return err;
407 5 : fd_log_collector_msg_literal( instr_ctx, "Authority did not sign" );
408 5 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
409 5 : }
410 :
411 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1324 */
412 141 : fd_guarded_borrowed_account_t close_account = {0};
413 141 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &close_account );
414 :
415 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1326 */
416 141 : fd_guarded_borrowed_account_t recipient_account = {0};
417 141 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 1UL, &recipient_account );
418 :
419 141 : err = fd_borrowed_account_checked_add_lamports( &recipient_account,
420 141 : fd_borrowed_account_get_lamports( &close_account ) );
421 141 : if( FD_UNLIKELY( err ) ) {
422 0 : return err;
423 0 : }
424 :
425 141 : err = fd_borrowed_account_set_lamports( &close_account, 0UL );
426 141 : if( FD_UNLIKELY( err ) ) {
427 0 : return err;
428 0 : }
429 :
430 : /* https://github.com/anza-xyz/agave/blob/v4.4.0-beta.0/programs/bpf_loader/src/lib.rs#L1043 */
431 141 : err = fd_borrowed_account_set_data_length( &close_account, SIZE_OF_UNINITIALIZED );
432 141 : if( FD_UNLIKELY( err ) ) {
433 0 : return err;
434 0 : }
435 :
436 141 : state->discriminant = FD_BPF_STATE_UNINITIALIZED;
437 141 : err = fd_bpf_loader_v3_program_set_state( &close_account, state );
438 141 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
439 0 : return err;
440 0 : }
441 :
442 141 : return FD_EXECUTOR_INSTR_SUCCESS;
443 141 : }
444 :
445 :
446 : /* Every loader-owned BPF program goes through this function, which goes into the VM.
447 :
448 : https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L1332-L1501 */
449 : static int
450 : bpf_execute( fd_exec_instr_ctx_t * instr_ctx,
451 : fd_progcache_rec_t const * cache_entry,
452 4231 : uchar is_deprecated ) {
453 4231 : long const regime0 = fd_tickcount();
454 :
455 4231 : int err = FD_EXECUTOR_INSTR_SUCCESS;
456 :
457 4231 : uchar syscalls_mem[ FD_SBPF_SYSCALLS_FOOTPRINT ] __attribute__((aligned(FD_SBPF_SYSCALLS_ALIGN)));
458 4231 : fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_join( fd_sbpf_syscalls_new( syscalls_mem ) );
459 4231 : if( FD_UNLIKELY( !syscalls ) ) {
460 0 : FD_LOG_CRIT(( "Unable to allocate syscalls" ));
461 0 : }
462 :
463 : /* TODO do we really need to re-do this on every instruction? */
464 4231 : fd_vm_syscall_register_slot( syscalls,
465 4231 : instr_ctx->bank->f.slot,
466 4231 : &instr_ctx->bank->f.features,
467 4231 : 0 );
468 :
469 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L1362-L1368 */
470 4231 : ulong input_sz = 0UL;
471 4231 : ulong pre_lens[256];
472 4231 : fd_vm_input_region_t input_mem_regions[1000]; /* We can have a max of (3 * num accounts + 1) regions */
473 4231 : fd_vm_acc_region_meta_t acc_region_metas[256]; /* instr acc idx to idx */
474 4231 : uint input_mem_regions_cnt = 0U;
475 4231 : int direct_mapping = FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, account_data_direct_mapping );
476 4231 : int syscall_parameter_address_restrictions = FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, syscall_parameter_address_restrictions );
477 4231 : int virtual_address_space_adjustments = FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, virtual_address_space_adjustments );
478 4231 : int provide_instruction_data_offset_in_vm_r2 = FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, provide_instruction_data_offset_in_vm_r2 );
479 4231 : int direct_account_pointers_in_program_input = FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, direct_account_pointers_in_program_input );
480 :
481 4231 : ulong instruction_data_offset = 0UL;
482 : /* 16-byte aligned buffer:
483 : https://github.com/anza-xyz/agave/blob/v3.0.0/program-runtime/src/serialization.rs#L60 */
484 4231 : uchar * input = instr_ctx->runtime->bpf_loader_serialization.frame[ instr_ctx->runtime->instr.stack_sz-1UL ];
485 4231 : err = fd_bpf_loader_input_serialize_parameters( instr_ctx, pre_lens,
486 4231 : input_mem_regions, &input_mem_regions_cnt,
487 4231 : acc_region_metas, virtual_address_space_adjustments, direct_mapping,
488 4231 : direct_account_pointers_in_program_input, is_deprecated,
489 4231 : &instruction_data_offset, &input_sz );
490 4231 : if( FD_UNLIKELY( err ) ) {
491 0 : return err;
492 0 : }
493 :
494 4231 : fd_sha256_t _sha[1];
495 4231 : fd_sha256_t * sha = fd_sha256_join( fd_sha256_new( _sha ) );
496 :
497 4231 : fd_vm_t _vm[1];
498 4231 : fd_vm_t * vm = fd_vm_join( fd_vm_new( _vm ) );
499 :
500 4231 : ulong pre_insn_cus = instr_ctx->txn_out->details.compute_budget.compute_meter;
501 4231 : ulong heap_size = instr_ctx->txn_out->details.compute_budget.heap_size;
502 :
503 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L275-L278 */
504 4231 : ulong heap_cost = calculate_heap_cost( heap_size, FD_VM_HEAP_COST );
505 4231 : int heap_cost_result = fd_executor_consume_cus( instr_ctx->txn_out, heap_cost );
506 4231 : if( FD_UNLIKELY( heap_cost_result ) ) {
507 0 : return FD_EXECUTOR_INSTR_ERR_PROGRAM_ENVIRONMENT_SETUP_FAILURE;
508 0 : }
509 :
510 : /* For dumping syscalls for seed corpora */
511 4231 : int dump_syscall_to_pb = instr_ctx->runtime->log.dump_proto_ctx &&
512 4231 : instr_ctx->bank->f.slot>=instr_ctx->runtime->log.dump_proto_ctx->dump_proto_start_slot &&
513 4231 : instr_ctx->runtime->log.dump_proto_ctx->dump_syscall_to_pb;
514 :
515 : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/bpf_loader/src/lib.rs#L1525-L1528 */
516 4231 : ulong r2_initial_value = provide_instruction_data_offset_in_vm_r2 ? instruction_data_offset : 0UL;
517 :
518 : /* TODO: (topointon): correctly set check_size in vm setup */
519 4231 : fd_wksp_t * progcache_wksp = instr_ctx->runtime->progcache->join->data_base;
520 4231 : void const * rodata = fd_progcache_rec_rodata( cache_entry, progcache_wksp );
521 4231 : vm = fd_vm_init(
522 4231 : /* vm */ vm,
523 4231 : /* instr_ctx */ instr_ctx,
524 4231 : /* heap_max */ heap_size,
525 4231 : /* entry_cu */ instr_ctx->txn_out->details.compute_budget.compute_meter,
526 4231 : /* rodata */ rodata,
527 4231 : /* rodata_sz */ cache_entry->rodata_sz,
528 4231 : /* text (note: text_off is byte offset) */ (ulong *)( (ulong)rodata + cache_entry->text_off ),
529 4231 : /* text_cnt */ cache_entry->text_cnt,
530 4231 : /* text_off */ cache_entry->text_off,
531 4231 : /* text_sz */ cache_entry->text_sz,
532 4231 : /* entry_pc */ cache_entry->entry_pc,
533 4231 : /* calldests */ fd_progcache_rec_calldests( cache_entry, progcache_wksp ),
534 4231 : /* sbpf_version */ cache_entry->sbpf_version,
535 4231 : /* syscalls */ syscalls,
536 : /* trace */ NULL,
537 4231 : /* sha */ sha,
538 4231 : /* input_mem_regions */ input_mem_regions,
539 4231 : /* input_mem_regions_cnt */ input_mem_regions_cnt,
540 4231 : /* acc_region_metas */ acc_region_metas,
541 4231 : /* is_deprecated */ is_deprecated,
542 4231 : /* direct_mapping */ direct_mapping,
543 4231 : /* syscall_parameter_address_restrictions */ syscall_parameter_address_restrictions,
544 4231 : /* virtual_address_space_adjustments */ virtual_address_space_adjustments,
545 4231 : /* dump_syscall_to_pb */ dump_syscall_to_pb,
546 4231 : /* r2_initial_value */ r2_initial_value );
547 4231 : if( FD_UNLIKELY( !vm ) ) {
548 : /* We throw an error here because it could be the case that the given heap_size > HEAP_MAX.
549 : In this case, Agave fails the transaction but does not error out.
550 :
551 : https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L1396 */
552 0 : return FD_EXECUTOR_INSTR_ERR_PROGRAM_ENVIRONMENT_SETUP_FAILURE;
553 0 : }
554 :
555 4231 : if( FD_UNLIKELY( instr_ctx->runtime->log.enable_vm_tracing && instr_ctx->runtime->log.tracing_mem ) ) {
556 0 : vm->trace = fd_vm_trace_join( fd_vm_trace_new( instr_ctx->runtime->log.tracing_mem + ((instr_ctx->runtime->instr.stack_sz-1UL) * FD_RUNTIME_VM_TRACE_STATIC_FOOTPRINT), FD_RUNTIME_VM_TRACE_EVENT_MAX ));
557 0 : if( FD_UNLIKELY( !vm->trace ) ) FD_LOG_ERR(( "unable to create trace; make sure you've compiled with sufficient spad size " ));
558 0 : }
559 :
560 4231 : long const regime1 = fd_tickcount();
561 :
562 4231 : int exec_err = fd_vm_exec( vm );
563 4231 : instr_ctx->txn_out->details.compute_budget.compute_meter = vm->cu;
564 :
565 4231 : long const regime2 = fd_tickcount();
566 :
567 4231 : if( instr_ctx->instr->stack_height==1 ) {
568 3600 : instr_ctx->runtime->metrics.vm_setup_cum_ticks += (ulong)( regime1-regime0 );
569 3600 : instr_ctx->runtime->metrics.vm_exec_cum_ticks += (ulong)( regime2-regime1 );
570 3600 : } else {
571 631 : instr_ctx->runtime->metrics.cpi_setup_cum_ticks += (ulong)( regime1-regime0 );
572 631 : }
573 :
574 4231 : if( FD_UNLIKELY( vm->trace ) ) {
575 0 : err = fd_vm_trace_printf( vm->trace );
576 0 : if( FD_UNLIKELY( err ) ) {
577 0 : FD_LOG_WARNING(( "fd_vm_trace_printf failed (%i-%s)", err, fd_vm_strerror( err ) ));
578 0 : }
579 0 : }
580 :
581 : /* Log consumed compute units and return data.
582 : https://github.com/anza-xyz/agave/blob/v4.3.0/program-runtime/src/vm.rs#L347-L353 */
583 4231 : fd_log_collector_program_consumed( instr_ctx, pre_insn_cus-heap_cost-vm->cu, pre_insn_cus );
584 4231 : if( FD_UNLIKELY( instr_ctx->txn_out->details.return_data.len ) ) {
585 150 : fd_log_collector_program_return( instr_ctx );
586 150 : }
587 :
588 : /* We have a big error-matching arm here
589 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1674-L1744 */
590 :
591 : /* Handle non-zero return status with successful VM execution. This is
592 : the Ok(status) case, hence exec_err must be 0 for this case to be hit.
593 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1675-L1678 */
594 4231 : if( FD_LIKELY( !exec_err ) ) {
595 1635 : ulong status = vm->reg[0];
596 1635 : if( FD_UNLIKELY( status ) ) {
597 47 : err = program_error_to_instr_error( status, &instr_ctx->txn_out->err.custom_err );
598 47 : FD_VM_PREPARE_ERR_OVERWRITE( vm );
599 47 : FD_VM_ERR_FOR_LOG_INSTR( vm, err );
600 47 : return err;
601 47 : }
602 2596 : } else {
603 : /* https://github.com/anza-xyz/agave/blob/v2.1.13/programs/bpf_loader/src/lib.rs#L1434-L1439 */
604 : /* (SIMD-182) Consume ALL requested CUs on non-Syscall errors */
605 2596 : if( FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, deplete_cu_meter_on_vm_failure ) &&
606 2596 : exec_err!=FD_VM_ERR_EBPF_SYSCALL_ERROR ) {
607 0 : instr_ctx->txn_out->details.compute_budget.compute_meter = 0UL;
608 0 : }
609 :
610 : /* With virtual_address_space_adjustments, remap load and store
611 : access violations to more specific account errors.
612 : https://github.com/anza-xyz/agave/blob/v3.0.4/programs/bpf_loader/src/lib.rs#L1556-L1618 */
613 2596 : if( FD_UNLIKELY( virtual_address_space_adjustments &&
614 2596 : ( exec_err==FD_VM_ERR_EBPF_ACCESS_VIOLATION || instr_ctx->txn_out->err.exec_err==FD_VM_ERR_EBPF_ACCESS_VIOLATION ) &&
615 2596 : vm->segv_vaddr!=ULONG_MAX ) ) {
616 :
617 : /* If the vaddr of the access violation falls within the bounds of a
618 : serialized account vaddr range, then try to retrieve a more specific
619 : vm error based on the account's access permissions. */
620 796 : for( ushort i=0UL; i<instr_ctx->instr->acct_cnt; i++ ) {
621 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1455 */
622 :
623 : /* Find the input memory region that corresponds to the access
624 : https://github.com/anza-xyz/agave/blob/v3.0.4/programs/bpf_loader/src/lib.rs#L1566-L1617 */
625 784 : ulong idx = acc_region_metas[i].region_idx;
626 784 : fd_vm_input_region_t const * input_mem_region = &input_mem_regions[idx];
627 784 : fd_vm_acc_region_meta_t const * acc_region_meta = &acc_region_metas[i];
628 :
629 : /* https://github.com/anza-xyz/agave/blob/v3.0.4/programs/bpf_loader/src/lib.rs#L1484-L1492 */
630 784 : ulong region_data_vaddr_start = FD_VM_MEM_MAP_INPUT_REGION_START + input_mem_region->vaddr_offset + input_mem_region->region_sz;
631 784 : ulong region_data_vaddr_end = fd_ulong_sat_add( region_data_vaddr_start, acc_region_meta->original_data_len );
632 784 : if( FD_LIKELY( !is_deprecated ) ) {
633 432 : region_data_vaddr_end = fd_ulong_sat_add( region_data_vaddr_end, MAX_PERMITTED_DATA_INCREASE );
634 432 : }
635 :
636 784 : if( vm->segv_vaddr >= region_data_vaddr_start && vm->segv_vaddr <= region_data_vaddr_end ) {
637 :
638 : /* https://github.com/anza-xyz/agave/blob/v3.0.4/programs/bpf_loader/src/lib.rs#L1575-L1616 */
639 5 : fd_guarded_borrowed_account_t instr_acc = {0};
640 5 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, i, &instr_acc );
641 :
642 : /* https://github.com/anza-xyz/agave/blob/v3.0.4/programs/bpf_loader/src/lib.rs#L1581-L1616 */
643 5 : if( fd_ulong_sat_add( vm->segv_vaddr, vm->segv_access_len ) <= region_data_vaddr_end ) {
644 : /* https://github.com/anza-xyz/agave/blob/v3.0.4/programs/bpf_loader/src/lib.rs#L1592-L1601 */
645 4 : if( vm->segv_access_type==FD_VM_ACCESS_TYPE_ST ) {
646 1 : int borrow_err = FD_EXECUTOR_INSTR_SUCCESS;
647 1 : if( !fd_borrowed_account_can_data_be_changed( &instr_acc, &borrow_err ) || borrow_err != FD_EXECUTOR_INSTR_SUCCESS ) {
648 1 : err = borrow_err;
649 1 : } else {
650 0 : err = FD_EXECUTOR_INSTR_ERR_INVALID_REALLOC;
651 0 : }
652 3 : } else if( vm->segv_access_type==FD_VM_ACCESS_TYPE_LD ) {
653 3 : int borrow_err = FD_EXECUTOR_INSTR_SUCCESS;
654 3 : if( !fd_borrowed_account_can_data_be_changed( &instr_acc, &borrow_err ) || borrow_err != FD_EXECUTOR_INSTR_SUCCESS ) {
655 3 : err = FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
656 3 : } else {
657 0 : err = FD_EXECUTOR_INSTR_ERR_INVALID_REALLOC;
658 0 : }
659 3 : } else {
660 0 : FD_LOG_CRIT(( "invariant violation: unsupported access type: %i", vm->segv_access_type ));
661 0 : }
662 :
663 4 : FD_VM_PREPARE_ERR_OVERWRITE( vm );
664 4 : FD_VM_ERR_FOR_LOG_INSTR( vm, err );
665 4 : return err;
666 4 : }
667 5 : }
668 784 : }
669 16 : }
670 :
671 : /* The error kind should have been set in the VM. Match it and set
672 : the error code accordingly. There are no direct permalinks here -
673 : this is all a result of Agave's complex nested error-code handling
674 : and our design decisions for making our error codes match. */
675 :
676 : /* Instr error case. Set the error kind and return the instruction error */
677 2592 : if( instr_ctx->txn_out->err.exec_err_kind==FD_EXECUTOR_ERR_KIND_INSTR ) {
678 575 : err = instr_ctx->txn_out->err.exec_err;
679 575 : FD_VM_PREPARE_ERR_OVERWRITE( vm );
680 575 : FD_VM_ERR_FOR_LOG_INSTR( vm, err );
681 575 : return err;
682 575 : }
683 :
684 : /* Syscall error case. The VM would have also set the syscall error
685 : code in the txn_ctx exec_err. */
686 2017 : if( instr_ctx->txn_out->err.exec_err_kind==FD_EXECUTOR_ERR_KIND_SYSCALL ) {
687 317 : err = instr_ctx->txn_out->err.exec_err;
688 317 : FD_VM_PREPARE_ERR_OVERWRITE( vm );
689 317 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, err );
690 317 : return FD_EXECUTOR_INSTR_ERR_PROGRAM_FAILED_TO_COMPLETE;
691 317 : }
692 :
693 : /* An access violation that takes place inside a syscall will
694 : cause `exec_res` to be set to EbpfError::SyscallError,
695 : 'but the `txn_ctx->err.exec_err_kind` will be set to EBPF and
696 : `txn_ctx->err.exec_err` will be set to the EBPF error. In this
697 : specific case, there is nothing to do since the error and error
698 : kind area already set correctly. Otherwise, we need to log the
699 : EBPF error. */
700 1700 : if( exec_err!=FD_VM_ERR_EBPF_SYSCALL_ERROR ) {
701 1646 : FD_VM_PREPARE_ERR_OVERWRITE( vm );
702 1646 : FD_VM_ERR_FOR_LOG_EBPF( vm, exec_err );
703 1646 : }
704 :
705 1700 : return FD_EXECUTOR_INSTR_ERR_PROGRAM_FAILED_TO_COMPLETE;
706 2017 : }
707 :
708 1588 : err = fd_bpf_loader_input_deserialize_parameters(
709 1588 : instr_ctx, pre_lens, input, input_sz, virtual_address_space_adjustments, direct_mapping, is_deprecated );
710 :
711 1588 : long const regime3 = fd_tickcount();
712 1588 : if( instr_ctx->instr->stack_height==1 ) {
713 1217 : instr_ctx->runtime->metrics.vm_commit_cum_ticks += (ulong)( regime3-regime2 );
714 1217 : } else {
715 371 : instr_ctx->runtime->metrics.cpi_commit_cum_ticks += (ulong)( regime3-regime2 );
716 371 : }
717 :
718 1588 : return err;
719 4231 : }
720 :
721 : int
722 : fd_bpf_execute( fd_exec_instr_ctx_t * instr_ctx,
723 : fd_progcache_rec_t const * cache_entry,
724 4231 : uchar is_deprecated ) {
725 : /* Tiles holding frames can wait on the program cache spill lock, so
726 : its holder must never wait for a frame. */
727 4231 : fd_runtime_t * runtime = instr_ctx->runtime;
728 4231 : uchar * frame = fd_bpf_frames_acquire( runtime->bpf_frames, fd_progcache_spill_held( runtime->progcache ) );
729 4231 : runtime->bpf_loader_serialization.frame[ runtime->instr.stack_sz-1UL ] = frame;
730 4231 : int err = bpf_execute( instr_ctx, cache_entry, is_deprecated );
731 4231 : fd_bpf_frames_release( runtime->bpf_frames, frame );
732 4231 : return err;
733 4231 : }
734 :
735 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1358-L1539 */
736 : static int
737 : common_extend_program( fd_exec_instr_ctx_t * instr_ctx,
738 : uint additional_bytes,
739 381 : uchar check_authority ) {
740 381 : int err;
741 :
742 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1366 */
743 381 : fd_pubkey_t const * program_id = NULL;
744 381 : err = fd_exec_instr_ctx_get_last_program_key( instr_ctx, &program_id );
745 381 : if( FD_UNLIKELY( err ) ) {
746 0 : return err;
747 0 : }
748 :
749 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1368-L1370 */
750 381 : #define PROGRAM_DATA_ACCOUNT_INDEX (0)
751 381 : #define PROGRAM_ACCOUNT_INDEX (1)
752 381 : #define AUTHORITY_ACCOUNT_INDEX (2)
753 :
754 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1371-L1372 */
755 381 : uchar optional_payer_account_index = check_authority ? 4 : 3;
756 :
757 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1374-L1377 */
758 381 : if( FD_UNLIKELY( additional_bytes==0U ) ) {
759 0 : fd_log_collector_msg_literal( instr_ctx, "Additional bytes must be greater than 0" );
760 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
761 0 : }
762 :
763 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1379-L1381 */
764 381 : fd_guarded_borrowed_account_t programdata_account = {0};
765 381 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, PROGRAM_DATA_ACCOUNT_INDEX, &programdata_account );
766 381 : fd_pubkey_t const * programdata_key = (fd_pubkey_t*)programdata_account.acc->pubkey;
767 :
768 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1383-L1386 */
769 381 : if( FD_UNLIKELY( memcmp( program_id, fd_borrowed_account_get_owner( &programdata_account ), sizeof(fd_pubkey_t) ) ) ) {
770 7 : fd_log_collector_msg_literal( instr_ctx, "ProgramData owner is invalid" );
771 7 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
772 7 : }
773 :
774 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1387-L1390 */
775 374 : if( FD_UNLIKELY( !fd_borrowed_account_is_writable( &programdata_account ) ) ) {
776 0 : fd_log_collector_msg_literal( instr_ctx, "ProgramData is not writable" );
777 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
778 0 : }
779 :
780 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1392-L1393 */
781 374 : fd_guarded_borrowed_account_t program_account = {0};
782 374 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, PROGRAM_ACCOUNT_INDEX, &program_account );
783 :
784 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1394-L1397 */
785 374 : if( FD_UNLIKELY( !fd_borrowed_account_is_writable( &program_account ) ) ) {
786 0 : fd_log_collector_msg_literal( instr_ctx, "Program account is not writable" );
787 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
788 0 : }
789 :
790 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1398-L1401 */
791 374 : if( FD_UNLIKELY( memcmp( program_id, fd_borrowed_account_get_owner( &program_account ), sizeof(fd_pubkey_t) ) ) ) {
792 1 : fd_log_collector_msg_literal( instr_ctx, "Program account not owned by loader" );
793 1 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
794 1 : }
795 :
796 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1403-L1419 */
797 373 : fd_bpf_state_t program_state[1];
798 373 : err = fd_bpf_loader_program_get_state( program_account.acc, program_state );
799 373 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
800 0 : return err;
801 0 : }
802 :
803 373 : if( program_state->discriminant==FD_BPF_STATE_PROGRAM ) {
804 373 : if( FD_UNLIKELY( memcmp( &program_state->inner.program.programdata_address, programdata_key, sizeof(fd_pubkey_t) ) ) ) {
805 83 : fd_log_collector_msg_literal( instr_ctx, "Program account does not match ProgramData account" );
806 83 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
807 83 : }
808 373 : } else {
809 0 : fd_log_collector_msg_literal( instr_ctx, "Invalid Program account" );
810 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
811 0 : }
812 :
813 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1420 */
814 290 : fd_borrowed_account_drop( &program_account );
815 :
816 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1422-L1432 */
817 290 : ulong old_len = fd_borrowed_account_get_data_len( &programdata_account );
818 290 : ulong new_len = fd_ulong_sat_add( old_len, additional_bytes );
819 290 : if( FD_UNLIKELY( new_len>MAX_PERMITTED_DATA_LENGTH ) ) {
820 : /* Max msg_sz: 85 - 6 + 2*20 = 119 < 127 => we can use printf */
821 0 : fd_log_collector_printf_dangerous_max_127( instr_ctx,
822 0 : "Extended ProgramData length of %lu bytes exceeds max account data length of %lu bytes", new_len, MAX_PERMITTED_DATA_LENGTH );
823 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_REALLOC;
824 0 : }
825 :
826 : /* https://github.com/anza-xyz/agave/blob/v4.1.0-beta.1/programs/bpf_loader/src/lib.rs#L861-L883 */
827 290 : if( FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, loader_v3_minimum_extend_program_size ) ) {
828 290 : ulong headroom = fd_ulong_sat_sub( MAX_PERMITTED_DATA_LENGTH, old_len );
829 290 : if( FD_UNLIKELY( additional_bytes<MINIMUM_EXTEND_PROGRAM_BYTES && additional_bytes!=headroom ) ) {
830 : /* Max msg_sz: 113 - (3+2) + (5+10) = 123 < 127 => we can use printf */
831 288 : fd_log_collector_printf_dangerous_max_127(
832 288 : instr_ctx,
833 288 : "ExtendProgram requires a minimum of %lu additional bytes or to extend to maximum size, but only %u were requested",
834 288 : MINIMUM_EXTEND_PROGRAM_BYTES,
835 288 : additional_bytes
836 288 : );
837 288 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
838 288 : }
839 290 : }
840 :
841 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1434-L1437 */
842 2 : fd_sol_sysvar_clock_t clock[1];
843 2 : if( FD_UNLIKELY( !fd_sysvar_cache_clock_read( instr_ctx->sysvar_cache, clock ) ) ) {
844 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
845 0 : }
846 2 : ulong clock_slot = clock->slot;
847 :
848 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1439-L1478 */
849 2 : fd_pubkey_t * upgrade_authority_address = NULL;
850 2 : fd_bpf_state_t programdata_state[1];
851 2 : err = fd_bpf_loader_program_get_state( programdata_account.acc, programdata_state );
852 2 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
853 0 : return err;
854 0 : }
855 2 : if( programdata_state->discriminant==FD_BPF_STATE_PROGRAM_DATA ) {
856 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1444-L1447 */
857 2 : if( FD_UNLIKELY( clock_slot==programdata_state->inner.program_data.slot ) ) {
858 0 : fd_log_collector_msg_literal( instr_ctx, "Program was extended in this block already" );
859 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
860 0 : }
861 :
862 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1449-L1455 */
863 2 : if( FD_UNLIKELY( !programdata_state->inner.program_data.has_upgrade_authority_address ) ) {
864 0 : fd_log_collector_msg_literal( instr_ctx, "Cannot extend ProgramData accounts that are not upgradeable" );
865 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
866 0 : }
867 :
868 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1457-L1472 */
869 2 : if( check_authority ) {
870 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1458-L1463 */
871 0 : fd_pubkey_t const * authority_key = NULL;
872 0 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, AUTHORITY_ACCOUNT_INDEX, &authority_key );
873 0 : if( FD_UNLIKELY( err ) ) {
874 0 : return err;
875 0 : }
876 :
877 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1464-L1467 */
878 0 : if( FD_UNLIKELY( !fd_pubkey_eq( &programdata_state->inner.program_data.upgrade_authority_address, authority_key ) ) ) {
879 0 : fd_log_collector_msg_literal( instr_ctx, "Incorrect upgrade authority provided" );
880 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
881 0 : }
882 :
883 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1468-L1471 */
884 0 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, AUTHORITY_ACCOUNT_INDEX, &err ) ) ) {
885 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
886 0 : if( FD_UNLIKELY( !!err ) ) return err;
887 0 : fd_log_collector_msg_literal( instr_ctx, "Upgrade authority did not sign" );
888 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
889 0 : }
890 0 : }
891 :
892 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1474 */
893 2 : fd_bpf_state_program_data_t * pd = &programdata_state->inner.program_data;
894 2 : upgrade_authority_address = pd->has_upgrade_authority_address ? &pd->upgrade_authority_address : NULL;
895 2 : } else {
896 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1476-L1477 */
897 0 : fd_log_collector_msg_literal( instr_ctx, "ProgramData state is invalid" );
898 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
899 0 : }
900 :
901 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1480-L1485 */
902 2 : fd_rent_t rent_;
903 2 : fd_rent_t const * rent = fd_sysvar_cache_rent_read( instr_ctx->sysvar_cache, &rent_ );
904 2 : if( FD_UNLIKELY( !rent ) ) {
905 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
906 0 : }
907 :
908 2 : ulong balance = fd_borrowed_account_get_lamports( &programdata_account );
909 2 : ulong min_balance = fd_ulong_max( fd_rent_exempt_minimum_balance( rent, new_len ), 1UL );
910 2 : ulong required_payment = fd_ulong_sat_sub( min_balance, balance );
911 :
912 : /* Borrowed accounts need to be dropped before native invocations. Note:
913 : the programdata account is manually released and acquired within the
914 : extend instruction to preserve the local variable scoping to maintain
915 : readability. The scoped macro still successfully handles the case of
916 : freeing a write lock in case of an early termination. */
917 :
918 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1488 */
919 2 : fd_borrowed_account_drop( &programdata_account );
920 :
921 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1492-L1502 */
922 2 : if( FD_UNLIKELY( required_payment>0UL ) ) {
923 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1493-L1496 */
924 2 : fd_pubkey_t const * payer_key = NULL;
925 2 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, optional_payer_account_index, &payer_key );
926 2 : if( FD_UNLIKELY( err ) ) {
927 0 : return err;
928 0 : }
929 :
930 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1498-L1501 */
931 2 : uchar instr_data[FD_TXN_MTU];
932 2 : fd_system_program_instruction_t instr = {
933 2 : .discriminant = FD_SYSTEM_PROGRAM_INSTR_TRANSFER,
934 2 : .inner = {
935 2 : .transfer = required_payment
936 2 : }
937 2 : };
938 :
939 2 : ulong instr_data_sz;
940 2 : int err = fd_system_program_instruction_encode( &instr, instr_data, FD_TXN_MTU, &instr_data_sz );
941 2 : if( FD_UNLIKELY( err ) ) {
942 0 : FD_LOG_CRIT(( "Failed to encode SystemInstruction with error %d", err ));
943 0 : }
944 :
945 2 : fd_vm_rust_account_meta_t acct_metas[ 2UL ];
946 2 : fd_native_cpi_create_account_meta( payer_key, 1UL, 1UL, &acct_metas[ 0UL ] );
947 2 : fd_native_cpi_create_account_meta( programdata_key, 0UL, 1UL, &acct_metas[ 1UL ] );
948 :
949 2 : err = fd_native_cpi_native_invoke( instr_ctx,
950 2 : &fd_solana_system_program_id,
951 2 : instr_data,
952 2 : instr_data_sz,
953 2 : acct_metas,
954 2 : 2UL,
955 2 : NULL,
956 2 : 0UL );
957 2 : if( FD_UNLIKELY( err ) ) {
958 2 : return err;
959 2 : }
960 2 : }
961 :
962 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1506-L1507 */
963 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, PROGRAM_DATA_ACCOUNT_INDEX, &programdata_account );
964 :
965 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1508 */
966 0 : err = fd_borrowed_account_set_data_length( &programdata_account, new_len );
967 0 : if( FD_UNLIKELY( err ) ) {
968 0 : return err;
969 0 : }
970 :
971 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1510 */
972 0 : ulong programdata_data_offset = PROGRAMDATA_METADATA_SIZE;
973 :
974 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1517-L1520 */
975 0 : if( FD_UNLIKELY( programdata_data_offset>fd_borrowed_account_get_data_len( &programdata_account ) ) ) {
976 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
977 0 : }
978 0 : uchar const * programdata_data = fd_borrowed_account_get_data( &programdata_account ) + programdata_data_offset;
979 0 : ulong programdata_size = new_len - PROGRAMDATA_METADATA_SIZE;
980 :
981 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1512-L1522 */
982 : /* SIMD-0500: extend_program is exempt from the deployment-version gate. */
983 0 : err = fd_deploy_program( instr_ctx, programdata_data, programdata_size, /* disable_sbpf_v0_v1_v2_deployment */ 0 );
984 0 : if( FD_UNLIKELY( err ) ) {
985 0 : return err;
986 0 : }
987 :
988 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1523 */
989 0 : fd_borrowed_account_drop( &programdata_account );
990 :
991 : /* Setting the discriminant and upgrade authority address here can likely
992 : be a no-op because these values shouldn't change. These can probably be
993 : removed, but can help to mirror against Agave client's implementation.
994 : The set_state function also contains an ownership check. */
995 :
996 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1525-L1526 */
997 0 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &programdata_account );
998 :
999 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1527-L1530 */
1000 0 : programdata_state->discriminant = FD_BPF_STATE_PROGRAM_DATA;
1001 0 : programdata_state->inner.program_data.slot = clock_slot;
1002 0 : programdata_state->inner.program_data.has_upgrade_authority_address = !!upgrade_authority_address;
1003 0 : if( upgrade_authority_address ) programdata_state->inner.program_data.upgrade_authority_address = *upgrade_authority_address;
1004 :
1005 0 : err = fd_bpf_loader_v3_program_set_state( &programdata_account, programdata_state );
1006 0 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1007 0 : return err;
1008 0 : }
1009 0 : programdata_account.acc->pd_write = 1;
1010 :
1011 : /* Max msg_sz: 41 - 2 + 20 = 57 < 127 => we can use printf
1012 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1532-L1536 */
1013 0 : fd_log_collector_printf_dangerous_max_127( instr_ctx,
1014 0 : "Extended ProgramData account by %u bytes", additional_bytes );
1015 :
1016 : /* programdata account is dropped when it goes out of scope */
1017 :
1018 0 : return FD_EXECUTOR_INSTR_SUCCESS;
1019 :
1020 0 : #undef PROGRAM_DATA_ACCOUNT_INDEX
1021 0 : #undef PROGRAM_ACCOUNT_INDEX
1022 0 : #undef AUTHORITY_ACCOUNT_INDEX
1023 0 : }
1024 :
1025 : /* https://github.com/anza-xyz/agave/blob/77daab497df191ef485a7ad36ed291c1874596e5/programs/bpf_loader/src/lib.rs#L566-L1444 */
1026 : static int
1027 3704 : process_loader_upgradeable_instruction( fd_exec_instr_ctx_t * instr_ctx ) {
1028 3704 : fd_bpf_instruction_t instruction[1] = {0};
1029 3704 : if( FD_UNLIKELY( fd_bpf_instruction_decode(
1030 3704 : instruction,
1031 3704 : instr_ctx->instr->data,
1032 3704 : instr_ctx->instr->data_sz ) ) ) {
1033 51 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1034 51 : }
1035 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/programs/bpf_loader/src/lib.rs#L510 */
1036 3653 : fd_pubkey_t const * program_id = NULL;
1037 3653 : int err = fd_exec_instr_ctx_get_last_program_key( instr_ctx, &program_id );
1038 3653 : if( FD_UNLIKELY( err ) ) {
1039 0 : return err;
1040 0 : }
1041 :
1042 3653 : switch( instruction->discriminant ) {
1043 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L476-L493 */
1044 311 : case FD_BPF_INSTR_INITIALIZE_BUFFER: {
1045 311 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 2U ) ) ) {
1046 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1047 0 : }
1048 :
1049 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L479 */
1050 311 : fd_guarded_borrowed_account_t buffer = {0};
1051 311 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &buffer );
1052 :
1053 311 : fd_bpf_state_t buffer_state[1];
1054 311 : err = fd_bpf_loader_program_get_state( buffer.acc, buffer_state );
1055 311 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1056 0 : return err;
1057 0 : }
1058 :
1059 311 : if( FD_UNLIKELY( buffer_state->discriminant!=FD_BPF_STATE_UNINITIALIZED ) ) {
1060 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer account already initialized" );
1061 0 : return FD_EXECUTOR_INSTR_ERR_ACC_ALREADY_INITIALIZED;
1062 0 : }
1063 :
1064 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L487-L489 */
1065 311 : fd_pubkey_t const * authority_key = NULL;
1066 311 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &authority_key );
1067 311 : if( FD_UNLIKELY( err ) ) {
1068 0 : return err;
1069 0 : }
1070 :
1071 311 : buffer_state->discriminant = FD_BPF_STATE_BUFFER;
1072 311 : buffer_state->inner.buffer.has_authority_address = 1;
1073 311 : buffer_state->inner.buffer.authority_address = *authority_key;
1074 :
1075 311 : err = fd_bpf_loader_v3_program_set_state( &buffer, buffer_state );
1076 311 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1077 297 : return err;
1078 297 : }
1079 :
1080 : /* implicit drop of buffer account */
1081 :
1082 14 : break;
1083 311 : }
1084 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L494-L525 */
1085 278 : case FD_BPF_INSTR_WRITE: {
1086 278 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 2U ) ) ) {
1087 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1088 0 : }
1089 :
1090 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L497 */
1091 278 : fd_guarded_borrowed_account_t buffer = {0};
1092 278 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &buffer );
1093 :
1094 278 : fd_bpf_state_t loader_state[1];
1095 278 : err = fd_bpf_loader_program_get_state( buffer.acc, loader_state );
1096 278 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1097 16 : return err;
1098 16 : }
1099 :
1100 262 : if( loader_state->discriminant==FD_BPF_STATE_BUFFER ) {
1101 262 : if( FD_UNLIKELY( !loader_state->inner.buffer.has_authority_address ) ) {
1102 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer is immutable" );
1103 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
1104 0 : }
1105 :
1106 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L505-L507 */
1107 262 : fd_pubkey_t const * authority_key = NULL;
1108 262 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &authority_key );
1109 262 : if( FD_UNLIKELY( err ) ) {
1110 0 : return err;
1111 0 : }
1112 :
1113 262 : if( FD_UNLIKELY( !fd_pubkey_eq( &loader_state->inner.buffer.authority_address, authority_key ) ) ) {
1114 1 : fd_log_collector_msg_literal( instr_ctx, "Incorrect buffer authority provided" );
1115 1 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1116 1 : }
1117 261 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 1UL, &err ) ) ) {
1118 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1119 1 : if( FD_UNLIKELY( !!err ) ) return err;
1120 1 : fd_log_collector_msg_literal( instr_ctx, "Buffer authority did not sign" );
1121 1 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1122 1 : }
1123 261 : } else {
1124 0 : fd_log_collector_msg_literal( instr_ctx, "Invalid Buffer account" );
1125 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1126 0 : }
1127 :
1128 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L520 */
1129 260 : fd_borrowed_account_drop( &buffer );
1130 :
1131 260 : ulong program_data_offset = fd_ulong_sat_add( BUFFER_METADATA_SIZE, instruction->inner.write.offset );
1132 260 : err = write_program_data( instr_ctx,
1133 260 : 0UL,
1134 260 : program_data_offset,
1135 260 : instruction->inner.write.bytes,
1136 260 : instruction->inner.write.bytes_len );
1137 260 : if( FD_UNLIKELY( err ) ) {
1138 1 : return err;
1139 1 : }
1140 :
1141 259 : break;
1142 260 : }
1143 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L526-L702 */
1144 360 : case FD_BPF_INSTR_DEPLOY_WITH_MAX_DATA_LEN: {
1145 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L527-L541 */
1146 360 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 4U ) ) ) {
1147 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1148 0 : }
1149 :
1150 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L529-L534 */
1151 360 : fd_pubkey_t const * payer_key = NULL;
1152 360 : fd_pubkey_t const * programdata_key = NULL;
1153 :
1154 360 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 0UL, &payer_key );
1155 360 : if( FD_UNLIKELY( err ) ) {
1156 0 : return err;
1157 0 : }
1158 :
1159 360 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &programdata_key );
1160 360 : if( FD_UNLIKELY( err ) ) {
1161 0 : return err;
1162 0 : }
1163 :
1164 360 : err = fd_sysvar_instr_acct_check( instr_ctx, 4UL, &fd_sysvar_rent_id );
1165 360 : if( FD_UNLIKELY( err ) ) {
1166 2 : return err;
1167 2 : }
1168 :
1169 358 : fd_rent_t rent_;
1170 358 : fd_rent_t const * rent = fd_sysvar_cache_rent_read( instr_ctx->sysvar_cache, &rent_ );
1171 358 : if( FD_UNLIKELY( !rent ) ) {
1172 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1173 0 : }
1174 :
1175 358 : err = fd_sysvar_instr_acct_check( instr_ctx, 5UL, &fd_sysvar_clock_id );
1176 358 : if( FD_UNLIKELY( err ) ) {
1177 22 : return err;
1178 22 : }
1179 :
1180 336 : fd_sol_sysvar_clock_t clock_;
1181 336 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( instr_ctx->sysvar_cache, &clock_ );
1182 336 : if( FD_UNLIKELY( !clock ) ) {
1183 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1184 0 : }
1185 :
1186 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L538 */
1187 336 : if( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 8U ) ) {
1188 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1189 0 : }
1190 :
1191 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L539-L541 */
1192 336 : fd_pubkey_t const * authority_key = NULL;
1193 336 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 7UL, &authority_key );
1194 336 : if( FD_UNLIKELY( err ) ) return err;
1195 :
1196 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L542-L560 */
1197 : /* Verify Program account */
1198 336 : fd_pubkey_t const * new_program_id = NULL;
1199 :
1200 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L545 */
1201 336 : fd_guarded_borrowed_account_t program = {0};
1202 336 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 2UL, &program );
1203 :
1204 336 : fd_bpf_state_t loader_state[1];
1205 336 : int err = fd_bpf_loader_program_get_state( program.acc, loader_state );
1206 336 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1207 0 : return err;
1208 0 : }
1209 336 : if( FD_UNLIKELY( loader_state->discriminant!=FD_BPF_STATE_UNINITIALIZED ) ) {
1210 0 : fd_log_collector_msg_literal( instr_ctx, "Program account already initialized" );
1211 0 : return FD_EXECUTOR_INSTR_ERR_ACC_ALREADY_INITIALIZED;
1212 0 : }
1213 336 : if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &program )<SIZE_OF_PROGRAM ) ) {
1214 0 : fd_log_collector_msg_literal( instr_ctx, "Program account too small" );
1215 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
1216 0 : }
1217 336 : if( FD_UNLIKELY( fd_borrowed_account_get_lamports( &program )<
1218 336 : fd_rent_exempt_minimum_balance( rent, fd_borrowed_account_get_data_len( &program ) ) ) ) {
1219 0 : fd_log_collector_msg_literal( instr_ctx, "Program account not rent-exempt" );
1220 0 : return FD_EXECUTOR_INSTR_ERR_EXECUTABLE_ACCOUNT_NOT_RENT_EXEMPT;
1221 0 : }
1222 336 : new_program_id = (fd_pubkey_t*)program.acc->pubkey;
1223 :
1224 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L560 */
1225 336 : fd_borrowed_account_drop( &program );
1226 :
1227 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L561-L600 */
1228 : /* Verify Buffer account */
1229 :
1230 336 : fd_pubkey_t const* buffer_key = NULL;
1231 336 : ulong buffer_data_offset = 0UL;
1232 336 : ulong buffer_data_len = 0UL;
1233 336 : ulong programdata_len = 0UL;
1234 :
1235 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L564-L565 */
1236 336 : fd_guarded_borrowed_account_t buffer = {0};
1237 336 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 3UL, &buffer );
1238 :
1239 : /* https://github.com/anza-xyz/agave/blob/645f638832e19269d7d1c8614bed3b3b9badb158/programs/bpf_loader/src/lib.rs#L234-L241 */
1240 336 : if( FD_UNLIKELY( !fd_borrowed_account_is_writable( &buffer ) ) ) {
1241 1 : fd_log_collector_msg_literal( instr_ctx, "Buffer account not writeable" );
1242 1 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1243 1 : }
1244 :
1245 335 : if( FD_UNLIKELY( memcmp( program_id, fd_borrowed_account_get_owner( &buffer ), sizeof(fd_pubkey_t) ) ) ) {
1246 20 : fd_log_collector_msg_literal( instr_ctx, "Buffer account not owned by loader" );
1247 20 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_PROGRAM_ID;
1248 20 : }
1249 :
1250 315 : fd_bpf_state_t buffer_state[1];
1251 315 : err = fd_bpf_loader_program_get_state( buffer.acc, buffer_state );
1252 315 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1253 54 : return err;
1254 54 : }
1255 :
1256 261 : if( buffer_state->discriminant==FD_BPF_STATE_BUFFER ) {
1257 261 : if( FD_UNLIKELY( (authority_key==NULL) != (!buffer_state->inner.buffer.has_authority_address) ||
1258 261 : (authority_key!=NULL && !fd_pubkey_eq( &buffer_state->inner.buffer.authority_address, authority_key ) ) ) ) {
1259 13 : fd_log_collector_msg_literal( instr_ctx, "Buffer and upgrade authority don't match" );
1260 13 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1261 13 : }
1262 248 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 7UL, &err ) ) ) {
1263 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1264 4 : if( FD_UNLIKELY( !!err ) ) return err;
1265 4 : fd_log_collector_msg_literal( instr_ctx, "Upgrade authority did not sign" );
1266 4 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1267 4 : }
1268 248 : } else {
1269 0 : fd_log_collector_msg_literal( instr_ctx, "Invalid Buffer account" );
1270 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1271 0 : }
1272 244 : buffer_key = (fd_pubkey_t*)buffer.acc->pubkey;
1273 244 : buffer_data_offset = BUFFER_METADATA_SIZE;
1274 244 : buffer_data_len = fd_ulong_sat_sub( fd_borrowed_account_get_data_len( &buffer ), buffer_data_offset );
1275 : /* UpgradeableLoaderState::size_of_program_data( max_data_len ) */
1276 244 : programdata_len = fd_ulong_sat_add( PROGRAMDATA_METADATA_SIZE,
1277 244 : instruction->inner.deploy_with_max_data_len.max_data_len );
1278 :
1279 244 : if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &buffer )<BUFFER_METADATA_SIZE || buffer_data_len==0UL ) ) {
1280 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer account too small" );
1281 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1282 0 : }
1283 :
1284 244 : if( FD_UNLIKELY( instruction->inner.deploy_with_max_data_len.max_data_len<buffer_data_len ) ) {
1285 0 : fd_log_collector_msg_literal( instr_ctx, "Max data length is too small to hold Buffer data" );
1286 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
1287 0 : }
1288 :
1289 244 : if( FD_UNLIKELY( programdata_len>MAX_PERMITTED_DATA_LENGTH ) ) {
1290 0 : fd_log_collector_msg_literal( instr_ctx, "Max data length is too large" );
1291 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1292 0 : }
1293 :
1294 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L590 */
1295 244 : fd_borrowed_account_drop( &buffer );
1296 :
1297 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L602-L608 */
1298 : /* Create ProgramData account */
1299 :
1300 244 : fd_pubkey_t derived_address[ 1UL ];
1301 244 : uchar const * seeds[ 1UL ];
1302 244 : seeds[ 0UL ] = (uchar const *)new_program_id;
1303 244 : ulong seed_sz = sizeof(fd_pubkey_t);
1304 244 : uchar bump_seed = 0;
1305 244 : err = fd_pubkey_find_program_address( program_id, 1UL, seeds, &seed_sz, derived_address,
1306 244 : &bump_seed, &instr_ctx->txn_out->err.custom_err );
1307 244 : if( FD_UNLIKELY( err ) ) {
1308 : /* TODO: We should handle these errors more gracefully instead of just killing the client (e.g. excluding the transaction
1309 : from the block). */
1310 0 : FD_LOG_ERR(( "Unable to find a viable program address bump seed" )); // Solana panics, error code is undefined
1311 0 : return err;
1312 0 : }
1313 244 : if( FD_UNLIKELY( memcmp( derived_address, programdata_key, sizeof(fd_pubkey_t) ) ) ) {
1314 1 : fd_log_collector_msg_literal( instr_ctx, "ProgramData address is not derived" );
1315 1 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1316 1 : }
1317 :
1318 : /* Drain the Buffer account to payer before paying for programdata account in a local scope
1319 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L612-L628 */
1320 :
1321 243 : do {
1322 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L615 */
1323 243 : fd_guarded_borrowed_account_t payer = {0};
1324 243 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &payer );
1325 :
1326 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L613 */
1327 243 : fd_guarded_borrowed_account_t buffer = {0};
1328 243 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 3UL, &buffer );
1329 :
1330 243 : err = fd_borrowed_account_checked_add_lamports( &payer, fd_borrowed_account_get_lamports( &buffer ) );
1331 243 : if( FD_UNLIKELY( err ) ) {
1332 0 : return err;
1333 0 : }
1334 243 : err = fd_borrowed_account_set_lamports( &buffer, 0UL );
1335 243 : if( FD_UNLIKELY( err ) ) {
1336 0 : return err;
1337 0 : }
1338 243 : } while (0);
1339 :
1340 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L628-L642 */
1341 : /* Pass an extra account to avoid the overly strict unbalanced instruction error */
1342 : /* Invoke the system program to create the new account */
1343 243 : uchar instr_data[FD_TXN_MTU];
1344 243 : create_account_t create_acct = {
1345 243 : .lamports = fd_rent_exempt_minimum_balance( rent, programdata_len ),
1346 243 : .space = programdata_len,
1347 243 : .owner = *program_id,
1348 243 : };
1349 243 : if( !create_acct.lamports ) {
1350 0 : create_acct.lamports = 1UL;
1351 0 : }
1352 :
1353 243 : fd_system_program_instruction_t instr = {
1354 243 : .discriminant = FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT,
1355 243 : .inner = {
1356 243 : .create_account = create_acct,
1357 243 : }
1358 243 : };
1359 :
1360 243 : ulong instr_data_sz;
1361 243 : err = fd_system_program_instruction_encode( &instr, instr_data, FD_TXN_MTU, &instr_data_sz );
1362 243 : if( FD_UNLIKELY( err ) ) {
1363 0 : FD_LOG_CRIT(( "Failed to encode SystemInstruction with error %d", err ));
1364 0 : }
1365 :
1366 243 : fd_vm_rust_account_meta_t acct_metas[ 3UL ];
1367 243 : fd_native_cpi_create_account_meta( payer_key, 1U, 1U, &acct_metas[ 0UL ] );
1368 243 : fd_native_cpi_create_account_meta( programdata_key, 1U, 1U, &acct_metas[ 1UL ] );
1369 243 : fd_native_cpi_create_account_meta( buffer_key, 0U, 1U, &acct_metas[ 2UL ] );
1370 :
1371 : /* caller_program_id == program_id */
1372 243 : fd_pubkey_t signers[ 1UL ];
1373 243 : err = fd_pubkey_derive_pda( program_id, 1UL, seeds, &seed_sz, &bump_seed, signers, &instr_ctx->txn_out->err.custom_err );
1374 243 : if( FD_UNLIKELY( err ) ) {
1375 0 : return err;
1376 0 : }
1377 243 : err = fd_native_cpi_native_invoke( instr_ctx,
1378 243 : &fd_solana_system_program_id,
1379 243 : instr_data,
1380 243 : instr_data_sz,
1381 243 : acct_metas,
1382 243 : 3UL,
1383 243 : signers,
1384 243 : 1UL );
1385 243 : if( FD_UNLIKELY( err ) ) {
1386 0 : return err;
1387 0 : }
1388 :
1389 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L644-L665 */
1390 : /* Load and verify the program bits */
1391 :
1392 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L648-L649 */
1393 243 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 3UL, &buffer );
1394 :
1395 243 : if( FD_UNLIKELY( buffer_data_offset>fd_borrowed_account_get_data_len( &buffer ) ) ) {
1396 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
1397 0 : }
1398 :
1399 243 : const uchar * buffer_data = fd_borrowed_account_get_data( &buffer ) + buffer_data_offset;
1400 :
1401 243 : err = fd_deploy_program( instr_ctx, buffer_data, buffer_data_len,
1402 243 : FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, disable_sbpf_v0_v1_v2_deployment ) );
1403 243 : if( FD_UNLIKELY( err ) ) {
1404 69 : return err;
1405 69 : }
1406 :
1407 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L657 */
1408 174 : fd_borrowed_account_drop( &buffer );
1409 :
1410 : /* Update the ProgramData account and record the program bits in a local scope
1411 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L669-L691 */
1412 174 : do {
1413 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L670-L671 */
1414 174 : fd_guarded_borrowed_account_t programdata = {0};
1415 174 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 1UL, &programdata );
1416 :
1417 174 : fd_bpf_state_t programdata_loader_state = {
1418 174 : .discriminant = FD_BPF_STATE_PROGRAM_DATA,
1419 174 : .inner.program_data = {
1420 174 : .slot = clock->slot,
1421 174 : .has_upgrade_authority_address = !!authority_key,
1422 174 : .upgrade_authority_address = authority_key ? *authority_key : (fd_pubkey_t){{0}},
1423 174 : },
1424 174 : };
1425 174 : err = fd_bpf_loader_v3_program_set_state( &programdata, &programdata_loader_state );
1426 174 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1427 0 : return err;
1428 0 : }
1429 174 : programdata.acc->pd_write = 1;
1430 :
1431 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L675-L689 */
1432 174 : if( FD_UNLIKELY( PROGRAMDATA_METADATA_SIZE+buffer_data_len>fd_borrowed_account_get_data_len( &programdata ) ) ) {
1433 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
1434 0 : }
1435 :
1436 174 : uchar * programdata_data = NULL;
1437 174 : ulong programdata_dlen = 0UL;
1438 174 : err = fd_borrowed_account_get_data_mut( &programdata, &programdata_data, &programdata_dlen );
1439 174 : if( FD_UNLIKELY( err ) ) {
1440 0 : return err;
1441 0 : }
1442 :
1443 174 : uchar * dst_slice = programdata_data + PROGRAMDATA_METADATA_SIZE;
1444 174 : ulong dst_slice_len = buffer_data_len;
1445 :
1446 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L683-L684 */
1447 174 : fd_guarded_borrowed_account_t buffer = {0};
1448 174 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 3UL, &buffer );
1449 :
1450 174 : if( FD_UNLIKELY( buffer_data_offset>fd_borrowed_account_get_data_len( &buffer ) ) ) {
1451 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
1452 0 : }
1453 174 : const uchar * src_slice = fd_borrowed_account_get_data( &buffer ) + buffer_data_offset;
1454 174 : fd_memcpy( dst_slice, src_slice, dst_slice_len );
1455 : /* Update buffer data length.
1456 : BUFFER_METADATA_SIZE == UpgradeableLoaderState::size_of_buffer(0) */
1457 174 : err = fd_borrowed_account_set_data_length( &buffer, BUFFER_METADATA_SIZE );
1458 174 : if( FD_UNLIKELY( err ) ) {
1459 0 : return err;
1460 0 : }
1461 174 : } while(0);
1462 :
1463 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L692-L699 */
1464 :
1465 : /* Update the Program account
1466 : https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L694-L695 */
1467 174 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 2UL, &program );
1468 :
1469 174 : loader_state->discriminant = FD_BPF_STATE_PROGRAM;
1470 174 : loader_state->inner.program.programdata_address = *programdata_key;
1471 174 : err = fd_bpf_loader_v3_program_set_state( &program, loader_state );
1472 174 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1473 0 : return err;
1474 0 : }
1475 174 : err = fd_borrowed_account_set_executable( &program, 1 );
1476 174 : if( FD_UNLIKELY( err ) ) {
1477 0 : return err;
1478 0 : }
1479 :
1480 174 : FD_BASE58_ENCODE_32_BYTES( program.acc->pubkey, program_b58 );
1481 174 : FD_LOG_INFO(( "Program deployed %s", program_b58 ));
1482 :
1483 : /* Max msg_sz: 19 - 2 + 45 = 62 < 127 => we can use printf */
1484 174 : fd_log_collector_printf_dangerous_max_127( instr_ctx, "Deployed program %s", program_b58 );
1485 :
1486 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L700 */
1487 174 : fd_borrowed_account_drop( &program );
1488 :
1489 174 : break;
1490 174 : }
1491 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L703-L891 */
1492 497 : case FD_BPF_INSTR_UPGRADE: {
1493 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L704-L714 */
1494 497 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 3U ) ) ) {
1495 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1496 0 : }
1497 :
1498 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L706-L708 */
1499 497 : fd_pubkey_t const * programdata_key = NULL;
1500 497 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 0UL, &programdata_key );
1501 497 : if( FD_UNLIKELY( err ) ) {
1502 0 : return err;
1503 0 : }
1504 :
1505 : /* rent is accessed directly from the epoch bank and the clock from the
1506 : slot context. However, a check must be done to make sure that the
1507 : sysvars are correctly included in the set of transaction accounts. */
1508 497 : err = fd_sysvar_instr_acct_check( instr_ctx, 4UL, &fd_sysvar_rent_id );
1509 497 : if( FD_UNLIKELY( err ) ) {
1510 1 : return err;
1511 1 : }
1512 :
1513 496 : fd_rent_t rent_;
1514 496 : fd_rent_t const * rent = fd_sysvar_cache_rent_read( instr_ctx->sysvar_cache, &rent_ );
1515 496 : if( FD_UNLIKELY( !rent ) ) {
1516 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1517 0 : }
1518 :
1519 496 : err = fd_sysvar_instr_acct_check( instr_ctx, 5UL, &fd_sysvar_clock_id );
1520 496 : if( FD_UNLIKELY( err ) ) {
1521 25 : return err;
1522 25 : }
1523 :
1524 471 : fd_sol_sysvar_clock_t clock_;
1525 471 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( instr_ctx->sysvar_cache, &clock_ );
1526 471 : if( FD_UNLIKELY( !clock ) ) {
1527 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
1528 0 : }
1529 :
1530 471 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 7U ) ) ) {
1531 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1532 0 : }
1533 :
1534 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L713-L715 */
1535 471 : fd_pubkey_t const * authority_key = NULL;
1536 471 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 6UL, &authority_key );
1537 471 : if( FD_UNLIKELY( err ) ) return err;
1538 :
1539 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L716-L745 */
1540 : /* Verify Program account */
1541 :
1542 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L719-L720 */
1543 471 : fd_guarded_borrowed_account_t program = {0};
1544 471 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 1UL, &program );
1545 :
1546 471 : if( FD_UNLIKELY( !fd_borrowed_account_is_writable( &program ) ) ) {
1547 0 : fd_log_collector_msg_literal( instr_ctx, "Program account not writeable" );
1548 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1549 0 : }
1550 471 : if( FD_UNLIKELY( memcmp( fd_borrowed_account_get_owner( &program ), program_id, sizeof(fd_pubkey_t) ) ) ) {
1551 0 : fd_log_collector_msg_literal( instr_ctx, "Program account not owned by loader" );
1552 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_PROGRAM_ID;
1553 0 : }
1554 471 : fd_bpf_state_t program_state[1];
1555 471 : err = fd_bpf_loader_program_get_state( program.acc, program_state );
1556 471 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1557 0 : return err;
1558 0 : }
1559 471 : if( FD_UNLIKELY( program_state->discriminant==FD_BPF_STATE_PROGRAM ) ) {
1560 471 : if( FD_UNLIKELY( memcmp( &program_state->inner.program.programdata_address, programdata_key, sizeof(fd_pubkey_t) ) ) ) {
1561 211 : fd_log_collector_msg_literal( instr_ctx, "Program and ProgramData account mismatch" );
1562 211 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1563 211 : }
1564 471 : } else {
1565 0 : fd_log_collector_msg_literal( instr_ctx, "Invalid Program account" );
1566 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1567 0 : }
1568 :
1569 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L746 */
1570 260 : fd_pubkey_t new_program_id = *(fd_pubkey_t*)program.acc->pubkey;
1571 260 : fd_borrowed_account_drop( &program );
1572 :
1573 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L747-L773 */
1574 : /* Verify Buffer account */
1575 :
1576 260 : ulong buffer_lamports = 0UL;
1577 260 : ulong buffer_data_offset = 0UL;
1578 260 : ulong buffer_data_len = 0UL;
1579 :
1580 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L750-L751 */
1581 260 : fd_guarded_borrowed_account_t buffer = {0};
1582 260 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 2UL, &buffer );
1583 :
1584 : /* https://github.com/anza-xyz/agave/blob/645f638832e19269d7d1c8614bed3b3b9badb158/programs/bpf_loader/src/lib.rs#L407-L414 */
1585 260 : if( FD_UNLIKELY( !fd_borrowed_account_is_writable( &buffer ) ) ) {
1586 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer account not writeable" );
1587 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1588 0 : }
1589 :
1590 260 : if( FD_UNLIKELY( memcmp( program_id, fd_borrowed_account_get_owner( &buffer ), sizeof(fd_pubkey_t) ) ) ) {
1591 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer account not owned by loader" );
1592 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_PROGRAM_ID;
1593 0 : }
1594 :
1595 260 : fd_bpf_state_t buffer_state[1];
1596 260 : err = fd_bpf_loader_program_get_state( buffer.acc, buffer_state );
1597 260 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1598 0 : return err;
1599 0 : }
1600 260 : if( buffer_state->discriminant==FD_BPF_STATE_BUFFER ) {
1601 260 : if( FD_UNLIKELY( (authority_key==NULL) != (!buffer_state->inner.buffer.has_authority_address) ||
1602 260 : (authority_key!=NULL && !fd_pubkey_eq( &buffer_state->inner.buffer.authority_address, authority_key ) ) ) ) {
1603 5 : fd_log_collector_msg_literal( instr_ctx, "Buffer and upgrade authority don't match" );
1604 5 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1605 5 : }
1606 255 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 6UL, &err ) ) ) {
1607 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1608 5 : if( FD_UNLIKELY( !!err ) ) return err;
1609 5 : fd_log_collector_msg_literal( instr_ctx, "Upgrade authority did not sign" );
1610 5 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1611 5 : }
1612 255 : } else {
1613 0 : fd_log_collector_msg_literal( instr_ctx, "Invalid Buffer account" );
1614 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1615 0 : }
1616 250 : buffer_lamports = fd_borrowed_account_get_lamports( &buffer );
1617 250 : buffer_data_offset = BUFFER_METADATA_SIZE;
1618 250 : buffer_data_len = fd_ulong_sat_sub( fd_borrowed_account_get_data_len( &buffer ), buffer_data_offset );
1619 250 : if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &buffer )<BUFFER_METADATA_SIZE || buffer_data_len==0UL ) ) {
1620 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer account too small" );
1621 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1622 0 : }
1623 :
1624 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L774 */
1625 250 : fd_borrowed_account_drop( &buffer );
1626 :
1627 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L775-L823 */
1628 : /* Verify ProgramData account */
1629 :
1630 250 : ulong programdata_data_offset = PROGRAMDATA_METADATA_SIZE;
1631 250 : ulong programdata_len = 0UL;
1632 250 : ulong programdata_balance_required = 0UL;
1633 :
1634 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L778-L779 */
1635 250 : fd_guarded_borrowed_account_t programdata = {0};
1636 250 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &programdata );
1637 :
1638 : /* SIMD-0433: ProgramData account is resized to hold the new ELF */
1639 250 : err = fd_bpf_loader_v3_upgrade_sizing(
1640 250 : FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, loader_v3_set_program_data_to_elf_length ),
1641 250 : fd_borrowed_account_get_data_len( &programdata ),
1642 250 : buffer_data_len,
1643 250 : rent,
1644 250 : &programdata_len,
1645 250 : &programdata_balance_required );
1646 250 : if( FD_UNLIKELY( err==FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA ) ) {
1647 0 : fd_log_collector_printf_dangerous_max_127( instr_ctx,
1648 0 : "Resized ProgramData length of %lu bytes exceeds max account data length", programdata_len );
1649 0 : return err;
1650 0 : }
1651 250 : if( FD_UNLIKELY( err==FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL ) ) {
1652 0 : fd_log_collector_msg_literal( instr_ctx, "ProgramData account not large enough" );
1653 0 : return err;
1654 0 : }
1655 250 : if( FD_UNLIKELY( fd_ulong_sat_add( fd_borrowed_account_get_lamports( &programdata ), buffer_lamports )<programdata_balance_required ) ) {
1656 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer account balance too low to fund upgrade" );
1657 0 : return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
1658 0 : }
1659 :
1660 250 : fd_bpf_state_t programdata_state[1];
1661 250 : err = fd_bpf_loader_program_get_state( programdata.acc, programdata_state );
1662 250 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1663 0 : return err;
1664 0 : }
1665 :
1666 250 : if( programdata_state->discriminant==FD_BPF_STATE_PROGRAM_DATA ) {
1667 250 : if( FD_UNLIKELY( clock->slot==programdata_state->inner.program_data.slot ) ) {
1668 242 : fd_log_collector_msg_literal( instr_ctx, "Program was deployed in this block already" );
1669 242 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1670 242 : }
1671 8 : if( FD_UNLIKELY( !programdata_state->inner.program_data.has_upgrade_authority_address ) ) {
1672 0 : fd_log_collector_msg_literal( instr_ctx, "Program not upgradeable" );
1673 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
1674 0 : }
1675 8 : if( FD_UNLIKELY( !fd_pubkey_eq( &programdata_state->inner.program_data.upgrade_authority_address, authority_key ) ) ) {
1676 0 : fd_log_collector_msg_literal( instr_ctx, "Incorrect upgrade authority provided" );
1677 0 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1678 0 : }
1679 8 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 6UL, &err ) ) ) {
1680 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1681 0 : if( FD_UNLIKELY( !!err ) ) return err;
1682 0 : fd_log_collector_msg_literal( instr_ctx, "Upgrade authority did not sign" );
1683 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1684 0 : }
1685 8 : } else {
1686 0 : fd_log_collector_msg_literal( instr_ctx, "Invalid ProgramData account" );
1687 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
1688 0 : }
1689 :
1690 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L824 */
1691 8 : fd_borrowed_account_drop( &programdata );
1692 :
1693 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L825-L845 */
1694 : /* Load and verify the program bits */
1695 :
1696 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L827-L828 */
1697 8 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 2UL, &buffer );
1698 :
1699 8 : if( FD_UNLIKELY( buffer_data_offset>fd_borrowed_account_get_data_len( &buffer ) ) ) {
1700 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
1701 0 : }
1702 :
1703 8 : const uchar * buffer_data = fd_borrowed_account_get_data( &buffer ) + buffer_data_offset;
1704 8 : err = fd_deploy_program( instr_ctx, buffer_data, buffer_data_len,
1705 8 : FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, disable_sbpf_v0_v1_v2_deployment ) );
1706 8 : if( FD_UNLIKELY( err ) ) {
1707 3 : return err;
1708 3 : }
1709 :
1710 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L836 */
1711 5 : fd_borrowed_account_drop( &buffer );
1712 :
1713 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L849-L850 */
1714 5 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &programdata );
1715 :
1716 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L846-L874 */
1717 : /* Update the ProgramData account, record the upgraded data, and zero the rest in a local scope */
1718 5 : do {
1719 5 : err = fd_borrowed_account_set_data_length( &programdata, programdata_len );
1720 5 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1721 0 : return err;
1722 0 : }
1723 :
1724 5 : programdata_state->discriminant = FD_BPF_STATE_PROGRAM_DATA;
1725 5 : programdata_state->inner.program_data.slot = clock->slot;
1726 5 : programdata_state->inner.program_data.has_upgrade_authority_address = 1;
1727 5 : programdata_state->inner.program_data.upgrade_authority_address = *authority_key;
1728 5 : err = fd_bpf_loader_v3_program_set_state( &programdata, programdata_state );
1729 5 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1730 0 : return err;
1731 0 : }
1732 5 : programdata.acc->pd_write = 1;
1733 :
1734 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L846-L875 */
1735 : /* We want to copy over the data and zero out the rest */
1736 5 : if( FD_UNLIKELY( programdata_data_offset+buffer_data_len>fd_borrowed_account_get_data_len( &programdata ) ) ) {
1737 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
1738 0 : }
1739 :
1740 5 : uchar * programdata_data = NULL;
1741 5 : ulong programdata_dlen = 0UL;
1742 5 : err = fd_borrowed_account_get_data_mut( &programdata, &programdata_data, &programdata_dlen );
1743 5 : if( FD_UNLIKELY( err ) ) {
1744 0 : return err;
1745 0 : }
1746 5 : uchar * dst_slice = programdata_data + programdata_data_offset;
1747 5 : ulong dst_slice_len = buffer_data_len;
1748 :
1749 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L863-L864 */
1750 5 : fd_guarded_borrowed_account_t buffer = {0};
1751 5 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 2UL, &buffer );
1752 :
1753 5 : if( FD_UNLIKELY( buffer_data_offset>fd_borrowed_account_get_data_len( &buffer ) ) ){
1754 0 : return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
1755 0 : }
1756 :
1757 5 : const uchar * src_slice = fd_borrowed_account_get_data( &buffer ) + buffer_data_offset;
1758 5 : fd_memcpy( dst_slice, src_slice, dst_slice_len );
1759 5 : fd_memset( dst_slice + dst_slice_len, 0, fd_borrowed_account_get_data_len( &programdata ) - programdata_data_offset - dst_slice_len );
1760 :
1761 : /* implicit drop of buffer */
1762 5 : } while (0);
1763 :
1764 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L876-L891 */
1765 : /* Fund ProgramData to rent-exemption, spill the rest */
1766 :
1767 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L878-L879 */
1768 5 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 2UL, &buffer );
1769 :
1770 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L880-L881 */
1771 5 : fd_guarded_borrowed_account_t spill = {0};
1772 5 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 3UL, &spill );
1773 :
1774 5 : ulong spill_addend = fd_ulong_sat_sub( fd_ulong_sat_add( fd_borrowed_account_get_lamports( &programdata ), buffer_lamports ),
1775 5 : programdata_balance_required );
1776 5 : err = fd_borrowed_account_checked_add_lamports( &spill, spill_addend );
1777 5 : if( FD_UNLIKELY( err ) ) {
1778 0 : return err;
1779 0 : }
1780 5 : err = fd_borrowed_account_set_lamports( &buffer, 0UL );
1781 5 : if( FD_UNLIKELY( err ) ) {
1782 0 : return err;
1783 0 : }
1784 5 : err = fd_borrowed_account_set_lamports( &programdata, programdata_balance_required );
1785 5 : if( FD_UNLIKELY( err ) ) {
1786 0 : return err;
1787 0 : }
1788 :
1789 : /* Buffer account set_data_length */
1790 5 : err = fd_borrowed_account_set_data_length( &buffer, BUFFER_METADATA_SIZE );
1791 5 : if( FD_UNLIKELY( err ) ) {
1792 0 : return err;
1793 0 : }
1794 :
1795 : /* buffer is dropped when it goes out of scope */
1796 : /* spill is dropped when it goes out of scope */
1797 : /* programdata is dropped when it goes out of scope */
1798 :
1799 : /* Max msg_sz: 19 - 2 + 45 = 62 < 127 => we can use printf */
1800 5 : FD_BASE58_ENCODE_32_BYTES( new_program_id.uc, program_id_b58 );
1801 5 : fd_log_collector_printf_dangerous_max_127( instr_ctx, "Upgraded program %s", program_id_b58 );
1802 :
1803 5 : break;
1804 5 : }
1805 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L893-L957 */
1806 303 : case FD_BPF_INSTR_SET_AUTHORITY: {
1807 303 : int err;
1808 303 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 2U ) ) ) {
1809 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1810 0 : }
1811 :
1812 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L896-L897 */
1813 303 : fd_guarded_borrowed_account_t account = {0};
1814 303 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &account );
1815 :
1816 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L898-L900 */
1817 303 : fd_pubkey_t const * present_authority_key = NULL;
1818 303 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &present_authority_key );
1819 303 : if( FD_UNLIKELY( err ) ) {
1820 0 : return err;
1821 0 : }
1822 :
1823 : /* Don't check the error here because the new_authority key is allowed to be NULL until further checks.
1824 : https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L901-L906 */
1825 303 : fd_pubkey_t const * new_authority = NULL;
1826 303 : fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 2UL, &new_authority );
1827 :
1828 303 : fd_bpf_state_t account_state[1];
1829 303 : err = fd_bpf_loader_program_get_state( account.acc, account_state );
1830 303 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1831 2 : return err;
1832 2 : }
1833 :
1834 301 : if( account_state->discriminant==FD_BPF_STATE_BUFFER ) {
1835 152 : if( FD_UNLIKELY( !new_authority ) ) {
1836 13 : fd_log_collector_msg_literal( instr_ctx, "Buffer authority is not optional" );
1837 13 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1838 13 : }
1839 139 : if( FD_UNLIKELY( !account_state->inner.buffer.has_authority_address ) ) {
1840 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer is immutable" );
1841 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
1842 0 : }
1843 139 : if( FD_UNLIKELY( !fd_pubkey_eq( &account_state->inner.buffer.authority_address, present_authority_key ) ) ) {
1844 1 : fd_log_collector_msg_literal( instr_ctx, "Incorrect buffer authority provided" );
1845 1 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1846 1 : }
1847 138 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 1UL, &err ) ) ) {
1848 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1849 3 : if( FD_UNLIKELY( !!err ) ) return err;
1850 3 : fd_log_collector_msg_literal( instr_ctx, "Buffer authority did not sign" );
1851 3 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1852 3 : }
1853 :
1854 : /* copy in the authority public key into the authority address.
1855 : https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L926-L928 */
1856 135 : account_state->inner.buffer.has_authority_address = !!new_authority;
1857 135 : if( new_authority ) {
1858 135 : account_state->inner.buffer.authority_address = *new_authority;
1859 135 : }
1860 :
1861 135 : err = fd_bpf_loader_v3_program_set_state( &account, account_state );
1862 135 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1863 0 : return err;
1864 0 : }
1865 149 : } else if( account_state->discriminant==FD_BPF_STATE_PROGRAM_DATA ) {
1866 149 : if( FD_UNLIKELY( !account_state->inner.program_data.has_upgrade_authority_address ) ) {
1867 0 : fd_log_collector_msg_literal( instr_ctx, "Program not upgradeable" );
1868 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
1869 0 : }
1870 149 : if( FD_UNLIKELY( !fd_pubkey_eq( &account_state->inner.program_data.upgrade_authority_address, present_authority_key ) ) ) {
1871 2 : fd_log_collector_msg_literal( instr_ctx, "Incorrect upgrade authority provided" );
1872 2 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1873 2 : }
1874 147 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 1UL, &err ) ) ) {
1875 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1876 8 : if( FD_UNLIKELY( !!err ) ) return err;
1877 8 : fd_log_collector_msg_literal( instr_ctx, "Upgrade authority did not sign" );
1878 8 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1879 8 : }
1880 :
1881 : /* SIMD-0500: forbid finalize (SetAuthority None) on programs
1882 : that are not at least SBPF v3. */
1883 139 : if( !new_authority ) {
1884 12 : int rc = fd_bpf_loader_finalize_v3_check(
1885 12 : FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, disable_sbpf_v0_v1_v2_deployment ),
1886 12 : fd_borrowed_account_get_data ( &account ),
1887 12 : fd_borrowed_account_get_data_len( &account ) );
1888 12 : if( FD_UNLIKELY( rc!=FD_EXECUTOR_INSTR_SUCCESS ) ) return rc;
1889 12 : }
1890 :
1891 : /* copy in the authority public key into the upgrade authority address.
1892 : https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L946-L949 */
1893 139 : account_state->inner.program_data.has_upgrade_authority_address = !!new_authority;
1894 139 : if( new_authority ) {
1895 127 : account_state->inner.program_data.upgrade_authority_address = *new_authority;
1896 127 : }
1897 :
1898 139 : err = fd_bpf_loader_v3_program_set_state( &account, account_state );
1899 139 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1900 0 : return err;
1901 0 : }
1902 139 : } else {
1903 0 : fd_log_collector_msg_literal( instr_ctx, "Account does not support authorities" );
1904 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
1905 0 : }
1906 :
1907 : /* Max msg_sz: 16 - 2 + 45 = 59 < 127 => we can use printf */
1908 274 : if( new_authority ) {
1909 262 : FD_BASE58_ENCODE_32_BYTES( new_authority->uc, new_authority_b58 );
1910 262 : fd_log_collector_printf_dangerous_max_127( instr_ctx, "New authority Some(%s)", new_authority_b58 );
1911 262 : } else {
1912 12 : fd_log_collector_printf_dangerous_max_127( instr_ctx, "New authority None" );
1913 12 : }
1914 :
1915 : /* implicit drop of account */
1916 :
1917 274 : break;
1918 301 : }
1919 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L958-L1030 */
1920 295 : case FD_BPF_INSTR_SET_AUTHORITY_CHECKED: {
1921 295 : int err;
1922 295 : if( !FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, enable_bpf_loader_set_authority_checked_ix ) ) {
1923 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
1924 0 : }
1925 :
1926 295 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 3U ) ) ) {
1927 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
1928 0 : }
1929 :
1930 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L968-L969 */
1931 295 : fd_guarded_borrowed_account_t account = {0};
1932 295 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &account );
1933 :
1934 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L970-L975 */
1935 295 : fd_pubkey_t const * present_authority_key = NULL;
1936 295 : fd_pubkey_t const * new_authority_key = NULL;
1937 :
1938 295 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 1UL, &present_authority_key );
1939 295 : if( FD_UNLIKELY( err ) ) return err;
1940 :
1941 295 : err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, 2UL, &new_authority_key );
1942 295 : if( FD_UNLIKELY( err ) ) return err;
1943 :
1944 295 : fd_bpf_state_t account_state[1];
1945 295 : err = fd_bpf_loader_program_get_state( account.acc, account_state );
1946 295 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1947 7 : return err;
1948 7 : }
1949 :
1950 288 : if( account_state->discriminant==FD_BPF_STATE_BUFFER ) {
1951 135 : if( FD_UNLIKELY( !account_state->inner.buffer.has_authority_address ) ) {
1952 0 : fd_log_collector_msg_literal( instr_ctx, "Buffer is immutable" );
1953 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
1954 0 : }
1955 135 : if( FD_UNLIKELY( !fd_pubkey_eq( &account_state->inner.buffer.authority_address, present_authority_key ) ) ) {
1956 3 : fd_log_collector_msg_literal( instr_ctx, "Incorrect buffer authority provided" );
1957 3 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1958 3 : }
1959 132 : int instr_err_code = 0;
1960 132 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 1UL, &instr_err_code ) ) ) {
1961 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1962 3 : if( FD_UNLIKELY( !!instr_err_code ) ) return instr_err_code;
1963 3 : fd_log_collector_msg_literal( instr_ctx, "Buffer authority did not sign" );
1964 3 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1965 3 : }
1966 129 : instr_err_code = 0;
1967 129 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 2UL, &instr_err_code ) ) ) {
1968 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1969 0 : if( FD_UNLIKELY( !!instr_err_code ) ) return instr_err_code;
1970 0 : fd_log_collector_msg_literal( instr_ctx, "New authority did not sign" );
1971 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1972 0 : }
1973 129 : account_state->inner.buffer.has_authority_address = 1;
1974 129 : account_state->inner.buffer.authority_address = *new_authority_key;
1975 129 : err = fd_bpf_loader_v3_program_set_state( &account, account_state );
1976 129 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
1977 0 : return err;
1978 0 : }
1979 153 : } else if( account_state->discriminant==FD_BPF_STATE_PROGRAM_DATA ) {
1980 153 : if( FD_UNLIKELY( !account_state->inner.program_data.has_upgrade_authority_address ) ) {
1981 0 : fd_log_collector_msg_literal( instr_ctx, "Program not upgradeable" );
1982 0 : return FD_EXECUTOR_INSTR_ERR_ACC_IMMUTABLE;
1983 0 : }
1984 153 : if( FD_UNLIKELY( !fd_pubkey_eq( &account_state->inner.program_data.upgrade_authority_address, present_authority_key ) ) ) {
1985 1 : fd_log_collector_msg_literal( instr_ctx, "Incorrect upgrade authority provided" );
1986 1 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_AUTHORITY;
1987 1 : }
1988 152 : int instr_err_code = 0;
1989 152 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 1UL, &instr_err_code ) ) ) {
1990 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1991 3 : if( FD_UNLIKELY( !!instr_err_code ) ) return instr_err_code;
1992 3 : fd_log_collector_msg_literal( instr_ctx, "Upgrade authority did not sign" );
1993 3 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
1994 3 : }
1995 149 : instr_err_code = 0;
1996 149 : if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( instr_ctx->instr, 2UL, &instr_err_code ) ) ) {
1997 : /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
1998 1 : if( FD_UNLIKELY( !!instr_err_code ) ) return instr_err_code;
1999 1 : fd_log_collector_msg_literal( instr_ctx, "New authority did not sign" );
2000 1 : return FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
2001 1 : }
2002 148 : account_state->inner.program_data.has_upgrade_authority_address = 1;
2003 148 : account_state->inner.program_data.upgrade_authority_address = *new_authority_key;
2004 148 : err = fd_bpf_loader_v3_program_set_state( &account, account_state );
2005 148 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
2006 0 : return err;
2007 0 : }
2008 148 : } else {
2009 0 : fd_log_collector_msg_literal( instr_ctx, "Account does not support authorities" );
2010 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2011 0 : }
2012 :
2013 : /* Max msg_sz: 16 - 2 + 45 = 59 < 127 => we can use printf */
2014 277 : FD_BASE58_ENCODE_32_BYTES( new_authority_key->uc, new_authority_b58 );
2015 277 : fd_log_collector_printf_dangerous_max_127( instr_ctx, "New authority %s", new_authority_b58 );
2016 :
2017 : /* implicit drop of account */
2018 :
2019 277 : break;
2020 288 : }
2021 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L1031-L1134 */
2022 334 : case FD_BPF_INSTR_CLOSE: {
2023 334 : int err;
2024 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L1032-L1046 */
2025 334 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 2U ) ) ) {
2026 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2027 0 : }
2028 :
2029 : /* It's safe to directly access the instruction accounts because we already checked for two
2030 : instruction accounts previously.
2031 : https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L1034-L1035 */
2032 334 : if( FD_UNLIKELY( instr_ctx->instr->accounts[ 0UL ].index_in_transaction ==
2033 334 : instr_ctx->instr->accounts[ 1UL ].index_in_transaction ) ) {
2034 0 : fd_log_collector_msg_literal( instr_ctx, "Recipient is the same as the account being closed" );
2035 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2036 0 : }
2037 :
2038 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1043-L1044 */
2039 334 : fd_guarded_borrowed_account_t close_account = {0};
2040 334 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 0UL, &close_account );
2041 :
2042 334 : fd_pubkey_t const * close_key = (fd_pubkey_t*)close_account.acc->pubkey;
2043 334 : fd_bpf_state_t close_account_state[1];
2044 334 : err = fd_bpf_loader_program_get_state( close_account.acc, close_account_state );
2045 334 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
2046 2 : return err;
2047 2 : }
2048 :
2049 : /* https://github.com/anza-xyz/agave/blob/v4.4.0-beta.0/programs/bpf_loader/src/lib.rs#L719-L728 */
2050 332 : if( close_account_state->discriminant==FD_BPF_STATE_UNINITIALIZED ) {
2051 :
2052 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1050-L1051 */
2053 86 : fd_guarded_borrowed_account_t recipient_account = {0};
2054 86 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( instr_ctx, 1UL, &recipient_account );
2055 :
2056 86 : err = fd_borrowed_account_checked_add_lamports( &recipient_account, fd_borrowed_account_get_lamports( &close_account ) );
2057 86 : if( FD_UNLIKELY( err ) ) {
2058 1 : return err;
2059 1 : }
2060 85 : err = fd_borrowed_account_set_lamports( &close_account, 0UL );
2061 85 : if( FD_UNLIKELY( err ) ) {
2062 1 : return err;
2063 1 : }
2064 84 : err = fd_borrowed_account_set_data_length( &close_account, SIZE_OF_UNINITIALIZED );
2065 84 : if( FD_UNLIKELY( err ) ) {
2066 0 : return err;
2067 0 : }
2068 : /* Max msg_sz: 23 - 2 + 45 = 66 < 127 => we can use printf */
2069 84 : FD_BASE58_ENCODE_32_BYTES( close_key->uc, close_key_b58 );
2070 84 : fd_log_collector_printf_dangerous_max_127( instr_ctx, "Closed Uninitialized %s", close_key_b58 );
2071 :
2072 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L1057-L1068 */
2073 246 : } else if( close_account_state->discriminant==FD_BPF_STATE_BUFFER ) {
2074 :
2075 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1059 */
2076 158 : fd_borrowed_account_drop( &close_account );
2077 :
2078 158 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 3U ) ) ) {
2079 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2080 0 : }
2081 :
2082 158 : fd_bpf_state_buffer_t * state_buf = &close_account_state->inner.buffer;
2083 158 : err = common_close_account(
2084 158 : state_buf->has_authority_address ? &state_buf->authority_address : NULL,
2085 158 : instr_ctx,
2086 158 : close_account_state );
2087 158 : if( FD_UNLIKELY( err ) ) return err;
2088 :
2089 : /* Max msg_sz: 16 - 2 + 45 = 63 < 127 => we can use printf */
2090 138 : FD_BASE58_ENCODE_32_BYTES( close_key->uc, close_key_b58 );
2091 138 : fd_log_collector_printf_dangerous_max_127( instr_ctx, "Closed Buffer %s", close_key_b58 );
2092 :
2093 : /* https://github.com/anza-xyz/agave/blob/574bae8fefc0ed256b55340b9d87b7689bcdf222/programs/bpf_loader/src/lib.rs#L1069-L1129 */
2094 138 : } else if( close_account_state->discriminant==FD_BPF_STATE_PROGRAM_DATA ) {
2095 88 : int err;
2096 88 : if( FD_UNLIKELY( fd_exec_instr_ctx_check_num_insn_accounts( instr_ctx, 4U ) ) ) {
2097 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
2098 0 : }
2099 :
2100 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1074 */
2101 88 : fd_borrowed_account_drop( &close_account );
2102 :
2103 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1075-L1076 */
2104 88 : fd_guarded_borrowed_account_t program_account = {0};
2105 88 : FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK(instr_ctx, 3UL, &program_account );
2106 :
2107 88 : if( FD_UNLIKELY( !fd_borrowed_account_is_writable( &program_account ) ) ) {
2108 0 : fd_log_collector_msg_literal( instr_ctx, "Program account is not writable" );
2109 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2110 0 : }
2111 88 : if( FD_UNLIKELY( memcmp( fd_borrowed_account_get_owner( &program_account ), program_id, sizeof(fd_pubkey_t) ) ) ) {
2112 1 : fd_log_collector_msg_literal( instr_ctx, "Program account not owned by loader" );
2113 1 : return FD_EXECUTOR_INSTR_ERR_INCORRECT_PROGRAM_ID;
2114 1 : }
2115 87 : fd_sol_sysvar_clock_t clock_;
2116 87 : fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( instr_ctx->sysvar_cache, &clock_ );
2117 87 : if( FD_UNLIKELY( !clock ) ) {
2118 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
2119 0 : }
2120 87 : if( FD_UNLIKELY( clock->slot==close_account_state->inner.program_data.slot ) ) {
2121 75 : fd_log_collector_msg_literal( instr_ctx,"Program was deployed in this block already" );
2122 75 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2123 75 : }
2124 :
2125 12 : fd_bpf_state_t program_state[1];
2126 12 : err = fd_bpf_loader_program_get_state( program_account.acc, program_state );
2127 12 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
2128 0 : return err;
2129 0 : }
2130 :
2131 12 : if( program_state->discriminant==FD_BPF_STATE_PROGRAM ) {
2132 12 : if( FD_UNLIKELY( memcmp( &program_state->inner.program.programdata_address, close_key, sizeof(fd_pubkey_t) ) ) ) {
2133 9 : fd_log_collector_msg_literal( instr_ctx,"ProgramData account does not match ProgramData account" );
2134 9 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2135 9 : }
2136 :
2137 : /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/lib.rs#L1105 */
2138 3 : fd_borrowed_account_drop( &program_account );
2139 :
2140 3 : fd_bpf_state_program_data_t * pd = &close_account_state->inner.program_data;
2141 3 : err = common_close_account(
2142 3 : pd->has_upgrade_authority_address ? &pd->upgrade_authority_address : NULL,
2143 3 : instr_ctx,
2144 3 : close_account_state );
2145 3 : if( FD_UNLIKELY( err ) ) return err;
2146 :
2147 : /* Mark the closed programdata pd_write via the txn account (no
2148 : borrow is live here, and buffer/uninitialized closes going
2149 : through common_close_account must not set the bit). */
2150 3 : ushort pd_idx_in_txn;
2151 3 : err = fd_exec_instr_ctx_get_index_of_instr_account_in_transaction( instr_ctx, 0U, &pd_idx_in_txn );
2152 3 : if( FD_UNLIKELY( err ) ) return err; /* unreachable */
2153 3 : instr_ctx->txn_out->accounts.account[ pd_idx_in_txn ]->pd_write = 1;
2154 :
2155 : /* Execution validates ProgramData and rejects a parent copy
2156 : if pd_write_this_slot is set, before consulting the program
2157 : cache. */
2158 :
2159 3 : } else {
2160 0 : fd_log_collector_msg_literal( instr_ctx, "Invalid Program account" );
2161 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2162 0 : }
2163 :
2164 : /* Max msg_sz: 17 - 2 + 45 = 60 < 127 => we can use printf */
2165 3 : FD_BASE58_ENCODE_32_BYTES( program_account.acc->pubkey, program_account_b58 );
2166 3 : fd_log_collector_printf_dangerous_max_127( instr_ctx, "Closed Program %s", program_account_b58 );
2167 :
2168 : /* program account is dropped when it goes out of scope */
2169 3 : } else {
2170 0 : fd_log_collector_msg_literal( instr_ctx, "Account does not support closing" );
2171 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
2172 0 : }
2173 :
2174 : /* implicit drop of close account */
2175 225 : break;
2176 332 : }
2177 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1158-L1170 */
2178 381 : case FD_BPF_INSTR_EXTEND_PROGRAM: {
2179 381 : err = common_extend_program( instr_ctx, instruction->inner.extend_program.additional_bytes, 0 );
2180 381 : if( FD_UNLIKELY( err ) ) {
2181 381 : return err;
2182 381 : }
2183 :
2184 0 : break;
2185 381 : }
2186 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/lib.rs#L1171-L1179 */
2187 299 : case FD_BPF_INSTR_EXTEND_PROGRAM_CHECKED: {
2188 299 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2189 381 : }
2190 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/bpf_loader/src/lib.rs#L1338-L1508 */
2191 613 : case FD_BPF_INSTR_MIGRATE: {
2192 : /* https://github.com/anza-xyz/agave/blob/v2.2.6/programs/bpf_loader/src/lib.rs#L1339-L1344 */
2193 613 : if( FD_UNLIKELY( !FD_FEATURE_ACTIVE_BANK( instr_ctx->bank, enable_loader_v4 ) ) ) {
2194 613 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2195 613 : }
2196 :
2197 : /* TODO: Implement me */
2198 0 : FD_LOG_WARNING(( "migrate instruction is not implemented" ));
2199 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
2200 613 : }
2201 0 : default: {
2202 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
2203 613 : }
2204 3653 : }
2205 1225 : return FD_EXECUTOR_INSTR_SUCCESS;
2206 3653 : }
2207 :
2208 : /* process_instruction_inner() */
2209 : /* https://github.com/anza-xyz/agave/blob/77daab497df191ef485a7ad36ed291c1874596e5/programs/bpf_loader/src/lib.rs#L394-L564 */
2210 : int
2211 9973 : fd_bpf_loader_program_execute( fd_exec_instr_ctx_t * ctx ) {
2212 : /* https://github.com/anza-xyz/agave/blob/77daab497df191ef485a7ad36ed291c1874596e5/programs/bpf_loader/src/lib.rs#L491-L529 */
2213 :
2214 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L403-L404 */
2215 9973 : fd_guarded_borrowed_account_t program_account = {0};
2216 9973 : int err = fd_exec_instr_ctx_try_borrow_last_program_account( ctx, &program_account );
2217 9973 : if( FD_UNLIKELY( err ) ) {
2218 0 : return err;
2219 0 : }
2220 :
2221 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/programs/bpf_loader/src/lib.rs#L409 */
2222 9973 : fd_pubkey_t const * program_id = NULL;
2223 9973 : err = fd_exec_instr_ctx_get_last_program_key( ctx, &program_id );
2224 9973 : if( FD_UNLIKELY( err ) ) {
2225 0 : return err;
2226 0 : }
2227 :
2228 : /* Program management instruction */
2229 9973 : if( FD_UNLIKELY( !memcmp( &fd_solana_native_loader_id, fd_borrowed_account_get_owner( &program_account ), sizeof(fd_pubkey_t) ) ) ) {
2230 : /* https://github.com/anza-xyz/agave/blob/v2.2.3/programs/bpf_loader/src/lib.rs#L416 */
2231 3735 : fd_borrowed_account_drop( &program_account );
2232 :
2233 3735 : if( FD_UNLIKELY( !memcmp( &fd_solana_bpf_loader_upgradeable_program_id, program_id, sizeof(fd_pubkey_t) ) ) ) {
2234 3710 : FD_EXEC_CU_UPDATE( ctx, UPGRADEABLE_LOADER_COMPUTE_UNITS );
2235 3704 : return process_loader_upgradeable_instruction( ctx );
2236 3710 : } else if( FD_UNLIKELY( !memcmp( &fd_solana_bpf_loader_program_id, program_id, sizeof(fd_pubkey_t) ) ) ) {
2237 26 : FD_EXEC_CU_UPDATE( ctx, DEFAULT_LOADER_COMPUTE_UNITS );
2238 26 : fd_log_collector_msg_literal( ctx, "BPF loader management instructions are no longer supported" );
2239 26 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2240 >1844*10^16 : } else if( FD_UNLIKELY( !memcmp( &fd_solana_bpf_loader_deprecated_program_id, program_id, sizeof(fd_pubkey_t) ) ) ) {
2241 2 : FD_EXEC_CU_UPDATE( ctx, DEPRECATED_LOADER_COMPUTE_UNITS );
2242 2 : fd_log_collector_msg_literal( ctx, "Deprecated loader is no longer supported" );
2243 2 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2244 >1844*10^16 : } else {
2245 >1844*10^16 : fd_log_collector_msg_literal( ctx, "Invalid BPF loader id" );
2246 >1844*10^16 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2247 >1844*10^16 : }
2248 3735 : }
2249 :
2250 : /* https://github.com/anza-xyz/agave/blob/77daab497df191ef485a7ad36ed291c1874596e5/programs/bpf_loader/src/lib.rs#L551-L563 */
2251 : /* The Agave client stores a loaded program type state in its implementation
2252 : of the loaded program cache. It checks to see if an account is able to be
2253 : executed. It is possible for a program to be in the DelayVisibility state or
2254 : Closed state but it won't be reflected in the Firedancer cache. Program
2255 : accounts that are in this state should exit with an unsupported program
2256 : id error. For programs that are recently deployed or upgraded, they should not
2257 : be allowed to be executed for the remainder of the slot. For closed
2258 : accounts, they're uninitialized and shouldn't be executed as well.
2259 :
2260 : For the former case the slot that the
2261 : program was last updated in is in the program data account.
2262 : This means that if the slot in the program data account is greater than or
2263 : equal to the current execution slot, then the account is in a
2264 : 'LoadedProgramType::DelayVisiblity' state.
2265 :
2266 : The latter case as described above is a tombstone account which is in a Closed
2267 : state. This occurs when a program data account is closed. However, our cache
2268 : does not track this. Instead, this can be checked for by seeing if the program
2269 : account's respective program data account is uninitialized. This should only
2270 : happen when the account is closed.
2271 :
2272 : The programdata account here is fetched via fd_runtime_get_executable_account,
2273 : which may return a copy read from the parent fork. A parent copy's
2274 : program_data.slot cannot reflect a current-slot deploy/upgrade/extend/close,
2275 : so the DelayVisibility gate below is provenance-aware: current-fork copies keep
2276 : the slot comparison, parent-fork copies gate on the accdb pd_write probe
2277 : result instead. */
2278 :
2279 6238 : uchar is_deprecated = !memcmp( program_account.acc->owner, &fd_solana_bpf_loader_deprecated_program_id, sizeof(fd_pubkey_t) );
2280 :
2281 6238 : fd_acc_t const * progdata_ro = program_account.acc;
2282 6238 : if( !memcmp( program_account.acc->owner, &fd_solana_bpf_loader_upgradeable_program_id, sizeof(fd_pubkey_t) ) ) {
2283 2400 : fd_bpf_state_t program_account_state[1];
2284 2400 : err = fd_bpf_loader_program_get_state( program_account.acc, program_account_state );
2285 2400 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
2286 1 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2287 1 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2288 1 : }
2289 :
2290 : /* https://github.com/anza-xyz/agave/blob/v2.0.9/svm/src/program_loader.rs#L96-L98
2291 : Program account and program data account discriminants get checked when loading in program accounts
2292 : into the program cache. If the discriminants are incorrect, the program is marked as closed. */
2293 2399 : if( FD_UNLIKELY( program_account_state->discriminant!=FD_BPF_STATE_PROGRAM ) ) {
2294 : /* https://github.com/anza-xyz/agave/tree/v3.0.5/programs/bpf_loader/src/lib.rs#L424-L433
2295 : Agave's program cache will add any non-migrating built-ins as built-in
2296 : accounts, even though they might be owned by the BPF loader. In these
2297 : cases, Agave does not log this message. Meanwhile, non-migrating
2298 : built-in programs do not use the BPF loader, by definition. */
2299 204 : if( !fd_is_non_migrating_builtin_program( program_id ) ) {
2300 203 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2301 203 : }
2302 204 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2303 204 : }
2304 :
2305 2195 : fd_pubkey_t * programdata_pubkey = &program_account_state->inner.program.programdata_address;
2306 2195 : int from_parent_copy = 0;
2307 2195 : int pd_write_this_slot = 0;
2308 2195 : progdata_ro = fd_runtime_get_executable_account( ctx->txn_out, programdata_pubkey, &from_parent_copy, &pd_write_this_slot );
2309 2195 : if( FD_UNLIKELY( !progdata_ro ) ) {
2310 1201 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2311 1201 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2312 1201 : }
2313 :
2314 994 : if( FD_UNLIKELY( progdata_ro->data_len<PROGRAMDATA_METADATA_SIZE ) ) {
2315 19 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2316 19 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2317 19 : }
2318 :
2319 975 : fd_bpf_state_t program_data_account_state[1];
2320 975 : err = fd_bpf_loader_program_get_state( progdata_ro, program_data_account_state );
2321 975 : if( FD_UNLIKELY( err!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
2322 6 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2323 6 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2324 6 : }
2325 :
2326 : /* https://github.com/anza-xyz/agave/blob/v2.0.9/svm/src/program_loader.rs#L100-L104
2327 : Same as above comment. Program data discriminant must be set correctly. */
2328 969 : if( FD_UNLIKELY( program_data_account_state->discriminant!=FD_BPF_STATE_PROGRAM_DATA ) ) {
2329 : /* The account is closed. */
2330 0 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2331 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2332 0 : }
2333 :
2334 969 : ulong program_data_slot = program_data_account_state->inner.program_data.slot;
2335 969 : if( FD_LIKELY( !from_parent_copy ) ) {
2336 969 : if( FD_UNLIKELY( program_data_slot>=ctx->bank->f.slot ) ) {
2337 : /* The account was likely just deployed or upgraded. Corresponds to
2338 : 'LoadedProgramType::DelayVisibility' */
2339 1 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2340 1 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2341 1 : }
2342 969 : } else {
2343 0 : if( FD_UNLIKELY( pd_write_this_slot ) ) {
2344 0 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2345 0 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2346 0 : }
2347 0 : }
2348 969 : }
2349 :
2350 4806 : fd_prog_load_env_t load_env[1]; fd_prog_load_env_from_bank( load_env, ctx->bank );
2351 4806 : fd_progcache_t * progcache = ctx->runtime->progcache;
2352 4806 : fd_progcache_rec_t * cache_entry =
2353 4806 : fd_progcache_pull( progcache, ctx->bank->progcache_fork_id, program_id, load_env, progdata_ro );
2354 4806 : if( FD_UNLIKELY( !cache_entry ) ) {
2355 1 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2356 1 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2357 1 : }
2358 :
2359 : /* The program may be in the cache but could have failed verification in the current epoch. */
2360 4805 : if( FD_UNLIKELY( !cache_entry->data_gaddr ) ) {
2361 576 : fd_progcache_rec_close( progcache, cache_entry );
2362 576 : fd_log_collector_msg_literal( ctx, "Program is not deployed" );
2363 576 : return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
2364 576 : }
2365 :
2366 : /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/bpf_loader/src/lib.rs#L446 */
2367 4229 : fd_borrowed_account_drop( &program_account );
2368 :
2369 4229 : int exec_err = fd_bpf_execute( ctx, cache_entry, is_deprecated );
2370 4229 : fd_progcache_rec_close( progcache, cache_entry );
2371 4229 : return exec_err;
2372 4805 : }
|