Line data Source code
1 : #include "fd_accdb_svm.h"
2 : #include "sysvar/fd_sysvar_rent.h"
3 : #include "program/fd_bpf_loader_program.h"
4 : #include "program/fd_builtin_programs.h"
5 : #include "program/fd_program_util.h"
6 : #include "fd_runtime_stack.h"
7 : #include "fd_pubkey_utils.h"
8 : #include "fd_system_ids.h"
9 : #include "../../ballet/sha256/fd_sha256.h"
10 : #include "../../ballet/sbpf/fd_sbpf_loader.h"
11 : #include "../progcache/fd_prog_load.h"
12 : #include "../vm/fd_vm.h"
13 :
14 : static fd_pubkey_t
15 0 : get_program_data_address( fd_pubkey_t const * program_addr ) {
16 0 : uchar const * seed = program_addr->uc;
17 0 : ulong seed_sz = 32UL;
18 0 : fd_pubkey_t out;
19 0 : uint custom_err;
20 0 : uchar out_bump_seed;
21 0 : int err = fd_pubkey_find_program_address( &fd_solana_bpf_loader_upgradeable_program_id, 1UL, &seed, &seed_sz, &out, &out_bump_seed, &custom_err );
22 0 : if( FD_UNLIKELY( err ) ) {
23 : /* https://github.com/anza-xyz/solana-sdk/blob/address%40v2.1.0/address/src/syscalls.rs#L277-L279 */
24 0 : FD_LOG_ERR(( "Unable to find a viable program address bump seed" ));
25 0 : }
26 0 : return out;
27 0 : }
28 :
29 : fd_tmp_account_t *
30 : tmp_account_new( fd_tmp_account_t * acc,
31 0 : ulong acc_sz ) {
32 0 : acc->data_sz = acc_sz;
33 0 : fd_memset( acc->data, 0, acc_sz );
34 0 : acc->lamports = 0UL;
35 0 : acc->executable = 0;
36 0 : fd_memset( acc->owner.uc, 0, 32UL );
37 0 : fd_memset( acc->pubkey.uc, 0, 32UL );
38 0 : return acc;
39 0 : }
40 :
41 : fd_tmp_account_t *
42 : tmp_account_read( fd_tmp_account_t * acc,
43 : fd_accdb_t * accdb,
44 : fd_accdb_fork_id_t fork_id,
45 0 : fd_pubkey_t const * addr ) {
46 0 : fd_acc_t db_acc = fd_accdb_read_one( accdb, fork_id, addr->uc );
47 0 : if( FD_UNLIKELY( !db_acc.lamports ) ) {
48 0 : fd_accdb_unread_one( accdb, &db_acc );
49 0 : return NULL;
50 0 : }
51 :
52 0 : acc->lamports = db_acc.lamports;
53 0 : fd_memcpy( acc->owner.uc, db_acc.owner, 32UL );
54 0 : acc->executable = db_acc.executable;
55 0 : fd_memcpy( acc->data, db_acc.data, db_acc.data_len );
56 0 : acc->data_sz = db_acc.data_len;
57 0 : acc->pubkey = *addr;
58 :
59 0 : fd_accdb_unread_one( accdb, &db_acc );
60 0 : return acc;
61 0 : }
62 :
63 : void
64 : tmp_account_store( fd_bank_t * bank,
65 : fd_accdb_t * accdb,
66 : fd_tmp_account_t * acc,
67 0 : fd_capture_ctx_t * capture_ctx ) {
68 0 : if( FD_UNLIKELY( fd_pubkey_eq( &acc->pubkey, &fd_solana_system_program_id ) ) ) FD_LOG_ERR(( "attempted to write to the system program account" ));
69 :
70 0 : fd_accdb_svm_update_t update[1];
71 0 : fd_acc_t db_acc = fd_accdb_svm_open_rw( bank, accdb, update, &acc->pubkey, 1 );
72 :
73 0 : db_acc.executable = acc->executable;
74 0 : fd_memcpy( db_acc.owner, acc->owner.uc, 32UL );
75 0 : db_acc.lamports = acc->lamports;
76 0 : fd_memcpy( db_acc.data, acc->data, acc->data_sz );
77 0 : db_acc.data_len = acc->data_sz;
78 :
79 0 : fd_accdb_svm_close_rw( bank, accdb, capture_ctx, &db_acc, update );
80 0 : }
81 :
82 : /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/target_core_bpf.rs#L12 */
83 :
84 : struct target_core_bpf {
85 : fd_pubkey_t program_address;
86 : fd_tmp_account_t * program_data_account;
87 : fd_pubkey_t upgrade_authority_address;
88 : uint has_upgrade_authority_address : 1;
89 : };
90 :
91 : typedef struct target_core_bpf target_core_bpf_t;
92 :
93 : /* https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L19 */
94 :
95 : struct target_builtin {
96 : fd_tmp_account_t * program_account;
97 : fd_pubkey_t program_data_address;
98 : ulong program_data_account_lamports;
99 : };
100 :
101 : typedef struct target_builtin target_builtin_t;
102 :
103 : /* https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L25-L91 */
104 :
105 : target_builtin_t *
106 : target_builtin_new_checked( target_builtin_t * target_builtin,
107 : fd_pubkey_t const * program_address,
108 : int migration_target,
109 : int relax_programdata_account_check_migration,
110 : fd_accdb_t * accdb,
111 : fd_accdb_fork_id_t fork_id,
112 0 : fd_runtime_stack_t * runtime_stack ) {
113 :
114 : /* https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L31-L53 */
115 :
116 0 : fd_tmp_account_t * program_account = &runtime_stack->bpf_migration.program_account;
117 0 : switch( migration_target ) {
118 0 : case FD_CORE_BPF_MIGRATION_TARGET_BUILTIN:
119 0 : if( FD_UNLIKELY( !tmp_account_read( program_account, accdb, fork_id, program_address ) ) ) {
120 : /* CoreBpfMigrationError::AccountNotFound(*program_address) */
121 0 : return NULL;
122 0 : }
123 0 : if( FD_UNLIKELY( 0!=memcmp( program_account->owner.uc, &fd_solana_native_loader_id, 32 ) ) ) {
124 : /* CoreBpfMigrationError::IncorrectOwner(*program_address) */
125 0 : return NULL;
126 0 : }
127 0 : break;
128 0 : case FD_CORE_BPF_MIGRATION_TARGET_STATELESS: {
129 : /* CoreBpfMigrationError::AccountAlreadyExists(*program_address) */
130 0 : if( FD_UNLIKELY( fd_accdb_exists( accdb, fork_id, program_address->uc ) ) ) return NULL;
131 0 : break;
132 0 : }
133 0 : default:
134 0 : FD_LOG_ERR(( "invalid migration_target %d", migration_target ));
135 0 : }
136 :
137 : /* https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L55 */
138 :
139 0 : fd_pubkey_t program_data_address = get_program_data_address( program_address );
140 :
141 : /* https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L57-L82 */
142 :
143 0 : ulong program_data_account_lamports = 0UL;
144 0 : do {
145 : /* Program data account should not exist */
146 0 : fd_acc_t progdata = fd_accdb_read_one( accdb, fork_id, program_data_address.uc );
147 0 : int progdata_exists = !!progdata.lamports;
148 0 : if( FD_UNLIKELY( !progdata_exists ) ) fd_accdb_unread_one( accdb, &progdata );
149 :
150 : /* SIMD-0444: relax_programdata_account_check_migration
151 : https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L57-L70 */
152 0 : if( relax_programdata_account_check_migration ) {
153 : /* The program data account should not exist, but a system
154 : account with funded lamports is acceptable. */
155 0 : if( FD_UNLIKELY( progdata_exists ) ) {
156 0 : if( FD_UNLIKELY( !fd_pubkey_eq( (fd_pubkey_t*)progdata.owner, &fd_solana_system_program_id ) ) ) {
157 : /* CoreBpfMigrationError::ProgramHasDataAccount(*program_address) */
158 0 : fd_accdb_unread_one( accdb, &progdata );
159 0 : return NULL;
160 0 : } else {
161 0 : program_data_account_lamports = progdata.lamports;
162 0 : fd_accdb_unread_one( accdb, &progdata );
163 0 : }
164 0 : }
165 0 : } else {
166 : /* If relax_programdata_account_check_migration is not enabled,
167 : we do not allow the program data account to exist at all. */
168 0 : if( FD_UNLIKELY( progdata_exists ) ) {
169 : /* CoreBpfMigrationError::AccountAlreadyExists(*program_address) */
170 0 : fd_accdb_unread_one( accdb, &progdata );
171 0 : return NULL;
172 0 : }
173 0 : }
174 0 : } while(0);
175 :
176 : /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L63-L67 */
177 :
178 0 : *target_builtin = (target_builtin_t) {
179 0 : .program_account = program_account,
180 0 : .program_data_address = program_data_address,
181 0 : .program_data_account_lamports = program_data_account_lamports
182 0 : };
183 0 : return target_builtin;
184 0 : }
185 :
186 : /* https://github.com/anza-xyz/agave/blob/v3.1.7/runtime/src/bank/builtins/core_bpf_migration/target_core_bpf.rs#L26-L93 */
187 : static target_core_bpf_t *
188 : target_core_bpf_new_checked( target_core_bpf_t * target_core_bpf,
189 : fd_pubkey_t const * program_address,
190 : fd_accdb_t * accdb,
191 : fd_accdb_fork_id_t fork_id,
192 0 : fd_runtime_stack_t * runtime_stack ) {
193 0 : fd_pubkey_t program_data_address = get_program_data_address( program_address );
194 :
195 : /* The program account should exist */
196 0 : fd_tmp_account_t * program_account = &runtime_stack->bpf_migration.program_account;
197 0 : if( FD_UNLIKELY( !tmp_account_read( program_account, accdb, fork_id, program_address ) ) ) {
198 0 : return NULL;
199 0 : }
200 :
201 : /* The program account should be owned by the upgradeable loader */
202 0 : if( FD_UNLIKELY( 0!=memcmp( &program_account->owner, &fd_solana_bpf_loader_upgradeable_program_id, sizeof(fd_pubkey_t) ) ) ) {
203 0 : return NULL;
204 0 : }
205 :
206 : /* The program account should be executable */
207 0 : if( FD_UNLIKELY( !program_account->executable ) ) {
208 0 : return NULL;
209 0 : }
210 :
211 : /* Decode and validate program account state */
212 0 : fd_bpf_state_t program_state[1];
213 0 : if( FD_UNLIKELY( FD_EXECUTOR_INSTR_SUCCESS!=fd_bpf_loader_program_get_state2( program_account->data, program_account->data_sz, program_state ) ) ) {
214 0 : return NULL;
215 0 : }
216 0 : if( FD_UNLIKELY( program_state->discriminant!=FD_BPF_STATE_PROGRAM ) ) {
217 0 : return NULL;
218 0 : }
219 0 : if( FD_UNLIKELY( 0!=memcmp( &program_state->inner.program.programdata_address, &program_data_address, sizeof(fd_pubkey_t) ) ) ) {
220 0 : return NULL;
221 0 : }
222 :
223 : /* The program data account should exist */
224 0 : fd_tmp_account_t * program_data_account = &runtime_stack->bpf_migration.new_target_program;
225 0 : if( FD_UNLIKELY( !tmp_account_read( program_data_account, accdb, fork_id, &program_data_address ) ) ) {
226 0 : return NULL;
227 0 : }
228 :
229 : /* The program data account should be owned by the upgradeable loader */
230 0 : if( FD_UNLIKELY( 0!=memcmp( &program_data_account->owner, &fd_solana_bpf_loader_upgradeable_program_id, sizeof(fd_pubkey_t) ) ) ) {
231 0 : return NULL;
232 0 : }
233 :
234 : /* Decode and validate program data account state */
235 0 : fd_bpf_state_t programdata_state[1];
236 0 : if( FD_UNLIKELY( FD_EXECUTOR_INSTR_SUCCESS!=fd_bpf_loader_program_get_state2( program_data_account->data, program_data_account->data_sz, programdata_state ) ) ) {
237 0 : return NULL;
238 0 : }
239 0 : if( FD_UNLIKELY( programdata_state->discriminant!=FD_BPF_STATE_PROGRAM_DATA ) ) {
240 0 : return NULL;
241 0 : }
242 :
243 : /* Extract upgrade authority from program data state */
244 0 : fd_pubkey_t upgrade_authority_address;
245 0 : if( programdata_state->inner.program_data.has_upgrade_authority_address ) {
246 0 : upgrade_authority_address = programdata_state->inner.program_data.upgrade_authority_address;
247 0 : } else {
248 0 : fd_memset( &upgrade_authority_address, 0, sizeof(fd_pubkey_t) );
249 0 : }
250 :
251 0 : *target_core_bpf = (target_core_bpf_t) {
252 0 : .program_address = *program_address,
253 0 : .program_data_account = program_data_account,
254 0 : .upgrade_authority_address = upgrade_authority_address,
255 0 : .has_upgrade_authority_address = (uint)!!programdata_state->inner.program_data.has_upgrade_authority_address
256 0 : };
257 0 : return target_core_bpf;
258 0 : }
259 :
260 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L25-L82
261 :
262 : Agave uses a separate TargetBpfV2 struct, but it has the
263 : same layout as target_builtin_t so we reuse that. */
264 : static target_builtin_t *
265 : target_bpf_v2_new_checked( target_builtin_t * target_bpf_v2,
266 : fd_pubkey_t const * program_address,
267 : int allow_prefunded,
268 : fd_accdb_t * accdb,
269 : fd_accdb_fork_id_t fork_id,
270 0 : fd_runtime_stack_t * runtime_stack ) {
271 :
272 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L30-L33 */
273 0 : fd_tmp_account_t * program_account = &runtime_stack->bpf_migration.program_account;
274 0 : if( FD_UNLIKELY( !tmp_account_read( program_account, accdb, fork_id, program_address ) ) ) {
275 : /* CoreBpfMigrationError::AccountNotFound(*program_address) */
276 0 : return NULL;
277 0 : }
278 :
279 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L35-L38 */
280 0 : if( FD_UNLIKELY( 0!=memcmp( &program_account->owner, &fd_solana_bpf_loader_program_id, sizeof(fd_pubkey_t) ) ) ) {
281 : /* CoreBpfMigrationError::IncorrectOwner(*program_address) */
282 0 : return NULL;
283 0 : }
284 :
285 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L40-L45 */
286 0 : if( FD_UNLIKELY( !program_account->executable ) ) {
287 : /* CoreBpfMigrationError::ProgramAccountNotExecutable(*program_address) */
288 0 : return NULL;
289 0 : }
290 :
291 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L47 */
292 0 : fd_pubkey_t program_data_address = get_program_data_address( program_address );
293 :
294 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L49-L74 */
295 0 : ulong program_data_account_lamports = 0UL;
296 0 : do {
297 0 : fd_acc_t acc = fd_accdb_read_one( accdb, fork_id, program_data_address.uc );
298 0 : int progdata_exists = !!acc.lamports;
299 0 : if( FD_UNLIKELY( !progdata_exists ) ) fd_accdb_unread_one( accdb, &acc );
300 :
301 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L49-L74 */
302 0 : if( FD_LIKELY( allow_prefunded ) ) {
303 : /* The program data account should not exist, but a system
304 : account with funded lamports is acceptable.
305 :
306 : https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L50-L61 */
307 0 : if( FD_UNLIKELY( progdata_exists ) ) {
308 0 : if( FD_UNLIKELY( !fd_pubkey_eq( (fd_pubkey_t*)acc.owner, &fd_solana_system_program_id ) ) ) {
309 : /* CoreBpfMigrationError::ProgramHasDataAccount(*program_address) */
310 0 : fd_accdb_unread_one( accdb, &acc );
311 0 : return NULL;
312 0 : } else {
313 0 : program_data_account_lamports = acc.lamports;
314 0 : fd_accdb_unread_one( accdb, &acc );
315 0 : }
316 0 : }
317 0 : } else {
318 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/target_bpf_v2.rs#L62-L74 */
319 0 : if( FD_UNLIKELY( progdata_exists ) ) {
320 : /* CoreBpfMigrationError::ProgramHasDataAccount(*program_address) */
321 0 : fd_accdb_unread_one( accdb, &acc );
322 0 : return NULL;
323 0 : }
324 0 : }
325 0 : } while(0);
326 :
327 0 : *target_bpf_v2 = (target_builtin_t) {
328 0 : .program_account = program_account,
329 0 : .program_data_address = program_data_address,
330 0 : .program_data_account_lamports = program_data_account_lamports
331 0 : };
332 0 : return target_bpf_v2;
333 0 : }
334 :
335 : /* This function contains the deployment checks that are equivalent to
336 : Agave's directly_invoke_loader_v3_deploy.
337 :
338 : There is no direct equivalent in Agave to this function, because
339 : we are not updating the program cache here. However, we do the same
340 : checks that our program cache does upon deployment.
341 :
342 : This is safe because the bpf migration code runs at the epoch
343 : boundary, before any transaction execution. The program cache
344 : automatically invalidates all programs at the start of an epoch
345 : boundary, so we do not need to explicitly update the cache during the
346 : migration.
347 :
348 : https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L120-L218 */
349 : static int
350 : fd_directly_invoke_loader_v3_deploy_checks( fd_bank_t const * bank,
351 : fd_runtime_stack_t * runtime_stack,
352 : uchar const * elf,
353 0 : ulong elf_sz ) {
354 0 : fd_features_t const * features = &bank->f.features;
355 0 : ulong slot = bank->f.slot;
356 :
357 : /* ELF verification with deploy checks enabled */
358 0 : fd_prog_versions_t versions = fd_prog_versions( features, slot );
359 0 : fd_sbpf_loader_config_t loader_config = {
360 0 : .elf_deploy_checks = 1,
361 0 : .sbpf_min_version = versions.min_sbpf_version,
362 0 : .sbpf_max_version = versions.max_sbpf_version,
363 0 : };
364 0 : fd_sbpf_elf_info_t elf_info[1];
365 0 : if( FD_UNLIKELY( fd_sbpf_elf_peek( elf_info, elf, elf_sz, &loader_config )!=FD_SBPF_ELF_SUCCESS ) ) return 1;
366 :
367 : /* Setup program (includes calldests) */
368 0 : fd_sbpf_program_t * prog = fd_sbpf_program_new(
369 0 : runtime_stack->bpf_migration.progcache_validate.sbpf_footprint,
370 0 : elf_info,
371 0 : runtime_stack->bpf_migration.progcache_validate.rodata );
372 0 : if( FD_UNLIKELY( !prog ) ) return 1;
373 :
374 0 : fd_sbpf_syscalls_t _syscalls[ FD_SBPF_SYSCALLS_SLOT_CNT ];
375 0 : fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_join( fd_sbpf_syscalls_new( _syscalls ) );
376 0 : if( FD_UNLIKELY( !syscalls ) ) return 1;
377 0 : if( FD_UNLIKELY( fd_vm_syscall_register_slot( syscalls, slot, features, /* is_deploy */ 1 )!=FD_VM_SUCCESS ) ) return 1;
378 :
379 : /* fd_sbpf_program_load checks */
380 0 : if( FD_UNLIKELY( fd_sbpf_program_load(
381 0 : prog,
382 0 : elf,
383 0 : elf_sz,
384 0 : syscalls,
385 0 : &loader_config,
386 0 : runtime_stack->bpf_migration.progcache_validate.programdata,
387 0 : sizeof(runtime_stack->bpf_migration.progcache_validate.programdata) ) ) ) return 1;
388 :
389 : /* fd_vm_validate checks */
390 0 : fd_vm_t _vm[1];
391 0 : fd_vm_t * vm = fd_vm_join( fd_vm_new( _vm ) );
392 0 : if( FD_UNLIKELY( !vm ) ) return 1;
393 0 : vm = fd_vm_init( vm,
394 0 : NULL,
395 0 : 0UL,
396 0 : 0UL,
397 0 : prog->rodata,
398 0 : prog->rodata_sz,
399 0 : prog->text,
400 0 : prog->info.text_cnt,
401 0 : prog->info.text_off,
402 0 : prog->info.text_sz,
403 0 : prog->entry_pc,
404 0 : prog->calldests,
405 0 : elf_info->sbpf_version,
406 0 : syscalls,
407 0 : NULL,
408 0 : NULL,
409 0 : NULL,
410 0 : 0U,
411 0 : NULL,
412 0 : 0,
413 0 : FD_FEATURE_ACTIVE( slot, features, account_data_direct_mapping ),
414 0 : FD_FEATURE_ACTIVE( slot, features, syscall_parameter_address_restrictions ),
415 0 : FD_FEATURE_ACTIVE( slot, features, virtual_address_space_adjustments ),
416 0 : 0,
417 0 : 0UL );
418 0 : if( FD_UNLIKELY( !vm ) ) return 1;
419 0 : if( FD_UNLIKELY( fd_vm_validate( vm )!=FD_VM_SUCCESS ) ) return 1;
420 :
421 0 : return FD_EXECUTOR_INSTR_SUCCESS;
422 0 : }
423 :
424 : /* https://github.com/anza-xyz/agave/blob/v3.1.7/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L51-L75 */
425 :
426 : static fd_tmp_account_t *
427 : source_buffer_new_checked( fd_tmp_account_t * acc,
428 : fd_accdb_t * accdb,
429 : fd_accdb_fork_id_t fork_id,
430 : fd_pubkey_t const * pubkey,
431 0 : fd_hash_t const * verified_build_hash ) {
432 :
433 0 : if( FD_UNLIKELY( !tmp_account_read( acc, accdb, fork_id, pubkey ) ) ) {
434 : /* CoreBpfMigrationError::AccountNotFound(*buffer_address) */
435 0 : return NULL;
436 0 : }
437 :
438 0 : if( FD_UNLIKELY( 0!=memcmp( acc->owner.uc, &fd_solana_bpf_loader_upgradeable_program_id, 32 ) ) ) {
439 : /* CoreBpfMigrationError::IncorrectOwner(*buffer_address) */
440 0 : return NULL;
441 0 : }
442 :
443 0 : if( acc->data_sz < BUFFER_METADATA_SIZE ) {
444 : /* CoreBpfMigrationError::InvalidBufferAccount(*buffer_address) */
445 0 : return NULL;
446 0 : }
447 :
448 0 : fd_bpf_state_t state[1];
449 0 : if( FD_UNLIKELY( fd_bpf_state_decode( state, acc->data, BUFFER_METADATA_SIZE ) ) ) {
450 0 : return NULL;
451 0 : }
452 :
453 0 : if( FD_UNLIKELY( state->discriminant!=FD_BPF_STATE_BUFFER ) ) {
454 : /* CoreBpfMigrationError::InvalidBufferAccount(*buffer_address) */
455 0 : return NULL;
456 0 : }
457 :
458 : /* https://github.com/anza-xyz/agave/blob/v3.1.7/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L61-L71 */
459 0 : if( verified_build_hash ) {
460 : /* Strip trailing zero-padding before hashing
461 : https://github.com/anza-xyz/agave/blob/v3.1.7/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L61-L63 */
462 0 : uchar const * data = (uchar const *)acc->data;
463 0 : ulong offset = BUFFER_METADATA_SIZE;
464 0 : ulong end_offset = acc->data_sz;
465 0 : while( end_offset>offset && data[end_offset-1]==0 ) end_offset--;
466 0 : uchar const * buffer_program_data = data + offset;
467 0 : ulong buffer_program_data_sz = end_offset - offset;
468 :
469 0 : fd_hash_t hash;
470 0 : fd_sha256_hash( buffer_program_data, buffer_program_data_sz, hash.uc );
471 0 : if( FD_UNLIKELY( 0!=memcmp( hash.uc, verified_build_hash->uc, FD_HASH_FOOTPRINT ) ) ) {
472 : /* CoreBpfMigrationError::BuildHashMismatch */
473 0 : return NULL;
474 0 : }
475 0 : }
476 :
477 0 : return acc;
478 0 : }
479 :
480 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L82-L95 */
481 :
482 : static fd_tmp_account_t *
483 : new_target_program_account( fd_tmp_account_t * acc,
484 : target_builtin_t const * target,
485 0 : fd_rent_t const * rent ) {
486 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L86-L88 */
487 0 : fd_bpf_state_t state = {
488 0 : .discriminant = FD_BPF_STATE_PROGRAM,
489 0 : .inner = {
490 0 : .program = {
491 0 : .programdata_address = target->program_data_address,
492 0 : }
493 0 : }
494 0 : };
495 :
496 0 : ulong state_sz = fd_bpf_state_size( &state );
497 0 : tmp_account_new( acc, state_sz );
498 0 : acc->lamports = fd_rent_exempt_minimum_balance( rent, SIZE_OF_PROGRAM );
499 0 : acc->executable = 1;
500 0 : memcpy( acc->owner.uc, fd_solana_bpf_loader_upgradeable_program_id.uc, sizeof(fd_pubkey_t) );
501 :
502 0 : ulong out_sz = 0UL;
503 0 : if( FD_UNLIKELY( fd_bpf_state_encode( &state, acc->data, state_sz, &out_sz ) ) ) {
504 0 : FD_LOG_ERR(( "fd_bpf_state_encode failed" ));
505 0 : }
506 :
507 0 : return acc;
508 0 : }
509 :
510 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L108-L153 */
511 : static fd_tmp_account_t *
512 : new_target_program_data_account( fd_tmp_account_t * acc,
513 : fd_tmp_account_t const * source,
514 : fd_pubkey_t const * upgrade_authority_address,
515 : fd_rent_t const * rent,
516 0 : ulong slot ) {
517 0 : ulong const buffer_metadata_sz = BUFFER_METADATA_SIZE;
518 :
519 0 : if( FD_UNLIKELY( source->data_sz < buffer_metadata_sz ) )
520 0 : return NULL; /* CoreBpfMigrationError::InvalidBufferAccount */
521 :
522 0 : fd_bpf_state_t state;
523 0 : if( FD_UNLIKELY( fd_bpf_state_decode( &state, source->data, buffer_metadata_sz ) ) ) {
524 0 : return NULL;
525 0 : }
526 :
527 0 : if( FD_UNLIKELY( state.discriminant!=FD_BPF_STATE_BUFFER ) )
528 0 : return NULL; /* CoreBpfMigrationError::InvalidBufferAccount */
529 :
530 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L118-L125 */
531 0 : if( upgrade_authority_address ) {
532 0 : if( FD_UNLIKELY( !state.inner.buffer.has_authority_address ||
533 0 : !fd_pubkey_eq( upgrade_authority_address, &state.inner.buffer.authority_address ) ) ) {
534 0 : return NULL; /* CoreBpfMigrationError::UpgradeAuthorityMismatch */
535 0 : }
536 0 : }
537 :
538 0 : void const * elf = (uchar const *)source->data + buffer_metadata_sz;
539 0 : ulong elf_sz = /* */source->data_sz - buffer_metadata_sz;
540 :
541 0 : ulong space = PROGRAMDATA_METADATA_SIZE + elf_sz;
542 0 : ulong lamports = fd_rent_exempt_minimum_balance( rent, space );
543 0 : fd_pubkey_t owner = fd_solana_bpf_loader_upgradeable_program_id;
544 :
545 0 : fd_bpf_state_t programdata_meta = {
546 0 : .discriminant = FD_BPF_STATE_PROGRAM_DATA,
547 0 : .inner = {
548 0 : .program_data = {
549 0 : .slot = slot,
550 0 : .has_upgrade_authority_address = !!upgrade_authority_address,
551 0 : .upgrade_authority_address = upgrade_authority_address ? *upgrade_authority_address : (fd_pubkey_t){{0}}
552 0 : }
553 0 : }
554 0 : };
555 :
556 0 : tmp_account_new( acc, space );
557 0 : acc->lamports = lamports;
558 0 : memcpy( acc->owner.uc, owner.uc, sizeof(fd_pubkey_t) );
559 0 : ulong out_sz = 0UL;
560 0 : if( FD_UNLIKELY( fd_bpf_state_encode( &programdata_meta, acc->data, PROGRAMDATA_METADATA_SIZE, &out_sz ) ) ) {
561 0 : FD_LOG_ERR(( "fd_bpf_state_encode failed" ));
562 0 : }
563 0 : fd_memcpy( (uchar *)acc->data+PROGRAMDATA_METADATA_SIZE, elf, elf_sz );
564 :
565 0 : return acc;
566 0 : }
567 :
568 : void
569 : migrate_builtin_to_core_bpf1( fd_core_bpf_migration_config_t const * config,
570 : fd_accdb_t * accdb,
571 : fd_bank_t * bank,
572 : fd_runtime_stack_t * runtime_stack,
573 : fd_pubkey_t const * builtin_program_id,
574 0 : fd_capture_ctx_t * capture_ctx ) {
575 0 : fd_memset( &runtime_stack->bpf_migration, 0, sizeof(runtime_stack->bpf_migration) );
576 :
577 0 : target_builtin_t target[1];
578 0 : if( FD_UNLIKELY( !target_builtin_new_checked(
579 0 : target,
580 0 : builtin_program_id,
581 0 : config->migration_target,
582 0 : FD_FEATURE_ACTIVE_BANK( bank, relax_programdata_account_check_migration ),
583 0 : accdb,
584 0 : bank->accdb_fork_id,
585 0 : runtime_stack ) ) )
586 0 : return;
587 :
588 0 : fd_tmp_account_t * source = &runtime_stack->bpf_migration.source;
589 0 : if( FD_UNLIKELY( !source_buffer_new_checked(
590 0 : source,
591 0 : accdb,
592 0 : bank->accdb_fork_id,
593 0 : config->source_buffer_address,
594 0 : config->verified_build_hash ) ) )
595 0 : return;
596 :
597 0 : fd_rent_t const * rent = &bank->f.rent;
598 0 : ulong const slot = bank->f.slot;
599 :
600 0 : fd_tmp_account_t * new_target_program = &runtime_stack->bpf_migration.new_target_program;
601 0 : if( FD_UNLIKELY( !new_target_program_account(
602 0 : new_target_program,
603 0 : target,
604 0 : rent ) ) )
605 0 : return;
606 0 : new_target_program->pubkey = *builtin_program_id;
607 :
608 0 : fd_tmp_account_t * new_target_program_data = &runtime_stack->bpf_migration.new_target_program_data;
609 0 : if( FD_UNLIKELY( !new_target_program_data_account(
610 0 : new_target_program_data,
611 0 : source,
612 0 : config->upgrade_authority_address,
613 0 : rent,
614 0 : slot ) ) )
615 0 : return;
616 0 : new_target_program_data->pubkey = target->program_data_address;
617 :
618 0 : ulong old_data_sz;
619 0 : if( FD_UNLIKELY( fd_ulong_checked_add( target->program_account->data_sz, source->data_sz, &old_data_sz ) ) ) return;
620 :
621 0 : ulong new_data_sz;
622 0 : if( FD_UNLIKELY( fd_ulong_checked_add( new_target_program->data_sz, new_target_program_data->data_sz, &new_data_sz ) ) ) return;
623 :
624 0 : FD_DCHECK_CRIT( new_target_program_data->data_sz>=PROGRAMDATA_METADATA_SIZE, "undersize account" );
625 : /* FIXME call fd_directly_invoke_loader_v3_deploy */
626 :
627 : /* https://github.com/anza-xyz/agave/blob/v3.1.8/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L267-L281 */
628 0 : ulong lamports_to_burn;
629 0 : if( FD_UNLIKELY( fd_ulong_checked_add( target->program_account->lamports, source->lamports, &lamports_to_burn ) ) ) return;
630 0 : if( FD_UNLIKELY( fd_ulong_checked_add( lamports_to_burn, target->program_data_account_lamports, &lamports_to_burn ) ) ) return;
631 :
632 0 : ulong lamports_to_fund;
633 0 : if( FD_UNLIKELY( fd_ulong_checked_add( new_target_program->lamports, new_target_program_data->lamports, &lamports_to_fund ) ) ) return;
634 :
635 : /* Write back accounts */
636 0 : tmp_account_store( bank, accdb, new_target_program, capture_ctx );
637 0 : tmp_account_store( bank, accdb, new_target_program_data, capture_ctx );
638 0 : fd_tmp_account_t * empty = &runtime_stack->bpf_migration.empty;
639 0 : tmp_account_new( empty, 0UL );
640 0 : empty->pubkey = source->pubkey;
641 0 : tmp_account_store( bank, accdb, empty, capture_ctx );
642 :
643 : /* FIXME "remove the built-in program from the bank's list of builtins" */
644 : /* FIXME "update account data size delta" */
645 0 : }
646 :
647 : /* Mimics migrate_builtin_to_core_bpf().
648 : https://github.com/anza-xyz/agave/blob/v3.1.7/runtime/src/bank/builtins/core_bpf_migration/mod.rs#215-303 */
649 : void
650 : fd_migrate_builtin_to_core_bpf( fd_bank_t * bank,
651 : fd_accdb_t * accdb,
652 : fd_runtime_stack_t * runtime_stack,
653 : fd_core_bpf_migration_config_t const * config,
654 0 : fd_capture_ctx_t * capture_ctx ) {
655 0 : migrate_builtin_to_core_bpf1( config, accdb, bank, runtime_stack, config->builtin_program_id, capture_ctx );
656 0 : }
657 :
658 : /* Mimics upgrade_core_bpf_program().
659 : https://github.com/anza-xyz/agave/blob/v3.1.7/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L319-L377 */
660 : void
661 : fd_upgrade_core_bpf_program( fd_bank_t * bank,
662 : fd_accdb_t * accdb,
663 : fd_runtime_stack_t * runtime_stack,
664 : fd_pubkey_t const * builtin_program_id,
665 : fd_pubkey_t const * source_buffer_address,
666 0 : fd_capture_ctx_t * capture_ctx ) {
667 0 : fd_memset( &runtime_stack->bpf_migration, 0, sizeof(runtime_stack->bpf_migration) );
668 :
669 : /* https://github.com/anza-xyz/agave/blob/v3.1.7/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L327 */
670 0 : target_core_bpf_t target[1];
671 0 : if( FD_UNLIKELY( !target_core_bpf_new_checked( target, builtin_program_id, accdb, bank->accdb_fork_id, runtime_stack ) ) ) {
672 0 : return;
673 0 : }
674 :
675 : /* https://github.com/anza-xyz/agave/blob/v3.1.7/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L328 */
676 0 : fd_tmp_account_t * source = &runtime_stack->bpf_migration.source;
677 0 : if( FD_UNLIKELY( !source_buffer_new_checked( source, accdb, bank->accdb_fork_id, source_buffer_address, NULL ) ) ) {
678 0 : return;
679 0 : }
680 :
681 : /* https://github.com/anza-xyz/agave/blob/v3.1.7/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L331-L332 */
682 0 : fd_tmp_account_t * new_target_program_data = &runtime_stack->bpf_migration.new_target_program_data;
683 0 : fd_pubkey_t program_data_address = get_program_data_address( builtin_program_id );
684 :
685 0 : ulong program_data_len = source->data_sz - BUFFER_METADATA_SIZE;
686 0 : ulong new_account_size = PROGRAMDATA_METADATA_SIZE + program_data_len;
687 :
688 0 : tmp_account_new( new_target_program_data, new_account_size );
689 0 : new_target_program_data->pubkey = program_data_address;
690 :
691 0 : fd_rent_t const * rent = &bank->f.rent;
692 0 : new_target_program_data->lamports = fd_rent_exempt_minimum_balance( rent, new_account_size );
693 0 : new_target_program_data->executable = 0;
694 0 : fd_memcpy( new_target_program_data->owner.uc, &fd_solana_bpf_loader_upgradeable_program_id, sizeof(fd_pubkey_t) );
695 :
696 0 : fd_bpf_state_t programdata_state[1] = {{
697 0 : .discriminant = FD_BPF_STATE_PROGRAM_DATA,
698 0 : .inner = { .program_data = {
699 0 : .slot = bank->f.slot,
700 0 : .upgrade_authority_address = target->upgrade_authority_address,
701 0 : .has_upgrade_authority_address = target->has_upgrade_authority_address
702 0 : }}
703 0 : }};
704 :
705 0 : ulong out_sz = 0UL;
706 0 : if( FD_UNLIKELY( fd_bpf_state_encode( programdata_state, new_target_program_data->data, PROGRAMDATA_METADATA_SIZE, &out_sz ) ) ) {
707 0 : return;
708 0 : }
709 :
710 0 : fd_memcpy( new_target_program_data->data + PROGRAMDATA_METADATA_SIZE,
711 0 : source->data + BUFFER_METADATA_SIZE,
712 0 : program_data_len );
713 :
714 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.4/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L339-L346 */
715 0 : ulong old_data_sz;
716 0 : if( FD_UNLIKELY( fd_ulong_checked_add( target->program_data_account->data_sz, source->data_sz, &old_data_sz ) ) ) return;
717 0 : ulong new_data_sz = new_target_program_data->data_sz;
718 :
719 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.4/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L349-L355 */
720 0 : uchar const * elf = new_target_program_data->data + PROGRAMDATA_METADATA_SIZE;
721 0 : ulong elf_sz = program_data_len;
722 0 : if( FD_UNLIKELY( fd_directly_invoke_loader_v3_deploy_checks( bank, runtime_stack, elf, elf_sz ) ) ) return;
723 :
724 : /* https://github.com/anza-xyz/agave/blob/v3.1.7/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L359-L364 */
725 0 : ulong lamports_to_burn;
726 0 : if( FD_UNLIKELY( fd_ulong_checked_add( target->program_data_account->lamports, source->lamports, &lamports_to_burn ) ) ) return;
727 :
728 : /* https://github.com/anza-xyz/agave/blob/v3.1.7/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L366-L371 */
729 0 : fd_pubkey_t source_addr = source->pubkey;
730 0 : tmp_account_store( bank, accdb, new_target_program_data, capture_ctx );
731 :
732 0 : fd_tmp_account_t * empty = &runtime_stack->bpf_migration.empty;
733 0 : tmp_account_new( empty, 0UL );
734 0 : empty->pubkey = source_addr;
735 0 : tmp_account_store( bank, accdb, empty, capture_ctx );
736 :
737 : /* https://github.com/anza-xyz/agave/blob/v3.1.7/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L374 */
738 : /* FIXME "update account data size delta" */
739 0 : (void)old_data_sz;
740 0 : (void)new_data_sz;
741 :
742 0 : fd_memset( &runtime_stack->bpf_migration, 0, sizeof(runtime_stack->bpf_migration) );
743 0 : }
744 :
745 : /* Mimics upgrade_loader_v2_program_with_loader_v3_program().
746 : https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L402-L474 */
747 : void
748 : fd_upgrade_loader_v2_program_with_loader_v3_program( fd_bank_t * bank,
749 : fd_accdb_t * accdb,
750 : fd_runtime_stack_t * runtime_stack,
751 : fd_pubkey_t const * loader_v2_program_address,
752 : fd_pubkey_t const * source_buffer_address,
753 : int allow_prefunded,
754 0 : fd_capture_ctx_t * capture_ctx ) {
755 0 : fd_memset( &runtime_stack->bpf_migration, 0, sizeof(runtime_stack->bpf_migration) );
756 :
757 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L411-L412 */
758 0 : target_builtin_t target[1];
759 0 : if( FD_UNLIKELY( !target_bpf_v2_new_checked(
760 0 : target,
761 0 : loader_v2_program_address,
762 0 : allow_prefunded,
763 0 : accdb,
764 0 : bank->accdb_fork_id,
765 0 : runtime_stack ) ) )
766 0 : return;
767 :
768 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L413 */
769 0 : fd_tmp_account_t * source = &runtime_stack->bpf_migration.source;
770 0 : if( FD_UNLIKELY( !source_buffer_new_checked( source, accdb, bank->accdb_fork_id, source_buffer_address, NULL ) ) )
771 0 : return;
772 :
773 0 : fd_rent_t const * rent = &bank->f.rent;
774 0 : ulong slot = bank->f.slot;
775 :
776 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L416-L417 */
777 0 : fd_tmp_account_t * new_target_program = &runtime_stack->bpf_migration.new_target_program;
778 0 : if( FD_UNLIKELY( !new_target_program_account( new_target_program, target, rent ) ) )
779 0 : return;
780 0 : new_target_program->pubkey = *loader_v2_program_address;
781 :
782 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L419-L421 */
783 0 : fd_tmp_account_t * new_target_program_data = &runtime_stack->bpf_migration.new_target_program_data;
784 0 : if( FD_UNLIKELY( !new_target_program_data_account( new_target_program_data, source, NULL, rent, slot ) ) ) {
785 0 : return;
786 0 : }
787 0 : new_target_program_data->pubkey = target->program_data_address;
788 :
789 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L427-L435 */
790 0 : ulong old_data_sz;
791 0 : if( FD_UNLIKELY( fd_ulong_checked_add( target->program_account->data_sz, source->data_sz, &old_data_sz ) ) ) return;
792 :
793 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L432-L435 */
794 0 : ulong new_data_sz;
795 0 : if( FD_UNLIKELY( fd_ulong_checked_add( new_target_program->data_sz, new_target_program_data->data_sz, &new_data_sz ) ) ) return;
796 :
797 0 : if( FD_UNLIKELY( new_target_program_data->data_sz<PROGRAMDATA_METADATA_SIZE ) ) {
798 0 : FD_LOG_CRIT(( "invariant violation: new target programdata too small" ));
799 0 : }
800 :
801 : /* Agave calls directly_invoke_loader_v3_deploy to deploy the new
802 : program to the program cache. We don't do that, but instead we
803 : perform the same checks as directly_invoke_loader_v3_deploy
804 : without modifying the program cache. We need to do the checks
805 : at this point so that we can fail the upgrade if the ELF is
806 : invalid.
807 :
808 : This is safe because the bpf migration code runs at the epoch
809 : boundary, before any transaction execution. The program cache
810 : automatically invalidates all programs at the start of an epoch
811 : boundary, so we do not need to explicitly update the cache during the
812 : migration.
813 :
814 : https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L437-L443*/
815 0 : uchar const * elf = (uchar const *)new_target_program_data->data + PROGRAMDATA_METADATA_SIZE;
816 0 : ulong elf_sz = new_target_program_data->data_sz - PROGRAMDATA_METADATA_SIZE;
817 0 : if( FD_UNLIKELY( fd_directly_invoke_loader_v3_deploy_checks( bank, runtime_stack, elf, elf_sz ) ) ) return;
818 :
819 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L451-L459 */
820 0 : ulong lamports_to_burn;
821 0 : if( FD_UNLIKELY( fd_ulong_checked_add( target->program_account->lamports, source->lamports, &lamports_to_burn ) ) ) return;
822 0 : if( FD_UNLIKELY( fd_ulong_checked_add( lamports_to_burn, target->program_data_account_lamports, &lamports_to_burn ) ) ) return;
823 :
824 0 : ulong lamports_to_fund;
825 0 : if( FD_UNLIKELY( fd_ulong_checked_add( new_target_program->lamports, new_target_program_data->lamports, &lamports_to_fund ) ) ) return;
826 :
827 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.2/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L462-L468 */
828 0 : tmp_account_store( bank, accdb, new_target_program, capture_ctx );
829 0 : tmp_account_store( bank, accdb, new_target_program_data, capture_ctx );
830 :
831 0 : fd_tmp_account_t * empty = &runtime_stack->bpf_migration.empty;
832 0 : tmp_account_new( empty, 0UL );
833 0 : empty->pubkey = source->pubkey;
834 0 : tmp_account_store( bank, accdb, empty, capture_ctx );
835 :
836 : /* NB: Agave updates "delta_off_chain", using these two fields,
837 : which is not consensus-critical (only used for Agave stats)
838 : so we don't update this in our migration code or store this in
839 : our bank. */
840 0 : (void)old_data_sz;
841 0 : (void)new_data_sz;
842 :
843 0 : fd_memset( &runtime_stack->bpf_migration, 0, sizeof(runtime_stack->bpf_migration) );
844 0 : }
|