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