Line data Source code
1 : #include "fd_vm_syscall.h"
2 : #include "../../runtime/fd_borrowed_account.h"
3 : #include "../../runtime/fd_system_ids.h"
4 : #include "../../runtime/program/fd_bpf_loader_program.h"
5 :
6 : /* FIXME: ALGO EFFICIENCY */
7 : static inline int
8 : fd_vm_syscall_cpi_is_signer( fd_pubkey_t const * account,
9 : fd_pubkey_t const * signers,
10 96 : ulong signers_cnt ) {
11 144 : for( ulong i=0UL; i<signers_cnt; i++ ) if( !memcmp( account->uc, signers[i].uc, sizeof(fd_pubkey_t) ) ) return 1;
12 48 : return 0;
13 96 : }
14 :
15 : /*
16 : fd_vm_prepare_instruction populates instruction_accounts and instruction_accounts_cnt
17 : with the instruction accounts ready for execution.
18 :
19 : The majority of this logic is taken from
20 : https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/invoke_context.rs#L311-L455,
21 : and is not vm-specific, but a part of the runtime.
22 : TODO: should we move this out of the CPI section?
23 :
24 : The bulk of the logic is concerned with unifying the privileges for each duplicated account,
25 : ensuring that each duplicate account referenced has the same privileges. It also performs some
26 : priviledge checks, for example ensuring the necessary signatures are present.
27 :
28 : TODO: instruction calling convention: const parameters after non-const.
29 :
30 : Assumptions:
31 : - We do not have more than 256 unique accounts in the callee_instr.
32 : This limit comes from the fact that a Solana transaction cannot
33 : refefence more than 256 unique accounts, due to the transaction
34 : serialization format.
35 : - callee_instr is not null.
36 : - callee_instr->acct_pubkeys is at least as long as callee_instr->acct_cnt
37 : - instr_ctx->txn_ctx->accounts_cnt is less than UCHAR_MAX.
38 : This is likely because the transaction is limited to 256 accounts.
39 : - callee_instr->program_id is set to UCHAR_MAX if account is not in instr_ctx->txn_ctx.
40 : - instruction_accounts is a 256-length empty array.
41 :
42 : Parameters:
43 : - callee_instr
44 : - instr_ctx
45 : - instruction_accounts
46 : - instruction_accounts_cnt
47 : - signers
48 : - signers_cnt
49 :
50 : Returns:
51 : - instruction_accounts
52 : - instruction_accounts_cnt
53 : Populated with the instruction accounts with normalized permissions.
54 :
55 : TODO: is it possible to pass the transaction indexes of the accounts in?
56 : This would allow us to make some of these algorithms more efficient.
57 : */
58 : int
59 : fd_vm_prepare_instruction( fd_instr_info_t * callee_instr,
60 : fd_exec_instr_ctx_t * instr_ctx,
61 : fd_pubkey_t const * callee_program_id_pubkey,
62 : fd_pubkey_t const instr_acct_keys[ FD_VM_CPI_MAX_INSTRUCTION_ACCOUNTS ],
63 : fd_instruction_account_t instruction_accounts[ FD_VM_CPI_MAX_INSTRUCTION_ACCOUNTS ],
64 : ulong * instruction_accounts_cnt,
65 : fd_pubkey_t const * signers,
66 3255 : ulong signers_cnt ) {
67 :
68 : /* De-duplicate the instruction accounts, using the same logic as Solana */
69 3255 : ulong deduplicated_instruction_accounts_cnt = 0;
70 3255 : fd_instruction_account_t deduplicated_instruction_accounts[ FD_VM_CPI_MAX_INSTRUCTION_ACCOUNTS ] = {0};
71 3255 : ulong duplicate_indicies_cnt = 0;
72 3255 : ulong duplicate_indices[ FD_VM_CPI_MAX_INSTRUCTION_ACCOUNTS ] = {0};
73 :
74 : /* This function is either called by a true CPI or by a native cpi invocation.
75 : The native CPI invocation is never called with more than 3 instruction
76 : accounts, and the true CPI is never called with more than
77 : FD_VM_CPI_MAX_INSTRUCTION_ACCOUNTS. */
78 3255 : if( FD_UNLIKELY( callee_instr->acct_cnt > FD_VM_CPI_MAX_INSTRUCTION_ACCOUNTS ) ) {
79 0 : FD_LOG_CRIT(( "invariant violation: too many accounts %u", callee_instr->acct_cnt ));
80 0 : }
81 :
82 : /* Normalize the privileges of each instruction account in the callee, after de-duping
83 : the account references.
84 : https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/invoke_context.rs#L327-L367 */
85 7707 : for( ulong i=0UL; i<callee_instr->acct_cnt; i++ ) {
86 4452 : ushort index_in_transaction = callee_instr->accounts[i].index_in_transaction;
87 4452 : ushort index_in_caller = callee_instr->accounts[i].index_in_caller;
88 :
89 4452 : if( index_in_transaction==USHORT_MAX ) {
90 : /* In this case the callee instruction is referencing an unknown account not listed in the
91 : transactions accounts. */
92 0 : FD_BASE58_ENCODE_32_BYTES( instr_acct_keys[i].uc, id_b58 );
93 0 : fd_log_collector_msg_many( instr_ctx, 2, "Instruction references an unknown account ", 42UL, id_b58, id_b58_len );
94 0 : FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_MISSING_ACC, instr_ctx->txn_out->err.exec_err_idx );
95 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
96 0 : }
97 :
98 : /* If there was an instruction account before this one which referenced the same
99 : transaction account index, find it's index in the deduplicated_instruction_accounts
100 : array. */
101 4452 : ulong duplicate_index = ULONG_MAX;
102 7713 : for( ulong j=0UL; j<deduplicated_instruction_accounts_cnt; j++ ) {
103 3405 : if( deduplicated_instruction_accounts[j].index_in_transaction==index_in_transaction ) {
104 144 : duplicate_index = j;
105 144 : break;
106 144 : }
107 3405 : }
108 :
109 : /* If this was account referenced in a previous iteration, update the flags to include those set
110 : in this iteration. This ensures that after all the iterations, the de-duplicated account flags
111 : for each account are the union of all the flags in all the references to that account in this instruction. */
112 :
113 : /* TODO: FD_UNLIKELY? Need to check which branch is more common by running against a larger mainnet ledger */
114 : /* TODO: this code would maybe be easier to read if we inverted the branches */
115 4452 : if( duplicate_index!=ULONG_MAX ) {
116 144 : if ( FD_UNLIKELY( duplicate_index >= deduplicated_instruction_accounts_cnt ) ) {
117 0 : FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_MISSING_ACC, instr_ctx->txn_out->err.exec_err_idx );
118 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
119 0 : }
120 :
121 144 : duplicate_indices[duplicate_indicies_cnt++] = duplicate_index;
122 144 : fd_instruction_account_t * instruction_account = &deduplicated_instruction_accounts[duplicate_index];
123 144 : instruction_account->is_signer = !!(instruction_account->is_signer | callee_instr->accounts[i].is_signer);
124 144 : instruction_account->is_writable = !!(instruction_account->is_writable | callee_instr->accounts[i].is_writable);
125 4308 : } else {
126 : /* In the case where the callee instruction is NOT a duplicate, we need to
127 : create the deduplicated_instruction_accounts fd_instruction_account_t object. */
128 :
129 : /* Add the instruction account to the duplicate indicies array */
130 4308 : duplicate_indices[duplicate_indicies_cnt++] = deduplicated_instruction_accounts_cnt;
131 :
132 : /* Initialize the instruction account in the deduplicated_instruction_accounts array */
133 4308 : fd_instruction_account_t * instruction_account = &deduplicated_instruction_accounts[deduplicated_instruction_accounts_cnt++];
134 4308 : *instruction_account = fd_instruction_account_init( index_in_transaction,
135 4308 : index_in_caller,
136 4308 : (ushort)i,
137 4308 : !!(callee_instr->accounts[i].is_writable),
138 4308 : !!(callee_instr->accounts[i].is_signer) );
139 4308 : }
140 4452 : }
141 :
142 : /* Check the normalized account permissions for privilege escalation.
143 : https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/invoke_context.rs#L369-L419 */
144 7467 : for( ulong i = 0; i < deduplicated_instruction_accounts_cnt; i++ ) {
145 4308 : fd_instruction_account_t * instruction_account = &deduplicated_instruction_accounts[i];
146 :
147 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/invoke_context.rs#L393-L403 */
148 4308 : ushort caller_idx = instruction_account->index_in_caller;
149 4308 : fd_pubkey_t const * caller_pubkey = NULL;
150 4308 : int err = fd_exec_instr_ctx_get_key_of_account_at_index( instr_ctx, caller_idx, &caller_pubkey );
151 4308 : if( FD_UNLIKELY( err ) ) {
152 0 : FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_MISSING_ACC, instr_ctx->txn_out->err.exec_err_idx );
153 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
154 0 : }
155 :
156 : /* Check that the account is not read-only in the caller but writable in the callee */
157 4308 : if( FD_UNLIKELY( instruction_account->is_writable && !fd_instr_acc_is_writable_idx( instr_ctx->instr, caller_idx ) ) ) {
158 48 : FD_BASE58_ENCODE_32_BYTES( caller_pubkey->uc, id_b58 );
159 48 : fd_log_collector_msg_many( instr_ctx, 2, id_b58, id_b58_len, "'s writable privilege escalated", 31UL );
160 48 : FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_PRIVILEGE_ESCALATION, instr_ctx->txn_out->err.exec_err_idx );
161 48 : return FD_EXECUTOR_INSTR_ERR_PRIVILEGE_ESCALATION;
162 48 : }
163 :
164 : /* If the account is signed in the callee, it must be signed by the caller or the program */
165 4260 : if ( FD_UNLIKELY( instruction_account->is_signer && !( fd_instr_acc_is_signer_idx( instr_ctx->instr, caller_idx, NULL ) || fd_vm_syscall_cpi_is_signer( caller_pubkey, signers, signers_cnt ) ) ) ) {
166 48 : FD_BASE58_ENCODE_32_BYTES( caller_pubkey->uc, id_b58 );
167 48 : fd_log_collector_msg_many( instr_ctx, 2, id_b58, id_b58_len, "'s signer privilege escalated", 29UL );
168 48 : FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_PRIVILEGE_ESCALATION, instr_ctx->txn_out->err.exec_err_idx );
169 48 : return FD_EXECUTOR_INSTR_ERR_PRIVILEGE_ESCALATION;
170 48 : }
171 4260 : }
172 :
173 : /* Copy the accounts with their normalized permissions over to the final instruction_accounts array,
174 : and set the callee_instr acct_flags. */
175 7515 : for (ulong i = 0; i < duplicate_indicies_cnt; i++) {
176 4356 : ulong duplicate_index = duplicate_indices[i];
177 :
178 : /* Failing this condition is technically impossible, but it is probably safest to keep this in
179 : so that we throw InstructionError::NotEnoughAccountKeys at the same point at Solana does,
180 : in the event any surrounding code is changed.
181 : https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/invoke_context.rs#L345-L366 */
182 4356 : if ( FD_LIKELY( duplicate_index < deduplicated_instruction_accounts_cnt ) ) {
183 4356 : instruction_accounts[i] = deduplicated_instruction_accounts[duplicate_index];
184 4356 : callee_instr->accounts[i].is_writable = !!(instruction_accounts[i].is_writable);
185 4356 : callee_instr->accounts[i].is_signer = !!(instruction_accounts[i].is_signer);
186 4356 : } else {
187 0 : FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_MISSING_ACC, instr_ctx->txn_out->err.exec_err_idx );
188 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
189 0 : }
190 4356 : }
191 :
192 : /* Obtain the program account index and return a MissingAccount error if not found.
193 : https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/invoke_context.rs#L421-L436 */
194 3159 : int program_idx = fd_exec_instr_ctx_find_idx_of_instr_account( instr_ctx, callee_program_id_pubkey );
195 3159 : if( FD_UNLIKELY( program_idx == -1 ) ) {
196 0 : FD_BASE58_ENCODE_32_BYTES( callee_program_id_pubkey->uc, id_b58 );
197 0 : fd_log_collector_msg_many( instr_ctx, 2, "Unknown program ", 16UL, id_b58, id_b58_len );
198 0 : FD_TXN_ERR_FOR_LOG_INSTR( instr_ctx->txn_out, FD_EXECUTOR_INSTR_ERR_MISSING_ACC, instr_ctx->txn_out->err.exec_err_idx );
199 0 : return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
200 0 : }
201 :
202 3159 : *instruction_accounts_cnt = duplicate_indicies_cnt;
203 :
204 3159 : return 0;
205 3159 : }
206 :
207 : /**********************************************************************
208 : CROSS PROGRAM INVOCATION (Generic logic)
209 : **********************************************************************/
210 :
211 : /* FD_CPI_MAX_SIGNER_CNT is the max amount of PDA signer addresses that
212 : a cross-program invocation can include in an instruction.
213 :
214 : https://github.com/solana-labs/solana/blob/dbf06e258ae418097049e845035d7d5502fe1327/programs/bpf_loader/src/syscalls/mod.rs#L80 */
215 :
216 : #define FD_CPI_MAX_SIGNER_CNT (16UL)
217 :
218 : /* "Maximum number of account info structs that can be used in a single CPI
219 : invocation. A limit on account info structs is effectively the same as
220 : limiting the number of unique accounts. 128 was chosen to match the max
221 : number of locked accounts per transaction (MAX_TX_ACCOUNT_LOCKS)."
222 :
223 : https://github.com/solana-labs/solana/blob/dbf06e258ae418097049e845035d7d5502fe1327/sdk/program/src/syscalls/mod.rs#L25
224 : https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/programs/bpf_loader/src/syscalls/cpi.rs#L1011 */
225 :
226 9 : #define FD_CPI_MAX_ACCOUNT_INFOS (128UL)
227 18 : #define FD_CPI_MAX_ACCOUNT_INFOS_SIMD_0339 (255UL)
228 :
229 : /* This is just encoding what Agave says in their code comments into a
230 : compile-time check, so if anyone ever inadvertently changes one of
231 : the limits, they will have to take a look. */
232 : FD_STATIC_ASSERT( FD_CPI_MAX_ACCOUNT_INFOS==MAX_TX_ACCOUNT_LOCKS, cpi_max_account_info );
233 :
234 : /* https://github.com/anza-xyz/agave/blob/v3.1.2/program-runtime/src/cpi.rs#L168-L180 */
235 : static inline ulong
236 3123 : get_cpi_max_account_infos( fd_bank_t * bank ) {
237 3123 : if( FD_LIKELY( FD_FEATURE_ACTIVE_BANK( bank, increase_cpi_account_info_limit ) ) ) {
238 18 : return FD_CPI_MAX_ACCOUNT_INFOS_SIMD_0339;
239 3105 : } else if( FD_LIKELY( FD_FEATURE_ACTIVE_BANK( bank, increase_tx_account_lock_limit ) ) ) {
240 9 : return FD_CPI_MAX_ACCOUNT_INFOS;
241 3096 : } else {
242 3096 : return 64UL;
243 3096 : }
244 3123 : }
245 :
246 : /* https://github.com/anza-xyz/agave/blob/v3.1.2/program-runtime/src/execution_budget.rs#L25-L31 */
247 : static inline ulong
248 3285 : get_cpi_invoke_unit_cost( fd_bank_t * bank ) {
249 3285 : return fd_ulong_if(
250 3285 : FD_FEATURE_ACTIVE_BANK( bank, increase_cpi_account_info_limit ),
251 3285 : FD_VM_INVOKE_UNITS_SIMD_0339,
252 3285 : FD_VM_INVOKE_UNITS );
253 3285 : }
254 :
255 : /* fd_vm_syscall_cpi_check_instruction contains common instruction acct
256 : count and data sz checks. Also consumes compute units proportional
257 : to instruction data size. */
258 :
259 : static int
260 : fd_vm_syscall_cpi_check_instruction( ulong acct_cnt,
261 3285 : ulong data_sz ) {
262 : /* https://github.com/anza-xyz/agave/blob/v3.1.2/program-runtime/src/cpi.rs#L146-L161 */
263 3285 : if( FD_UNLIKELY( acct_cnt > FD_VM_CPI_MAX_INSTRUCTION_ACCOUNTS ) ) {
264 : // SyscallError::MaxInstructionAccountsExceeded
265 6 : return FD_VM_SYSCALL_ERR_MAX_INSTRUCTION_ACCOUNTS_EXCEEDED;
266 6 : }
267 3279 : if( FD_UNLIKELY( data_sz>FD_RUNTIME_CPI_MAX_INSTR_DATA_LEN ) ) {
268 : // SyscallError::MaxInstructionDataLenExceeded
269 0 : return FD_VM_SYSCALL_ERR_MAX_INSTRUCTION_DATA_LEN_EXCEEDED;
270 0 : }
271 :
272 3279 : return FD_VM_SUCCESS;
273 3279 : }
274 :
275 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1276-L1311 */
276 : static inline int
277 : fd_vm_cpi_update_caller_account_region( fd_vm_t * vm,
278 : fd_vm_cpi_translated_account_t const * translated_account,
279 1263 : fd_borrowed_account_t * borrowed_account ) {
280 1263 : fd_vm_cpi_caller_account_t const * caller_account = &translated_account->caller_account;
281 :
282 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1283-L1290 */
283 1263 : ulong address_space_reserved_for_account;
284 1263 : if( vm->is_deprecated ) {
285 570 : address_space_reserved_for_account = caller_account->orig_data_len;
286 693 : } else {
287 693 : address_space_reserved_for_account = fd_ulong_sat_add( caller_account->orig_data_len, MAX_PERMITTED_DATA_INCREASE );
288 693 : }
289 :
290 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1292-L1308 */
291 1263 : if( address_space_reserved_for_account > 0UL ) {
292 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1295-L1297 */
293 1251 : fd_vm_acc_region_meta_t * acc_region_meta = &vm->acc_region_metas[ translated_account->index_in_caller ];
294 1251 : fd_vm_input_region_t * region = &vm->input_mem_regions[ acc_region_meta->region_idx + 1UL ];
295 :
296 : /* Note that we don't special-case direct mapping here, as Agave does,
297 : because we do not create regions using CoW upon resize like Agave does.
298 :
299 : Therefore we do not need the logic in the Agave code to create a new
300 : region, as we have already created all the regions for each account.
301 :
302 : Therefore we do not have equivalents of Agave's
303 : modify_memory_region_of_account and create_memory_region_of_account
304 : functions, but we instead inline this logic directly below. */
305 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/cpi.rs#L1301-L1307 */
306 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/program-runtime/src/serialization.rs#L23-L35 */
307 1251 : region->region_sz = (uint)fd_borrowed_account_get_data_len( borrowed_account );
308 :
309 1251 : int err;
310 1251 : int is_writable = fd_borrowed_account_can_data_be_changed( borrowed_account, &err );
311 :
312 1251 : region->is_writable = (uchar)is_writable && ( err == FD_EXECUTOR_INSTR_SUCCESS );
313 1251 : }
314 :
315 1263 : return FD_VM_SUCCESS;
316 1263 : }
317 :
318 : /**********************************************************************
319 : CROSS PROGRAM INVOCATION HELPERS
320 : **********************************************************************/
321 :
322 : static inline int
323 : fd_vm_syscall_cpi_check_id( fd_pubkey_t const * program_id,
324 22827 : uchar const * loader ) {
325 22827 : return !memcmp( program_id, loader, sizeof(fd_pubkey_t) );
326 22827 : }
327 :
328 : /* fd_vm_syscall_cpi_is_precompile returns true if the given program_id
329 : corresponds to a precompile. It does this by checking against a hardcoded
330 : list of known pre-compiles.
331 :
332 : This mirrors the behaviour in solana_sdk::precompiles::is_precompile
333 : https://github.com/solana-labs/solana/blob/2afde1b028ed4593da5b6c735729d8994c4bfac6/sdk/src/precompiles.rs#L93
334 : */
335 : static inline int
336 3261 : fd_vm_syscall_cpi_is_precompile( fd_pubkey_t const * program_id ) {
337 3261 : return fd_vm_syscall_cpi_check_id(program_id, fd_solana_keccak_secp_256k_program_id.key) |
338 3261 : fd_vm_syscall_cpi_check_id(program_id, fd_solana_ed25519_sig_verify_program_id.key) |
339 3261 : fd_vm_syscall_cpi_check_id(program_id, fd_solana_secp256r1_program_id.key);
340 3261 : }
341 :
342 : /* fd_vm_syscall_cpi_check_authorized_program corresponds to
343 : solana_bpf_loader_program::syscalls::cpi::check_authorized_program:
344 : https://github.com/solana-labs/solana/blob/2afde1b028ed4593da5b6c735729d8994c4bfac6/programs/bpf_loader/src/syscalls/cpi.rs#L1032
345 :
346 : It determines if the given program_id is authorized to execute a CPI call.
347 : Returns 1 if authorized, 0 otherwise.
348 : */
349 : static inline int
350 : fd_vm_syscall_cpi_check_authorized_program( fd_pubkey_t const * program_id,
351 : fd_bank_t * bank,
352 : uchar const * instruction_data,
353 3261 : ulong instruction_data_len ) {
354 : /* FIXME: do this in a branchless manner? using bitwise comparison would probably be faster */
355 3261 : return !( fd_vm_syscall_cpi_check_id(program_id, fd_solana_native_loader_id.key) ||
356 3261 : fd_vm_syscall_cpi_check_id(program_id, fd_solana_bpf_loader_program_id.key) ||
357 3261 : fd_vm_syscall_cpi_check_id(program_id, fd_solana_bpf_loader_deprecated_program_id.key) ||
358 3261 : ( fd_vm_syscall_cpi_check_id(program_id, fd_solana_bpf_loader_upgradeable_program_id.key) &&
359 3261 : !(( instruction_data_len != 0 && instruction_data[0] == FD_BPF_INSTR_UPGRADE ) ||
360 0 : ( instruction_data_len != 0 && instruction_data[0] == FD_BPF_INSTR_SET_AUTHORITY ) ||
361 0 : ( FD_FEATURE_ACTIVE_BANK( bank, enable_bpf_loader_set_authority_checked_ix ) &&
362 0 : ( instruction_data_len != 0 && instruction_data[0] == FD_BPF_INSTR_SET_AUTHORITY_CHECKED )) ||
363 0 : ( FD_FEATURE_ACTIVE_BANK( bank, enable_extend_program_checked ) &&
364 0 : ( instruction_data_len != 0 && instruction_data[0] == FD_BPF_INSTR_EXTEND_PROGRAM_CHECKED )) ||
365 0 : ( instruction_data_len != 0 && instruction_data[0] == FD_BPF_INSTR_CLOSE ))) ||
366 3261 : fd_vm_syscall_cpi_is_precompile( program_id ) );
367 3261 : }
368 :
369 : /* The data and lamports fields are in an Rc<Refcell<T>> in the Rust ABI AccountInfo.
370 : These macros perform the equivalent of Rc<Refcell<T>>.as_ptr() in Agave.
371 : This function doesn't actually touch any memory.
372 : It performs pointer arithmetic.
373 : */
374 : FD_FN_CONST static inline
375 11685 : ulong vm_syscall_cpi_acc_info_rc_refcell_as_ptr( ulong rc_refcell_vaddr ) {
376 11685 : return rc_refcell_vaddr + offsetof(fd_vm_rc_refcell_t, payload);
377 11685 : }
378 :
379 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L508-L514
380 : */
381 : FD_FN_CONST static inline
382 1878 : ulong vm_syscall_cpi_data_len_vaddr_c( ulong acct_info_vaddr, ulong data_len_haddr, ulong acct_info_haddr ) {
383 1878 : return fd_ulong_sat_sub( fd_ulong_sat_add( acct_info_vaddr, data_len_haddr ), acct_info_haddr );
384 1878 : }
385 :
386 : /**********************************************************************
387 : CROSS PROGRAM INVOCATION (C ABI)
388 : **********************************************************************/
389 :
390 : #define VM_SYSCALL_CPI_ABI c
391 1599 : #define VM_SYSCALL_CPI_INSTR_T fd_vm_c_instruction_t
392 : #define VM_SYSCALL_CPI_INSTR_ALIGN (FD_VM_C_INSTRUCTION_ALIGN)
393 : #define VM_SYSCALL_CPI_INSTR_SIZE (FD_VM_C_INSTRUCTION_SIZE)
394 6009 : #define VM_SYSCALL_CPI_ACC_META_T fd_vm_c_account_meta_t
395 : #define VM_SYSCALL_CPI_ACC_META_ALIGN (FD_VM_C_ACCOUNT_META_ALIGN)
396 : #define VM_SYSCALL_CPI_ACC_META_SIZE (FD_VM_C_ACCOUNT_META_SIZE)
397 1518 : #define VM_SYSCALL_CPI_ACC_INFO_T fd_vm_c_account_info_t
398 : #define VM_SYSCALL_CPI_ACC_INFO_ALIGN (FD_VM_C_ACCOUNT_INFO_ALIGN)
399 3414 : #define VM_SYSCALL_CPI_ACC_INFO_SIZE (FD_VM_C_ACCOUNT_INFO_SIZE)
400 :
401 : /* VM_SYSCALL_CPI_INSTR_T accessors */
402 : #define VM_SYSCALL_CPI_INSTR_DATA_ADDR( instr ) instr->data_addr
403 4779 : #define VM_SYSCALL_CPI_INSTR_DATA_LEN( instr ) instr->data_len
404 : #define VM_SYSCALL_CPI_INSTR_ACCS_ADDR( instr ) instr->accounts_addr
405 10791 : #define VM_SYSCALL_CPI_INSTR_ACCS_LEN( instr ) instr->accounts_len
406 : #define VM_SYSCALL_CPI_INSTR_PROGRAM_ID( vm, instr ) \
407 1599 : FD_VM_MEM_HADDR_LD( vm, instr->program_id_addr, alignof(uchar), sizeof(fd_pubkey_t) )
408 :
409 : /* VM_SYSCALL_CPI_ACC_META_T accessors */
410 4398 : #define VM_SYSCALL_CPI_ACC_META_IS_WRITABLE( acc_meta ) acc_meta->is_writable
411 4398 : #define VM_SYSCALL_CPI_ACC_META_IS_SIGNER( acc_meta ) acc_meta->is_signer
412 : #define VM_SYSCALL_CPI_ACC_META_PUBKEY( vm, acc_meta ) \
413 4410 : FD_VM_MEM_HADDR_LD( vm, acc_meta->pubkey_addr, alignof(uchar), sizeof(fd_pubkey_t) )
414 :
415 : /* VM_SYSCALL_CPI_ACC_INFO_T accessors */
416 : #define VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_VADDR( vm, acc_info, decl ) \
417 1935 : ulong decl = acc_info->lamports_addr;
418 : #define VM_SYSCALL_CPI_ACC_INFO_LAMPORTS( vm, acc_info, decl ) \
419 1917 : ulong * decl = FD_VM_MEM_HADDR_ST( vm, acc_info->lamports_addr, alignof(ulong), sizeof(ulong) );
420 :
421 : /* https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L304 */
422 : #define VM_SYSCALL_CPI_ACC_INFO_DATA_VADDR( vm, acc_info, decl ) \
423 1914 : ulong decl = acc_info->data_addr;
424 : #define VM_SYSCALL_CPI_ACC_INFO_DATA( vm, acc_info, decl ) \
425 984 : uchar * decl = FD_VM_MEM_SLICE_HADDR_ST( vm, acc_info->data_addr, alignof(uchar), acc_info->data_sz ); \
426 978 : ulong FD_EXPAND_THEN_CONCAT2(decl, _vm_addr) = acc_info->data_addr; \
427 978 : ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = acc_info->data_sz;
428 :
429 : #define VM_SYSCALL_CPI_ACC_INFO_METADATA( vm, acc_info, decl ) \
430 : ulong FD_EXPAND_THEN_CONCAT2(decl, _vm_addr) = acc_info->data_addr; \
431 : ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = acc_info->data_sz;
432 :
433 : #define VM_SYSCALL_CPI_SET_ACC_INFO_DATA_GET_LEN( vm, acc_info, decl ) \
434 540 : ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = acc_info->data_sz;
435 :
436 : #include "fd_vm_syscall_cpi_common.c"
437 :
438 : #undef VM_SYSCALL_CPI_ABI
439 : #undef VM_SYSCALL_CPI_INSTR_T
440 : #undef VM_SYSCALL_CPI_INSTR_ALIGN
441 : #undef VM_SYSCALL_CPI_INSTR_SIZE
442 : #undef VM_SYSCALL_CPI_ACC_META_T
443 : #undef VM_SYSCALL_CPI_ACC_META_ALIGN
444 : #undef VM_SYSCALL_CPI_ACC_META_SIZE
445 : #undef VM_SYSCALL_CPI_ACC_INFO_T
446 : #undef VM_SYSCALL_CPI_ACC_INFO_ALIGN
447 : #undef VM_SYSCALL_CPI_ACC_INFO_SIZE
448 : #undef VM_SYSCALL_CPI_INSTR_DATA_ADDR
449 : #undef VM_SYSCALL_CPI_INSTR_DATA_LEN
450 : #undef VM_SYSCALL_CPI_INSTR_ACCS_ADDR
451 : #undef VM_SYSCALL_CPI_INSTR_ACCS_LEN
452 : #undef VM_SYSCALL_CPI_INSTR_PROGRAM_ID
453 : #undef VM_SYSCALL_CPI_ACC_META_IS_WRITABLE
454 : #undef VM_SYSCALL_CPI_ACC_META_IS_SIGNER
455 : #undef VM_SYSCALL_CPI_ACC_META_PUBKEY
456 : #undef VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_VADDR
457 : #undef VM_SYSCALL_CPI_ACC_INFO_LAMPORTS
458 : #undef VM_SYSCALL_CPI_ACC_INFO_DATA_VADDR
459 : #undef VM_SYSCALL_CPI_ACC_INFO_DATA
460 : #undef VM_SYSCALL_CPI_ACC_INFO_METADATA
461 : #undef VM_SYSCALL_CPI_SET_ACC_INFO_DATA_GET_LEN
462 :
463 : /**********************************************************************
464 : CROSS PROGRAM INVOCATION (Rust ABI)
465 : **********************************************************************/
466 :
467 : #define VM_SYSCALL_CPI_ABI rust
468 1686 : #define VM_SYSCALL_CPI_INSTR_T fd_vm_rust_instruction_t
469 : #define VM_SYSCALL_CPI_INSTR_ALIGN (FD_VM_RUST_INSTRUCTION_ALIGN)
470 : #define VM_SYSCALL_CPI_INSTR_SIZE (FD_VM_RUST_INSTRUCTION_SIZE)
471 6204 : #define VM_SYSCALL_CPI_ACC_META_T fd_vm_rust_account_meta_t
472 : #define VM_SYSCALL_CPI_ACC_META_ALIGN (FD_VM_RUST_ACCOUNT_META_ALIGN)
473 : #define VM_SYSCALL_CPI_ACC_META_SIZE (FD_VM_RUST_ACCOUNT_META_SIZE)
474 1605 : #define VM_SYSCALL_CPI_ACC_INFO_T fd_vm_rust_account_info_t
475 : #define VM_SYSCALL_CPI_ACC_INFO_ALIGN (FD_VM_RUST_ACCOUNT_INFO_ALIGN)
476 1623 : #define VM_SYSCALL_CPI_ACC_INFO_SIZE (FD_VM_RUST_ACCOUNT_INFO_SIZE)
477 :
478 : /* VM_SYSCALL_CPI_INSTR_T accessors */
479 : #define VM_SYSCALL_CPI_INSTR_DATA_ADDR( instr ) instr->data.addr
480 5040 : #define VM_SYSCALL_CPI_INSTR_DATA_LEN( instr ) instr->data.len
481 : #define VM_SYSCALL_CPI_INSTR_ACCS_ADDR( instr ) instr->accounts.addr
482 11229 : #define VM_SYSCALL_CPI_INSTR_ACCS_LEN( instr ) instr->accounts.len
483 1686 : #define VM_SYSCALL_CPI_INSTR_PROGRAM_ID( vm, instr ) instr->pubkey
484 :
485 : /* VM_SYSCALL_CPI_ACC_META_T accessors */
486 4506 : #define VM_SYSCALL_CPI_ACC_META_IS_WRITABLE( acc_meta ) acc_meta->is_writable
487 4506 : #define VM_SYSCALL_CPI_ACC_META_IS_SIGNER( acc_meta ) acc_meta->is_signer
488 4518 : #define VM_SYSCALL_CPI_ACC_META_PUBKEY( vm, acc_meta ) acc_meta->pubkey
489 :
490 : /* VM_SYSCALL_CPI_ACC_INFO_T accessors */
491 :
492 : /* The lamports and the account data are stored behind RefCells,
493 : so we have an additional layer of indirection to unwrap. */
494 : #define VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_VADDR( vm, acc_info, decl ) \
495 2013 : ulong const * FD_EXPAND_THEN_CONCAT2(decl, _hptr_) = \
496 2013 : FD_VM_MEM_HADDR_LD( vm, vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->lamports_box_addr ), FD_VM_RC_REFCELL_ALIGN, sizeof(ulong) ); \
497 2013 : /* Extract the vaddr embedded in the RefCell */ \
498 2013 : ulong decl = *FD_EXPAND_THEN_CONCAT2(decl, _hptr_);
499 :
500 : /* https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L184-L195 */
501 : #define VM_SYSCALL_CPI_ACC_INFO_LAMPORTS( vm, acc_info, decl ) \
502 1977 : ulong FD_EXPAND_THEN_CONCAT2(decl, _vaddr_) = \
503 1977 : *((ulong const *)FD_VM_MEM_HADDR_LD( vm, vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->lamports_box_addr ), FD_VM_RC_REFCELL_ALIGN, sizeof(ulong) )); \
504 1977 : ulong * decl = FD_VM_MEM_HADDR_ST( vm, FD_EXPAND_THEN_CONCAT2(decl, _vaddr_), alignof(ulong), sizeof(ulong) );
505 :
506 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.3/program-runtime/src/cpi.rs#L367-L378 */
507 : #define VM_SYSCALL_CPI_ACC_INFO_DATA_VADDR( vm, acc_info, decl ) \
508 1968 : if( FD_UNLIKELY( vm->syscall_parameter_address_restrictions && \
509 1968 : vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->data_box_addr ) >= FD_VM_MEM_MAP_INPUT_REGION_START ) ) { \
510 18 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_POINTER ); \
511 18 : return FD_VM_SYSCALL_ERR_INVALID_POINTER; \
512 18 : } \
513 1968 : /* Translate the vaddr to the slice */ \
514 1968 : fd_vm_vec_t const * FD_EXPAND_THEN_CONCAT2(decl, _hptr_) = \
515 1950 : FD_VM_MEM_HADDR_LD( vm, vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->data_box_addr ), FD_VM_RC_REFCELL_ALIGN, sizeof(fd_vm_vec_t) ); \
516 1950 : /* Extract the vaddr embedded in the slice */ \
517 1950 : ulong decl = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->addr;
518 :
519 : /* https://github.com/anza-xyz/agave/blob/v3.0.4/syscalls/src/cpi.rs#L212-L221 */
520 : #define VM_SYSCALL_CPI_ACC_INFO_DATA_LEN_VADDR( vm, acc_info, decl ) \
521 1908 : ulong decl = fd_ulong_sat_add( vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->data_box_addr ), sizeof(ulong) );
522 :
523 : #define VM_SYSCALL_CPI_ACC_INFO_DATA( vm, acc_info, decl ) \
524 : /* Translate the vaddr to the slice */ \
525 1011 : fd_vm_vec_t const * FD_EXPAND_THEN_CONCAT2(decl, _hptr_) = \
526 1011 : FD_VM_MEM_HADDR_LD( vm, vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->data_box_addr ), FD_VM_RC_REFCELL_ALIGN, sizeof(fd_vm_vec_t) ); \
527 1011 : /* Declare the vaddr of the slice's underlying byte array */ \
528 1011 : ulong FD_EXPAND_THEN_CONCAT2(decl, _vm_addr) = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->addr; \
529 1011 : /* Declare the size of the slice's underlying byte array */ \
530 1011 : ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->len; \
531 1011 : /* Translate the vaddr to the underlying byte array */ \
532 1011 : uchar * decl = FD_VM_MEM_SLICE_HADDR_ST( \
533 1005 : vm, FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->addr, alignof(uchar), FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->len );
534 :
535 : #define VM_SYSCALL_CPI_ACC_INFO_METADATA( vm, acc_info, decl ) \
536 : /* Translate the vaddr to the slice */ \
537 : fd_vm_vec_t const * FD_EXPAND_THEN_CONCAT2(decl, _hptr_) = \
538 : FD_VM_MEM_HADDR_LD( vm, vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->data_box_addr ), FD_VM_RC_REFCELL_ALIGN, sizeof(fd_vm_vec_t) ); \
539 : /* Declare the vaddr of the slice's underlying byte array */ \
540 : ulong FD_EXPAND_THEN_CONCAT2(decl, _vm_addr) = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->addr; \
541 : /* Declare the size of the slice's underlying byte array */ \
542 : ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->len;
543 :
544 : #define VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_RC_REFCELL_VADDR( vm, acc_info, decl ) \
545 1431 : ulong decl = vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->lamports_box_addr );
546 :
547 : #define VM_SYSCALL_CPI_ACC_INFO_DATA_RC_REFCELL_VADDR( vm, acc_info, decl ) \
548 : ulong decl = vm_syscall_cpi_acc_info_rc_refcell_as_ptr( acc_info->data_box_addr );
549 :
550 : #define VM_SYSCALL_CPI_SET_ACC_INFO_DATA_GET_LEN( vm, acc_info, decl ) \
551 573 : ulong FD_EXPAND_THEN_CONCAT2(decl, _len) = FD_EXPAND_THEN_CONCAT2(decl, _hptr_)->len;
552 :
553 : #include "fd_vm_syscall_cpi_common.c"
554 :
555 : #undef VM_SYSCALL_CPI_ABI
556 : #undef VM_SYSCALL_CPI_INSTR_T
557 : #undef VM_SYSCALL_CPI_INSTR_ALIGN
558 : #undef VM_SYSCALL_CPI_INSTR_SIZE
559 : #undef VM_SYSCALL_CPI_ACC_META_T
560 : #undef VM_SYSCALL_CPI_ACC_META_ALIGN
561 : #undef VM_SYSCALL_CPI_ACC_META_SIZE
562 : #undef VM_SYSCALL_CPI_ACC_INFO_T
563 : #undef VM_SYSCALL_CPI_ACC_INFO_ALIGN
564 : #undef VM_SYSCALL_CPI_ACC_INFO_SIZE
565 : #undef VM_SYSCALL_CPI_INSTR_DATA_ADDR
566 : #undef VM_SYSCALL_CPI_INSTR_DATA_LEN
567 : #undef VM_SYSCALL_CPI_INSTR_ACCS_ADDR
568 : #undef VM_SYSCALL_CPI_INSTR_ACCS_LEN
569 : #undef VM_SYSCALL_CPI_INSTR_PROGRAM_ID
570 : #undef VM_SYSCALL_CPI_ACC_META_IS_WRITABLE
571 : #undef VM_SYSCALL_CPI_ACC_META_IS_SIGNER
572 : #undef VM_SYSCALL_CPI_ACC_META_PUBKEY
573 : #undef VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_VADDR
574 : #undef VM_SYSCALL_CPI_ACC_INFO_LAMPORTS
575 : #undef VM_SYSCALL_CPI_ACC_INFO_DATA_VADDR
576 : #undef VM_SYSCALL_CPI_ACC_INFO_DATA
577 : #undef VM_SYSCALL_CPI_ACC_INFO_DATA_LEN_VADDR
578 : #undef VM_SYSCALL_CPI_ACC_INFO_METADATA
579 : #undef VM_SYSCALL_CPI_ACC_INFO_LAMPORTS_RC_REFCELL_VADDR
580 : #undef VM_SYSCALL_CPI_ACC_INFO_DATA_RC_REFCELL_VADDR
581 : #undef VM_SYSCALL_CPI_SET_ACC_INFO_DATA_GET_LEN
|