Line data Source code
1 : #include "fd_vm_syscall.h"
2 : #include "../../runtime/program/fd_vote_program.h"
3 : #include "../../runtime/context/fd_exec_txn_ctx.h"
4 : #include "../../runtime/context/fd_exec_instr_ctx.h"
5 : #include "../../runtime/fd_system_ids.h"
6 : #include "fd_vm_syscall_macros.h"
7 :
8 : /* FIXME: In the original version of this code, there was an FD_TEST
9 : to check if the VM was attached to an instruction context (that
10 : would have crashed anyway because of pointer chasing). If the VM
11 : is being run outside the Solana runtime, it should never invoke
12 : this syscall in the first place. So we treat this as a SIGCALL in
13 : a non-crashing way for the time being. */
14 :
15 : int
16 : fd_vm_syscall_sol_get_clock_sysvar( /**/ void * _vm,
17 : /**/ ulong out_vaddr,
18 : FD_PARAM_UNUSED ulong r2,
19 : FD_PARAM_UNUSED ulong r3,
20 : FD_PARAM_UNUSED ulong r4,
21 : FD_PARAM_UNUSED ulong r5,
22 0 : /**/ ulong * _ret ) {
23 0 : fd_vm_t * vm = _vm;
24 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
25 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
26 :
27 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, sizeof(fd_sol_sysvar_clock_t) ) );
28 :
29 0 : if( FD_UNLIKELY( vm->stricter_abi_and_runtime_constraints && out_vaddr>=FD_VM_MEM_MAP_INPUT_REGION_START ) ) {
30 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_POINTER );
31 0 : return FD_VM_ERR_INVAL;
32 0 : }
33 :
34 0 : fd_vm_haddr_query_t var_query = {
35 0 : .vaddr = out_vaddr,
36 0 : .align = FD_VM_ALIGN_RUST_SYSVAR_CLOCK,
37 0 : .sz = sizeof(fd_sol_sysvar_clock_t),
38 0 : .is_slice = 0,
39 0 : };
40 :
41 0 : fd_vm_haddr_query_t * queries[] = { &var_query };
42 0 : FD_VM_TRANSLATE_MUT( vm, queries );
43 :
44 0 : fd_sol_sysvar_clock_t clock = fd_sysvar_cache_clock_read_nofail( instr_ctx->sysvar_cache );
45 0 : memcpy( var_query.haddr, &clock, sizeof(fd_sol_sysvar_clock_t) );
46 :
47 0 : *_ret = 0UL;
48 0 : return FD_VM_SUCCESS;
49 0 : }
50 :
51 : int
52 : fd_vm_syscall_sol_get_epoch_schedule_sysvar( /**/ void * _vm,
53 : /**/ ulong out_vaddr,
54 : FD_PARAM_UNUSED ulong r2,
55 : FD_PARAM_UNUSED ulong r3,
56 : FD_PARAM_UNUSED ulong r4,
57 : FD_PARAM_UNUSED ulong r5,
58 0 : /**/ ulong * _ret ) {
59 0 : fd_vm_t * vm = _vm;
60 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
61 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
62 :
63 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, sizeof(fd_epoch_schedule_t) ) );
64 :
65 0 : if( FD_UNLIKELY( vm->stricter_abi_and_runtime_constraints && out_vaddr>=FD_VM_MEM_MAP_INPUT_REGION_START ) ) {
66 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_POINTER );
67 0 : return FD_VM_ERR_INVAL;
68 0 : }
69 :
70 0 : fd_vm_haddr_query_t var_query = {
71 0 : .vaddr = out_vaddr,
72 0 : .align = FD_VM_ALIGN_RUST_SYSVAR_EPOCH_SCHEDULE,
73 0 : .sz = sizeof(fd_epoch_schedule_t),
74 0 : .is_slice = 0,
75 0 : };
76 :
77 0 : fd_vm_haddr_query_t * queries[] = { &var_query };
78 0 : FD_VM_TRANSLATE_MUT( vm, queries );
79 :
80 0 : fd_epoch_schedule_t schedule;
81 0 : if( FD_UNLIKELY( !fd_sysvar_cache_epoch_schedule_read( instr_ctx->sysvar_cache, &schedule ) ) ) {
82 0 : FD_TXN_ERR_FOR_LOG_INSTR( vm->instr_ctx->txn_ctx, FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR, vm->instr_ctx->txn_ctx->instr_err_idx );
83 0 : return FD_VM_ERR_INVAL;
84 0 : }
85 0 : memcpy( var_query.haddr, &schedule, sizeof(fd_epoch_schedule_t) );
86 :
87 0 : *_ret = 0UL;
88 0 : return FD_VM_SUCCESS;
89 0 : }
90 :
91 : int
92 : fd_vm_syscall_sol_get_rent_sysvar( /**/ void * _vm,
93 : /**/ ulong out_vaddr,
94 : FD_PARAM_UNUSED ulong r2,
95 : FD_PARAM_UNUSED ulong r3,
96 : FD_PARAM_UNUSED ulong r4,
97 : FD_PARAM_UNUSED ulong r5,
98 0 : /**/ ulong * _ret ) {
99 0 : fd_vm_t * vm = _vm;
100 :
101 : /* Unreachable in a real SVM, used for testing */
102 :
103 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
104 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
105 :
106 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, sizeof(fd_rent_t) ) );
107 :
108 0 : if( FD_UNLIKELY( vm->stricter_abi_and_runtime_constraints && out_vaddr>=FD_VM_MEM_MAP_INPUT_REGION_START ) ) {
109 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_POINTER );
110 0 : return FD_VM_ERR_INVAL;
111 0 : }
112 :
113 0 : fd_vm_haddr_query_t var_query = {
114 0 : .vaddr = out_vaddr,
115 0 : .align = FD_VM_ALIGN_RUST_SYSVAR_RENT,
116 0 : .sz = sizeof(fd_rent_t),
117 0 : .is_slice = 0,
118 0 : };
119 :
120 0 : fd_vm_haddr_query_t * queries[] = { &var_query };
121 0 : FD_VM_TRANSLATE_MUT( vm, queries );
122 :
123 0 : fd_rent_t rent = fd_sysvar_cache_rent_read_nofail( instr_ctx->sysvar_cache );
124 0 : memcpy( var_query.haddr, &rent, sizeof(fd_rent_t) );
125 :
126 0 : *_ret = 0UL;
127 0 : return FD_VM_SUCCESS;
128 0 : }
129 :
130 : /* https://github.com/anza-xyz/agave/blob/v2.3.2/programs/bpf_loader/src/syscalls/sysvar.rs#L149 */
131 : int
132 : fd_vm_syscall_sol_get_last_restart_slot_sysvar( /**/ void * _vm,
133 : /**/ ulong out_vaddr,
134 : FD_PARAM_UNUSED ulong r2,
135 : FD_PARAM_UNUSED ulong r3,
136 : FD_PARAM_UNUSED ulong r4,
137 : FD_PARAM_UNUSED ulong r5,
138 0 : /**/ ulong * _ret ) {
139 0 : fd_vm_t * vm = _vm;
140 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
141 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
142 :
143 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, sizeof(fd_sol_sysvar_last_restart_slot_t) ) );
144 :
145 0 : if( FD_UNLIKELY( vm->stricter_abi_and_runtime_constraints && out_vaddr>=FD_VM_MEM_MAP_INPUT_REGION_START ) ) {
146 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_POINTER );
147 0 : return FD_VM_ERR_INVAL;
148 0 : }
149 :
150 0 : fd_vm_haddr_query_t var_query = {
151 0 : .vaddr = out_vaddr,
152 0 : .align = FD_VM_ALIGN_RUST_SYSVAR_LAST_RESTART_SLOT,
153 0 : .sz = sizeof(fd_sol_sysvar_last_restart_slot_t),
154 0 : .is_slice = 0,
155 0 : };
156 :
157 0 : fd_vm_haddr_query_t * queries[] = { &var_query };
158 0 : FD_VM_TRANSLATE_MUT( vm, queries );
159 :
160 0 : fd_sol_sysvar_last_restart_slot_t last_restart_slot;
161 0 : if( FD_UNLIKELY( !fd_sysvar_cache_last_restart_slot_read( vm->instr_ctx->sysvar_cache, &last_restart_slot ) ) ) {
162 0 : FD_TXN_ERR_FOR_LOG_INSTR( vm->instr_ctx->txn_ctx, FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR, vm->instr_ctx->txn_ctx->instr_err_idx );
163 0 : return FD_VM_ERR_INVAL;
164 0 : }
165 :
166 0 : memcpy( var_query.haddr, &last_restart_slot, sizeof(fd_sol_sysvar_last_restart_slot_t) );
167 :
168 0 : *_ret = 0UL;
169 0 : return FD_VM_SUCCESS;
170 0 : }
171 :
172 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L167-L232 */
173 : int
174 : fd_vm_syscall_sol_get_sysvar( /**/ void * _vm,
175 : /**/ ulong sysvar_id_vaddr,
176 : /**/ ulong out_vaddr,
177 : /**/ ulong offset,
178 : /**/ ulong sz,
179 : FD_PARAM_UNUSED ulong r5,
180 0 : /**/ ulong * _ret ) {
181 0 : fd_vm_t * vm = _vm;
182 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
183 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
184 :
185 : /* sysvar_id_cost seems to just always be 32 / 250 = 0...
186 : https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L190-L197 */
187 0 : ulong sysvar_buf_cost = sz / FD_VM_CPI_BYTES_PER_UNIT;
188 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, fd_ulong_max( sysvar_buf_cost, FD_VM_MEM_OP_BASE_COST ) ) );
189 :
190 0 : if( FD_UNLIKELY( vm->stricter_abi_and_runtime_constraints && out_vaddr>=FD_VM_MEM_MAP_INPUT_REGION_START ) ) {
191 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_POINTER );
192 0 : return FD_VM_ERR_INVAL;
193 0 : }
194 :
195 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/sysvar.rs#L207-L211 */
196 0 : fd_vm_haddr_query_t var_query = {
197 0 : .vaddr = out_vaddr,
198 0 : .align = FD_VM_ALIGN_RUST_U8,
199 0 : .sz = sz,
200 0 : .is_slice = 1,
201 0 : };
202 :
203 0 : fd_vm_haddr_query_t * queries[] = { &var_query };
204 0 : FD_VM_TRANSLATE_MUT( vm, queries );
205 :
206 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L199-L200 */
207 0 : const fd_pubkey_t * sysvar_id = FD_VM_MEM_HADDR_LD( vm, sysvar_id_vaddr, FD_VM_ALIGN_RUST_PUBKEY, FD_PUBKEY_FOOTPRINT );
208 :
209 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L205-L208 */
210 0 : ulong offset_length;
211 0 : int err = fd_int_if( __builtin_uaddl_overflow( offset, sz, &offset_length ), FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW, FD_EXECUTOR_INSTR_SUCCESS );
212 0 : if( FD_UNLIKELY( err ) ) {
213 0 : FD_VM_ERR_FOR_LOG_INSTR( vm, err );
214 0 : return FD_VM_SYSCALL_ERR_ABORT;
215 0 : }
216 :
217 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L210-L213
218 : We don't need this, we already checked we can store in out_vaddr with requested sz. */
219 :
220 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L215-L221 */
221 0 : if( FD_UNLIKELY( memcmp( sysvar_id->uc, fd_sysvar_clock_id.uc, FD_PUBKEY_FOOTPRINT ) &&
222 0 : memcmp( sysvar_id->uc, fd_sysvar_epoch_schedule_id.uc, FD_PUBKEY_FOOTPRINT ) &&
223 0 : memcmp( sysvar_id->uc, fd_sysvar_epoch_rewards_id.uc, FD_PUBKEY_FOOTPRINT ) &&
224 0 : memcmp( sysvar_id->uc, fd_sysvar_rent_id.uc, FD_PUBKEY_FOOTPRINT ) &&
225 0 : memcmp( sysvar_id->uc, fd_sysvar_slot_hashes_id.uc, FD_PUBKEY_FOOTPRINT ) &&
226 0 : memcmp( sysvar_id->uc, fd_sysvar_stake_history_id.uc, FD_PUBKEY_FOOTPRINT ) &&
227 0 : memcmp( sysvar_id->uc, fd_sysvar_last_restart_slot_id.uc, FD_PUBKEY_FOOTPRINT ) ) ) {
228 0 : *_ret = 2UL;
229 0 : return FD_VM_SUCCESS;
230 0 : }
231 :
232 0 : ulong sysvar_buf_len;
233 0 : uchar const * sysvar_buf =
234 0 : fd_sysvar_cache_data_query( vm->instr_ctx->sysvar_cache, sysvar_id, &sysvar_buf_len );
235 0 : if( FD_UNLIKELY( !sysvar_buf ) ) {
236 0 : *_ret = 2UL;
237 0 : return FD_VM_SUCCESS;
238 0 : }
239 :
240 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L223-L228
241 : Note the length check is at the very end to fail after performing sufficient checks. */
242 :
243 0 : if( FD_UNLIKELY( offset_length>sysvar_buf_len ) ) {
244 0 : *_ret = 1UL;
245 0 : return FD_VM_SUCCESS;
246 0 : }
247 :
248 0 : if( FD_UNLIKELY( sz==0UL ) ) {
249 0 : *_ret = 0UL;
250 0 : return FD_VM_SUCCESS;
251 0 : }
252 :
253 0 : fd_memcpy( var_query.haddr, sysvar_buf + offset, sz );
254 0 : *_ret = 0;
255 0 : return FD_VM_SUCCESS;
256 0 : }
257 :
258 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2043-L2118 */
259 : int
260 : fd_vm_syscall_sol_get_epoch_stake( /**/ void * _vm,
261 : /**/ ulong var_addr,
262 : FD_PARAM_UNUSED ulong r2,
263 : FD_PARAM_UNUSED ulong r3,
264 : FD_PARAM_UNUSED ulong r4,
265 : FD_PARAM_UNUSED ulong r5,
266 0 : /**/ ulong * _ret ) {
267 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
268 :
269 : /* Var addr of 0 returns the total active stake on the cluster.
270 :
271 : https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2057-L2075 */
272 0 : if( FD_UNLIKELY( var_addr==0UL ) ) {
273 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2065-L2066 */
274 0 : FD_VM_CU_UPDATE( vm, FD_VM_SYSCALL_BASE_COST );
275 :
276 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2074 */
277 0 : *_ret = fd_bank_total_epoch_stake_get( vm->instr_ctx->txn_ctx->bank );
278 0 : return FD_VM_SUCCESS;
279 0 : }
280 :
281 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2083-L2091
282 : FD_PUBKEY_FOOTPRINT/FD_VM_CPI_BYTES_PER_UNIT is always 32/250 = 0,
283 : so we can omit it */
284 :
285 0 : FD_VM_CU_UPDATE( vm, FD_VM_MEM_OP_BASE_COST + FD_VM_SYSCALL_BASE_COST );
286 :
287 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2103-L2104 */
288 0 : const fd_pubkey_t * vote_address = FD_VM_MEM_HADDR_LD( vm, var_addr, FD_VM_ALIGN_RUST_PUBKEY, FD_PUBKEY_FOOTPRINT );
289 :
290 : /* https://github.com/anza-xyz/agave/blob/v2.2.14/runtime/src/bank.rs#L6954 */
291 0 : fd_vote_states_t const * vote_states = fd_bank_vote_states_prev_locking_query( vm->instr_ctx->txn_ctx->bank );
292 0 : fd_vote_state_ele_t const * vote_state_ele = fd_vote_states_query_const( vote_states, vote_address );
293 0 : *_ret = vote_state_ele ? vote_state_ele->stake : 0UL;
294 0 : fd_bank_vote_states_prev_end_locking_query( vm->instr_ctx->txn_ctx->bank );
295 :
296 0 : return FD_VM_SUCCESS;
297 0 : }
298 :
299 : int
300 : fd_vm_syscall_sol_get_stack_height( /**/ void * _vm,
301 : FD_PARAM_UNUSED ulong r1,
302 : FD_PARAM_UNUSED ulong r2,
303 : FD_PARAM_UNUSED ulong r3,
304 : FD_PARAM_UNUSED ulong r4,
305 : FD_PARAM_UNUSED ulong r5,
306 0 : /**/ ulong * _ret ) {
307 : /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mod.rs#L1547 */
308 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
309 :
310 0 : FD_VM_CU_UPDATE( vm, FD_VM_SYSCALL_BASE_COST );
311 :
312 0 : *_ret = vm->instr_ctx->txn_ctx->instr_stack_sz;
313 0 : return FD_VM_SUCCESS;
314 0 : }
315 :
316 : int
317 : fd_vm_syscall_sol_get_return_data( /**/ void * _vm,
318 : /**/ ulong return_data_vaddr,
319 : /**/ ulong sz,
320 : /**/ ulong program_id_vaddr,
321 : FD_PARAM_UNUSED ulong r4,
322 : FD_PARAM_UNUSED ulong r5,
323 0 : /**/ ulong * _ret ) {
324 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
325 :
326 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1465 */
327 0 : FD_VM_CU_UPDATE( vm, FD_VM_SYSCALL_BASE_COST );
328 :
329 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1467 */
330 0 : fd_txn_return_data_t const * return_data = &vm->instr_ctx->txn_ctx->return_data;
331 :
332 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1468 */
333 0 : ulong length = fd_ulong_min( return_data->len, sz );
334 :
335 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1469-L1492 */
336 0 : if( FD_LIKELY( length ) ) {
337 :
338 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1470-L1474 */
339 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( length, sizeof(fd_pubkey_t) ) / FD_VM_CPI_BYTES_PER_UNIT );
340 :
341 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1476-L1481 */
342 0 : fd_vm_haddr_query_t return_data_query = {
343 0 : .vaddr = return_data_vaddr,
344 0 : .align = FD_VM_ALIGN_RUST_U8,
345 0 : .sz = length,
346 0 : .is_slice = 1
347 0 : };
348 :
349 0 : fd_vm_haddr_query_t program_id_query = {
350 0 : .vaddr = program_id_vaddr,
351 0 : .align = FD_VM_ALIGN_RUST_PUBKEY,
352 0 : .sz = sizeof(fd_pubkey_t),
353 0 : .is_slice = 0
354 0 : };
355 :
356 0 : fd_vm_haddr_query_t * queries[] = { &return_data_query, &program_id_query };
357 0 : FD_VM_TRANSLATE_MUT( vm, queries );
358 :
359 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1490-L1491 */
360 0 : memcpy( return_data_query.haddr, return_data->data, length );
361 0 : memcpy( program_id_query.haddr, &return_data->program_id, sizeof(fd_pubkey_t) );
362 0 : }
363 :
364 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1495 */
365 0 : *_ret = return_data->len;
366 0 : return FD_VM_SUCCESS;
367 0 : }
368 :
369 : int
370 : fd_vm_syscall_sol_set_return_data( /**/ void * _vm,
371 : /**/ ulong src_vaddr,
372 : /**/ ulong src_sz,
373 : FD_PARAM_UNUSED ulong r3,
374 : FD_PARAM_UNUSED ulong r4,
375 : FD_PARAM_UNUSED ulong r5,
376 0 : /**/ ulong * _ret ) {
377 : /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mod.rs#L1297 */
378 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
379 :
380 : /* In the original version of this code, there was an FD_TEST
381 : to check if the VM was attached to an instruction context (that
382 : would have crashed anyway because of pointer chasing). If the VM
383 : is being run outside the Solana runtime, it should never invoke
384 : this syscall in the first place. So we treat this as a SIGCALL in
385 : a non-crashing way for the time being. */
386 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
387 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
388 :
389 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSCALL_BASE_COST, src_sz / FD_VM_CPI_BYTES_PER_UNIT ) );
390 :
391 : /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mod.rs#L1316 */
392 0 : if( FD_UNLIKELY( src_sz>FD_VM_RETURN_DATA_MAX ) ) {
393 : /* TODO: this is a bit annoying, we may want to unify return codes...
394 : - FD_VM_SYSCALL_ERR_RETURN_DATA_TOO_LARGE is Agave's return code,
395 : also used for logging */
396 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_RETURN_DATA_TOO_LARGE );
397 0 : return FD_VM_SYSCALL_ERR_RETURN_DATA_TOO_LARGE;
398 0 : }
399 :
400 : /* src_sz == 0 is ok */
401 0 : void const * src = FD_VM_MEM_SLICE_HADDR_LD( vm, src_vaddr, FD_VM_ALIGN_RUST_U8, src_sz );
402 :
403 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/programs/bpf_loader/src/syscalls/mod.rs#L1480-L1484 */
404 0 : fd_pubkey_t const * program_id = NULL;
405 0 : int err = fd_exec_instr_ctx_get_last_program_key( vm->instr_ctx, &program_id );
406 0 : if( FD_UNLIKELY( err ) ) {
407 0 : FD_VM_ERR_FOR_LOG_INSTR( vm, err );
408 0 : return err;
409 0 : }
410 :
411 0 : fd_txn_return_data_t * return_data = &instr_ctx->txn_ctx->return_data;
412 :
413 0 : return_data->len = src_sz;
414 0 : if( FD_LIKELY( src_sz!=0UL ) ) {
415 0 : fd_memcpy( return_data->data, src, src_sz );
416 0 : }
417 0 : return_data->program_id = *program_id;
418 :
419 0 : *_ret = 0;
420 0 : return FD_VM_SUCCESS;
421 0 : }
422 :
423 : /* Used to query and convey information about the sibling instruction
424 : https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/sdk/program/src/instruction.rs#L676
425 :
426 : */
427 : struct fd_vm_syscall_processed_sibling_instruction {
428 : /* Length of the instruction data */
429 : ulong data_len;
430 : /* Number of accounts */
431 : ulong accounts_len;
432 : };
433 : typedef struct fd_vm_syscall_processed_sibling_instruction fd_vm_syscall_processed_sibling_instruction_t;
434 :
435 0 : #define FD_VM_SYSCALL_PROCESSED_SIBLING_INSTRUCTION_SIZE (16UL)
436 0 : #define FD_VM_SYSCALL_PROCESSED_SIBLING_INSTRUCTION_ALIGN (8UL )
437 :
438 : /* https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/programs/bpf_loader/src/syscalls/mod.rs#L1402 */
439 : int
440 : fd_vm_syscall_sol_get_processed_sibling_instruction(
441 : void * _vm,
442 : ulong index,
443 : ulong result_meta_vaddr,
444 : ulong result_program_id_vaddr,
445 : ulong result_data_vaddr,
446 : ulong result_accounts_vaddr,
447 : ulong * _ret
448 0 : ) {
449 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
450 :
451 : /* Consume base compute cost
452 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1513 */
453 0 : FD_VM_CU_UPDATE( vm, FD_VM_SYSCALL_BASE_COST );
454 :
455 : /* Get the current instruction stack height. This value is 1-indexed
456 : (top level instruction has a stack height of 1).
457 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1517 */
458 0 : ulong stack_height = vm->instr_ctx->txn_ctx->instr_stack_sz;
459 :
460 : /* Reverse iterate through the instruction trace, ignoring anything except instructions on the same level.
461 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1518-L1522 */
462 0 : ulong instruction_trace_length = vm->instr_ctx->txn_ctx->instr_trace_length;
463 0 : ulong reverse_index_at_stack_height = 0UL;
464 0 : fd_exec_instr_trace_entry_t * found_instruction_context = NULL;
465 0 : for( ulong index_in_trace=instruction_trace_length; index_in_trace>0UL; index_in_trace-- ) {
466 :
467 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1524-L1526
468 : This error can never happen */
469 :
470 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1527-L1529 */
471 0 : fd_exec_instr_trace_entry_t * instruction_context = &vm->instr_ctx->txn_ctx->instr_trace[ index_in_trace-1UL ];
472 0 : if( FD_LIKELY( instruction_context->stack_height<stack_height ) ) {
473 0 : break;
474 0 : }
475 :
476 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1530-L1536 */
477 0 : if( FD_UNLIKELY( instruction_context->stack_height==stack_height ) ) {
478 0 : if( FD_UNLIKELY( fd_ulong_sat_add( index, 1UL )==reverse_index_at_stack_height ) ) {
479 0 : found_instruction_context = instruction_context;
480 0 : break;
481 0 : }
482 0 : reverse_index_at_stack_height = fd_ulong_sat_add( reverse_index_at_stack_height, 1UL );
483 0 : }
484 0 : }
485 :
486 : /* If we have found an entry, then copy the instruction into the
487 : result addresses.
488 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1539-L1588
489 : */
490 0 : if( FD_LIKELY( found_instruction_context != NULL ) ) {
491 0 : fd_instr_info_t * instr_info = found_instruction_context->instr_info;
492 :
493 0 : fd_vm_haddr_query_t result_header_query = {
494 0 : .vaddr = result_meta_vaddr,
495 0 : .align = FD_VM_SYSCALL_PROCESSED_SIBLING_INSTRUCTION_ALIGN,
496 0 : .sz = FD_VM_SYSCALL_PROCESSED_SIBLING_INSTRUCTION_SIZE,
497 0 : .is_slice = 0,
498 0 : };
499 :
500 0 : fd_vm_haddr_query_t * queries[] = { &result_header_query };
501 0 : FD_VM_TRANSLATE_MUT( vm, queries );
502 :
503 0 : fd_vm_syscall_processed_sibling_instruction_t * result_header = result_header_query.haddr;
504 :
505 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1546-L1583 */
506 0 : if( result_header->data_len==instr_info->data_sz && result_header->accounts_len==instr_info->acct_cnt ) {
507 0 : fd_vm_haddr_query_t program_id_query = {
508 0 : .vaddr = result_program_id_vaddr,
509 0 : .align = FD_VM_ALIGN_RUST_PUBKEY,
510 0 : .sz = sizeof(fd_pubkey_t),
511 0 : .is_slice = 0,
512 0 : };
513 :
514 0 : fd_vm_haddr_query_t data_query = {
515 0 : .vaddr = result_data_vaddr,
516 0 : .align = FD_VM_ALIGN_RUST_U8,
517 0 : .sz = result_header->data_len,
518 0 : .is_slice = 1,
519 0 : };
520 :
521 0 : fd_vm_haddr_query_t accounts_query = {
522 0 : .vaddr = result_accounts_vaddr,
523 0 : .align = FD_VM_RUST_ACCOUNT_META_ALIGN,
524 0 : .sz = fd_ulong_sat_mul( result_header->accounts_len, FD_VM_RUST_ACCOUNT_META_SIZE ),
525 0 : .is_slice = 1,
526 0 : };
527 :
528 0 : fd_vm_haddr_query_t * queries[] = { &program_id_query, &data_query, &accounts_query, &result_header_query };
529 0 : FD_VM_TRANSLATE_MUT( vm, queries );
530 :
531 0 : fd_pubkey_t * program_id = program_id_query.haddr;
532 0 : uchar * data = data_query.haddr;
533 0 : fd_vm_rust_account_meta_t * accounts = accounts_query.haddr;
534 :
535 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1561-L1562 */
536 0 : fd_pubkey_t const * instr_ctx_program_id = NULL;
537 0 : int err = fd_exec_txn_ctx_get_key_of_account_at_index( vm->instr_ctx->txn_ctx,
538 0 : instr_info->program_id,
539 0 : &instr_ctx_program_id );
540 0 : if( FD_UNLIKELY( err ) ) {
541 0 : FD_VM_ERR_FOR_LOG_INSTR( vm, err );
542 0 : return err;
543 0 : }
544 0 : fd_memcpy( program_id, instr_ctx_program_id, sizeof(fd_pubkey_t) );
545 :
546 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1563 */
547 0 : fd_memcpy( data, instr_info->data, instr_info->data_sz );
548 :
549 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1564-L1581 */
550 0 : for( ushort i=0; i<instr_info->acct_cnt; i++ ) {
551 0 : fd_pubkey_t const * account_key;
552 0 : ushort txn_idx = instr_info->accounts[ i ].index_in_transaction;
553 0 : err = fd_exec_txn_ctx_get_key_of_account_at_index( vm->instr_ctx->txn_ctx, txn_idx, &account_key );
554 0 : if( FD_UNLIKELY( err ) ) {
555 0 : FD_VM_ERR_FOR_LOG_INSTR( vm, err );
556 0 : return err;
557 0 : }
558 :
559 0 : fd_memcpy( accounts[ i ].pubkey, account_key, sizeof(fd_pubkey_t) );
560 0 : accounts[ i ].is_signer = !!(instr_info->accounts[ i ].is_signer );
561 0 : accounts[ i ].is_writable = !!(instr_info->accounts[ i ].is_writable );
562 0 : }
563 0 : } else {
564 : /* Copy the actual metadata into the result meta struct
565 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1584-L1586 */
566 0 : result_header->data_len = instr_info->data_sz;
567 0 : result_header->accounts_len = instr_info->acct_cnt;
568 0 : }
569 :
570 : /* Return true as we found a sibling instruction
571 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1588 */
572 0 : *_ret = 1UL;
573 0 : return FD_VM_SUCCESS;
574 0 : }
575 :
576 : /* Return false if we didn't find a sibling instruction
577 : https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/mod.rs#L1590 */
578 0 : *_ret = 0UL;
579 0 : return FD_VM_SUCCESS;
580 0 : }
581 :
582 : /* https://github.com/anza-xyz/agave/blob/v2.3.1/programs/bpf_loader/src/syscalls/sysvar.rs#L80 */
583 : int
584 : fd_vm_syscall_sol_get_epoch_rewards_sysvar( /**/ void * _vm,
585 : /**/ ulong out_vaddr,
586 : FD_PARAM_UNUSED ulong r2,
587 : FD_PARAM_UNUSED ulong r3,
588 : FD_PARAM_UNUSED ulong r4,
589 : FD_PARAM_UNUSED ulong r5,
590 0 : /**/ ulong * _ret ) {
591 0 : fd_vm_t * vm = _vm;
592 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
593 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
594 :
595 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, sizeof(fd_sysvar_epoch_rewards_t) ) );
596 :
597 0 : if( FD_UNLIKELY( vm->stricter_abi_and_runtime_constraints && out_vaddr>=FD_VM_MEM_MAP_INPUT_REGION_START ) ) {
598 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_INVALID_POINTER );
599 0 : return FD_VM_ERR_INVAL;
600 0 : }
601 :
602 0 : uchar * out = FD_VM_MEM_HADDR_ST( vm, out_vaddr, FD_VM_ALIGN_RUST_SYSVAR_EPOCH_REWARDS, sizeof(fd_sysvar_epoch_rewards_t) );
603 :
604 0 : fd_sysvar_epoch_rewards_t epoch_rewards;
605 0 : if( FD_UNLIKELY( !fd_sysvar_cache_epoch_rewards_read( instr_ctx->sysvar_cache, &epoch_rewards ) ) ) {
606 0 : FD_TXN_ERR_FOR_LOG_INSTR( vm->instr_ctx->txn_ctx, FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR, vm->instr_ctx->txn_ctx->instr_err_idx );
607 0 : return FD_VM_ERR_INVAL;
608 0 : }
609 0 : memcpy( out, &epoch_rewards, sizeof(fd_sysvar_epoch_rewards_t) );
610 0 : memset( out+81, 0, 7 ); /* padding */
611 :
612 0 : *_ret = 0UL;
613 0 : return FD_VM_SUCCESS;
614 0 : }
|