Line data Source code
1 : #include "fd_vm_syscall.h"
2 : #include "../../runtime/program/fd_vote_program.h"
3 : #include "../../runtime/sysvar/fd_sysvar.h"
4 : #include "../../runtime/sysvar/fd_sysvar_clock.h"
5 : #include "../../runtime/sysvar/fd_sysvar_epoch_rewards.h"
6 : #include "../../runtime/sysvar/fd_sysvar_epoch_schedule.h"
7 : #include "../../runtime/sysvar/fd_sysvar_fees.h"
8 : #include "../../runtime/sysvar/fd_sysvar_rent.h"
9 : #include "../../runtime/sysvar/fd_sysvar_last_restart_slot.h"
10 : #include "../../runtime/context/fd_exec_txn_ctx.h"
11 : #include "../../runtime/context/fd_exec_instr_ctx.h"
12 : #include "../../runtime/fd_system_ids.h"
13 :
14 : int
15 : fd_vm_syscall_sol_get_clock_sysvar( /**/ void * _vm,
16 : /**/ ulong out_vaddr,
17 : FD_PARAM_UNUSED ulong r2,
18 : FD_PARAM_UNUSED ulong r3,
19 : FD_PARAM_UNUSED ulong r4,
20 : FD_PARAM_UNUSED ulong r5,
21 0 : /**/ ulong * _ret ) {
22 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
23 :
24 : /* FIXME: In the original version of this code, there was an FD_TEST
25 : to check if the VM was attached to an instruction context (that
26 : would have crashed anyway because of pointer chasing). If the VM
27 : is being run outside the Solana runtime, it should never invoke
28 : this syscall in the first place. So we treat this as a SIGCALL in
29 : a non-crashing way. */
30 :
31 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
32 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
33 :
34 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, FD_SOL_SYSVAR_CLOCK_FOOTPRINT ) );
35 :
36 0 : void * out = FD_VM_MEM_HADDR_ST( vm, out_vaddr, FD_VM_ALIGN_RUST_SYSVAR_CLOCK, FD_SOL_SYSVAR_CLOCK_FOOTPRINT );
37 :
38 : /* FIXME: is it possible to do the read in-place? */
39 0 : fd_sol_sysvar_clock_t clock[1];
40 0 : fd_sol_sysvar_clock_new( clock ); /* FIXME: probably should be init as not a distributed persistent object */
41 0 : fd_sysvar_clock_read( clock,
42 0 : instr_ctx->txn_ctx->sysvar_cache,
43 0 : instr_ctx->txn_ctx->acc_mgr,
44 0 : instr_ctx->txn_ctx->funk_txn );
45 : /* FIXME: no delete function to match new (probably should be fini for the same reason anyway) */
46 :
47 0 : memcpy( out, clock, FD_SOL_SYSVAR_CLOCK_FOOTPRINT );
48 :
49 0 : *_ret = 0UL;
50 0 : return FD_VM_SUCCESS;
51 0 : }
52 :
53 : int
54 : fd_vm_syscall_sol_get_epoch_schedule_sysvar( /**/ void * _vm,
55 : /**/ ulong out_vaddr,
56 : FD_PARAM_UNUSED ulong r2,
57 : FD_PARAM_UNUSED ulong r3,
58 : FD_PARAM_UNUSED ulong r4,
59 : FD_PARAM_UNUSED ulong r5,
60 0 : /**/ ulong * _ret ) {
61 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
62 :
63 : /* FIXME: In the original version of this code, there was an FD_TEST
64 : to check if the VM was attached to an instruction context (that
65 : would have crashed anyway because of pointer chasing). If the VM
66 : is being run outside the Solana runtime, it should never invoke
67 : this syscall in the first place. So we treat this as a SIGCALL in
68 : a non-crashing way for the time being. */
69 :
70 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
71 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
72 :
73 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, FD_EPOCH_SCHEDULE_FOOTPRINT ) );
74 :
75 0 : void * out = FD_VM_MEM_HADDR_ST( vm, out_vaddr, FD_VM_ALIGN_RUST_SYSVAR_EPOCH_SCHEDULE, FD_EPOCH_SCHEDULE_FOOTPRINT );
76 :
77 : /* FIXME: is it possible to do the read in-place? */
78 0 : fd_epoch_schedule_t schedule[1];
79 0 : fd_epoch_schedule_new( schedule ); /* FIXME: probably should be init as not a distributed persistent object */
80 0 : fd_sysvar_epoch_schedule_read( schedule,
81 0 : instr_ctx->txn_ctx->sysvar_cache,
82 0 : instr_ctx->txn_ctx->acc_mgr,
83 0 : instr_ctx->txn_ctx->funk_txn );
84 : /* FIXME: no delete function to match new (probably should be fini for the same reason anyway) */
85 :
86 0 : memcpy( out, schedule, FD_EPOCH_SCHEDULE_FOOTPRINT );
87 :
88 0 : *_ret = 0UL;
89 0 : return FD_VM_SUCCESS;
90 0 : }
91 :
92 : int
93 : fd_vm_syscall_sol_get_fees_sysvar( /**/ void * _vm,
94 : /**/ ulong out_vaddr,
95 : FD_PARAM_UNUSED ulong r2,
96 : FD_PARAM_UNUSED ulong r3,
97 : FD_PARAM_UNUSED ulong r4,
98 : FD_PARAM_UNUSED ulong r5,
99 0 : /**/ ulong * _ret ) {
100 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
101 :
102 : /* FIXME: In the original version of this code, there was an FD_TEST
103 : to check if the VM was attached to an instruction context (that
104 : would have crashed anyway because of pointer chasing). If the VM
105 : is being run outside the Solana runtime, it should never invoke
106 : this syscall in the first place. So we treat this as a SIGCALL in
107 : a non-crashing way for the time being. */
108 :
109 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
110 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
111 :
112 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, FD_SYSVAR_FEES_FOOTPRINT ) );
113 :
114 0 : void * out = FD_VM_MEM_HADDR_ST( vm, out_vaddr, FD_VM_ALIGN_RUST_SYSVAR_FEES, FD_SYSVAR_FEES_FOOTPRINT );
115 :
116 : /* FIXME: is it possible to do the read in-place? */
117 0 : fd_sysvar_fees_t fees[1];
118 0 : fd_sysvar_fees_new( fees ); /* FIXME: probably should be init as not a distributed persistent object */
119 0 : fd_sysvar_fees_read( fees,
120 0 : instr_ctx->txn_ctx->sysvar_cache,
121 0 : instr_ctx->txn_ctx->acc_mgr,
122 0 : instr_ctx->txn_ctx->funk_txn );
123 : /* FIXME: no delete function to match new (probably should be fini for the same reason anyway) */
124 :
125 0 : memcpy( out, fees, FD_SYSVAR_FEES_FOOTPRINT );
126 :
127 0 : *_ret = 0UL;
128 0 : return FD_VM_SUCCESS;
129 0 : }
130 :
131 : int
132 : fd_vm_syscall_sol_get_rent_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 = (fd_vm_t *)_vm;
140 :
141 : /* FIXME: In the original version of this code, there was an FD_TEST
142 : to check if the VM was attached to an instruction context (that
143 : would have crashed anyway because of pointer chasing). If the VM
144 : is being run outside the Solana runtime, it should never invoke
145 : this syscall in the first place. So we treat this as a SIGCALL in
146 : a non-crashing way for the time being. */
147 :
148 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
149 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
150 :
151 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, FD_RENT_FOOTPRINT ) );
152 :
153 0 : void * out = FD_VM_MEM_HADDR_ST( vm, out_vaddr, FD_VM_ALIGN_RUST_SYSVAR_RENT, FD_RENT_FOOTPRINT );
154 :
155 : /* FIXME: is it possible to do the read in-place? */
156 0 : fd_rent_t * rent = fd_sysvar_rent_read( instr_ctx->txn_ctx->sysvar_cache,
157 0 : instr_ctx->txn_ctx->acc_mgr,
158 0 : instr_ctx->txn_ctx->funk_txn,
159 0 : instr_ctx->txn_ctx->spad );
160 : /* FIXME: no delete function to match new (probably should be fini for the same reason anyway) */
161 :
162 0 : memcpy( out, rent, FD_RENT_FOOTPRINT );
163 :
164 0 : *_ret = 0UL;
165 0 : return FD_VM_SUCCESS;
166 0 : }
167 :
168 : /* https://github.com/anza-xyz/agave/blob/36323b6dcd3e29e4d6fe6d73d716a3f33927148b/programs/bpf_loader/src/syscalls/sysvar.rs#L144 */
169 : int
170 : fd_vm_syscall_sol_get_last_restart_slot_sysvar( /**/ void * _vm,
171 : /**/ ulong out_vaddr,
172 : FD_PARAM_UNUSED ulong r2,
173 : FD_PARAM_UNUSED ulong r3,
174 : FD_PARAM_UNUSED ulong r4,
175 : FD_PARAM_UNUSED ulong r5,
176 0 : /**/ ulong * _ret ) {
177 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
178 :
179 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, FD_SOL_SYSVAR_LAST_RESTART_SLOT_FOOTPRINT ) );
180 :
181 0 : fd_sol_sysvar_last_restart_slot_t * out =
182 0 : FD_VM_MEM_HADDR_ST( vm, out_vaddr, FD_VM_ALIGN_RUST_SYSVAR_LAST_RESTART_SLOT, FD_SOL_SYSVAR_LAST_RESTART_SLOT_FOOTPRINT );
183 0 : if( FD_UNLIKELY( fd_sysvar_last_restart_slot_read( out,
184 0 : vm->instr_ctx->txn_ctx->sysvar_cache,
185 0 : vm->instr_ctx->txn_ctx->acc_mgr,
186 0 : vm->instr_ctx->txn_ctx->funk_txn ) == NULL ) ) {
187 0 : return FD_VM_SYSCALL_ERR_ABORT;
188 0 : }
189 :
190 0 : *_ret = 0UL;
191 0 : return FD_VM_SUCCESS;
192 0 : }
193 :
194 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L167-L232 */
195 : int
196 : fd_vm_syscall_sol_get_sysvar( /**/ void * _vm,
197 : /**/ ulong sysvar_id_vaddr,
198 : /**/ ulong out_vaddr,
199 : /**/ ulong offset,
200 : /**/ ulong sz,
201 : FD_PARAM_UNUSED ulong r5,
202 0 : /**/ ulong * _ret ) {
203 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
204 :
205 : /* sysvar_id_cost seems to just always be 32 / 250 = 0...
206 : https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L190-L197 */
207 0 : ulong sysvar_buf_cost = sz / FD_VM_CPI_BYTES_PER_UNIT;
208 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 ) ) );
209 :
210 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L199-L200 */
211 0 : const fd_pubkey_t * sysvar_id = FD_VM_MEM_HADDR_LD( vm, sysvar_id_vaddr, FD_VM_ALIGN_RUST_PUBKEY, FD_PUBKEY_FOOTPRINT );
212 :
213 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L202-L203 */
214 0 : void * out_haddr = FD_VM_MEM_SLICE_HADDR_ST( vm, out_vaddr, FD_VM_ALIGN_RUST_U8, sz );
215 :
216 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L205-L208 */
217 0 : ulong offset_length;
218 0 : int err = fd_int_if( __builtin_uaddl_overflow( offset, sz, &offset_length ), FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW, FD_EXECUTOR_INSTR_SUCCESS );
219 0 : if( FD_UNLIKELY( err ) ) {
220 0 : FD_VM_ERR_FOR_LOG_INSTR( vm, err );
221 0 : return FD_VM_SYSCALL_ERR_ABORT;
222 0 : }
223 :
224 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L210-L213
225 : We don't need this, we already checked we can store in out_vaddr with requested sz. */
226 :
227 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L215-L221 */
228 0 : if( FD_UNLIKELY( memcmp( sysvar_id->uc, fd_sysvar_clock_id.uc, FD_PUBKEY_FOOTPRINT ) &&
229 0 : memcmp( sysvar_id->uc, fd_sysvar_epoch_schedule_id.uc, FD_PUBKEY_FOOTPRINT ) &&
230 0 : memcmp( sysvar_id->uc, fd_sysvar_epoch_rewards_id.uc, FD_PUBKEY_FOOTPRINT ) &&
231 0 : memcmp( sysvar_id->uc, fd_sysvar_rent_id.uc, FD_PUBKEY_FOOTPRINT ) &&
232 0 : memcmp( sysvar_id->uc, fd_sysvar_slot_hashes_id.uc, FD_PUBKEY_FOOTPRINT ) &&
233 0 : memcmp( sysvar_id->uc, fd_sysvar_stake_history_id.uc, FD_PUBKEY_FOOTPRINT ) &&
234 0 : memcmp( sysvar_id->uc, fd_sysvar_last_restart_slot_id.uc, FD_PUBKEY_FOOTPRINT ) ) ) {
235 0 : *_ret = 2UL;
236 0 : return FD_VM_SUCCESS;
237 0 : }
238 :
239 0 : FD_TXN_ACCOUNT_DECL( sysvar_account );
240 0 : err = fd_acc_mgr_view( vm->instr_ctx->txn_ctx->acc_mgr, vm->instr_ctx->txn_ctx->funk_txn, sysvar_id, sysvar_account );
241 0 : if( FD_UNLIKELY( err ) ) {
242 0 : *_ret = 2UL;
243 0 : return FD_VM_SUCCESS;
244 0 : }
245 :
246 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/sysvar.rs#L223-L228
247 : Note the length check is at the very end to fail after performing sufficient checks. */
248 0 : const uchar * sysvar_buf = sysvar_account->const_data;
249 0 : ulong sysvar_buf_len = sysvar_account->const_meta->dlen;
250 :
251 0 : if( FD_UNLIKELY( offset_length>sysvar_buf_len ) ) {
252 0 : *_ret = 1UL;
253 0 : return FD_VM_SUCCESS;
254 0 : }
255 :
256 0 : if( FD_UNLIKELY( sz==0UL ) ) {
257 0 : *_ret = 0UL;
258 0 : return FD_VM_SUCCESS;
259 0 : }
260 :
261 0 : fd_memcpy( out_haddr, sysvar_buf + offset, sz );
262 0 : *_ret = 0;
263 0 : return FD_VM_SUCCESS;
264 0 : }
265 :
266 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2043-L2118 */
267 : int
268 : fd_vm_syscall_sol_get_epoch_stake( /**/ void * _vm,
269 : /**/ ulong var_addr,
270 : FD_PARAM_UNUSED ulong r2,
271 : FD_PARAM_UNUSED ulong r3,
272 : FD_PARAM_UNUSED ulong r4,
273 : FD_PARAM_UNUSED ulong r5,
274 0 : /**/ ulong * _ret ) {
275 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
276 :
277 : /* Var addr of 0 returns the total active stake on the cluster.
278 :
279 : https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2057-L2075 */
280 0 : if( FD_UNLIKELY( var_addr==0UL ) ) {
281 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2065-L2066 */
282 0 : FD_VM_CU_UPDATE( vm, FD_VM_SYSCALL_BASE_COST );
283 :
284 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2074 */
285 0 : *_ret = vm->instr_ctx->txn_ctx->total_epoch_stake;
286 0 : return FD_VM_SUCCESS;
287 0 : }
288 :
289 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2083-L2091 */
290 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_MEM_OP_BASE_COST,
291 0 : fd_ulong_sat_add( FD_VM_SYSCALL_BASE_COST, FD_PUBKEY_FOOTPRINT / FD_VM_CPI_BYTES_PER_UNIT ) ) );
292 :
293 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/programs/bpf_loader/src/syscalls/mod.rs#L2103-L2104 */
294 0 : const fd_pubkey_t * vote_address = FD_VM_MEM_HADDR_LD( vm, var_addr, FD_VM_ALIGN_RUST_PUBKEY, FD_PUBKEY_FOOTPRINT );
295 0 : *_ret = fd_query_pubkey_stake( vote_address, &vm->instr_ctx->txn_ctx->stakes.vote_accounts );
296 :
297 0 : return FD_VM_SUCCESS;
298 0 : }
299 :
300 : int
301 : fd_vm_syscall_sol_get_stack_height( /**/ void * _vm,
302 : FD_PARAM_UNUSED ulong r1,
303 : FD_PARAM_UNUSED ulong r2,
304 : FD_PARAM_UNUSED ulong r3,
305 : FD_PARAM_UNUSED ulong r4,
306 : FD_PARAM_UNUSED ulong r5,
307 0 : /**/ ulong * _ret ) {
308 : /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mod.rs#L1547 */
309 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
310 :
311 0 : FD_VM_CU_UPDATE( vm, FD_VM_SYSCALL_BASE_COST );
312 :
313 0 : *_ret = vm->instr_ctx->txn_ctx->instr_stack_sz;
314 0 : return FD_VM_SUCCESS;
315 0 : }
316 :
317 : int
318 : fd_vm_syscall_sol_get_return_data( /**/ void * _vm,
319 : /**/ ulong dst_vaddr,
320 : /**/ ulong dst_max,
321 : /**/ ulong program_id_vaddr,
322 : FD_PARAM_UNUSED ulong r4,
323 : FD_PARAM_UNUSED ulong r5,
324 0 : /**/ ulong * _ret ) {
325 : /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mod.rs#L1345 */
326 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
327 :
328 0 : FD_VM_CU_UPDATE( vm, FD_VM_SYSCALL_BASE_COST );
329 :
330 : /* FIXME: In the original version of this code, there was an FD_TEST
331 : to check if the VM was attached to an instruction context (that
332 : would have crashed anyway because of pointer chasing). If the VM
333 : is being run outside the Solana runtime, it should never invoke
334 : this syscall in the first place. So we treat this as a SIGCALL in
335 : a non-crashing way for the time being. */
336 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
337 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
338 :
339 0 : fd_txn_return_data_t const * return_data = &instr_ctx->txn_ctx->return_data;
340 0 : ulong return_data_sz = return_data->len;
341 :
342 0 : ulong cpy_sz = fd_ulong_min( return_data_sz, dst_max );
343 :
344 0 : if( FD_LIKELY( cpy_sz ) ) {
345 :
346 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( cpy_sz, sizeof(fd_pubkey_t) ) / FD_VM_CPI_BYTES_PER_UNIT );
347 :
348 0 : void * dst = FD_VM_MEM_SLICE_HADDR_ST( vm, dst_vaddr, FD_VM_ALIGN_RUST_U8, cpy_sz );
349 :
350 0 : memcpy( dst, return_data->data, cpy_sz );
351 :
352 : /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mod.rs#L1376-L1381
353 : These can never happen. */
354 :
355 0 : void * program_id = FD_VM_MEM_HADDR_ST( vm, program_id_vaddr, FD_VM_ALIGN_RUST_PUBKEY, sizeof(fd_pubkey_t) );
356 :
357 0 : FD_VM_MEM_CHECK_NON_OVERLAPPING( vm, (ulong)dst, cpy_sz, (ulong)program_id, sizeof(fd_pubkey_t) );
358 :
359 0 : memcpy( program_id, &return_data->program_id, sizeof(fd_pubkey_t) );
360 0 : }
361 :
362 0 : *_ret = return_data_sz;
363 0 : return FD_VM_SUCCESS;
364 0 : }
365 :
366 : int
367 : fd_vm_syscall_sol_set_return_data( /**/ void * _vm,
368 : /**/ ulong src_vaddr,
369 : /**/ ulong src_sz,
370 : FD_PARAM_UNUSED ulong r3,
371 : FD_PARAM_UNUSED ulong r4,
372 : FD_PARAM_UNUSED ulong r5,
373 0 : /**/ ulong * _ret ) {
374 : /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mod.rs#L1297 */
375 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
376 :
377 : /* FIXME: In the original version of this code, there was an FD_TEST
378 : to check if the VM was attached to an instruction context (that
379 : would have crashed anyway because of pointer chasing). If the VM
380 : is being run outside the Solana runtime, it should never invoke
381 : this syscall in the first place. So we treat this as a SIGCALL in
382 : a non-crashing way for the time being. */
383 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
384 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
385 :
386 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSCALL_BASE_COST, src_sz / FD_VM_CPI_BYTES_PER_UNIT ) );
387 :
388 : /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mod.rs#L1316 */
389 0 : if( FD_UNLIKELY( src_sz>FD_VM_RETURN_DATA_MAX ) ) {
390 : /* TODO: this is a bit annoying, we may want to unify return codes...
391 : - FD_VM_SYSCALL_ERR_RETURN_DATA_TOO_LARGE is Agave's return code,
392 : also used for logging */
393 0 : FD_VM_ERR_FOR_LOG_SYSCALL( vm, FD_VM_SYSCALL_ERR_RETURN_DATA_TOO_LARGE );
394 0 : return FD_VM_SYSCALL_ERR_RETURN_DATA_TOO_LARGE;
395 0 : }
396 :
397 : /* src_sz == 0 is ok */
398 0 : void const * src = FD_VM_MEM_SLICE_HADDR_LD( vm, src_vaddr, FD_VM_ALIGN_RUST_U8, src_sz );
399 :
400 0 : fd_pubkey_t const * program_id = &instr_ctx->instr->program_id_pubkey;
401 0 : fd_txn_return_data_t * return_data = &instr_ctx->txn_ctx->return_data;
402 :
403 0 : return_data->len = src_sz;
404 0 : if( FD_LIKELY( src_sz!=0UL ) ) {
405 0 : fd_memcpy( return_data->data, src, src_sz );
406 0 : }
407 0 : memcpy( &return_data->program_id, program_id, sizeof(fd_pubkey_t) );
408 :
409 0 : *_ret = 0;
410 0 : return FD_VM_SUCCESS;
411 0 : }
412 :
413 : /* Used to query and convey information about the sibling instruction
414 : https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/sdk/program/src/instruction.rs#L676
415 :
416 : */
417 : struct fd_vm_syscall_processed_sibling_instruction {
418 : /* Length of the instruction data */
419 : ulong data_len;
420 : /* Number of accounts */
421 : ulong accounts_len;
422 : };
423 : typedef struct fd_vm_syscall_processed_sibling_instruction fd_vm_syscall_processed_sibling_instruction_t;
424 :
425 : #define FD_VM_SYSCALL_PROCESSED_SIBLING_INSTRUCTION_SIZE (16UL)
426 : #define FD_VM_SYSCALL_PROCESSED_SIBLING_INSTRUCTION_ALIGN (8UL )
427 :
428 : /*
429 : sol_get_last_processed_sibling_instruction returns the last element from a reverse-ordered
430 : list of successfully processed sibling instructions: the "processed sibling instruction list".
431 :
432 : For example, given the call flow:
433 : A
434 : B -> C -> D
435 : B
436 : B -> F (current execution point)
437 :
438 : B's processed sibling instruction list is [A]
439 : F's processed sibling instruction list is [E, C]
440 :
441 : This allows the current instruction to know what the last processed sibling instruction was.
442 : This is useful to check that critical preceeding instructions have actually executed: for example
443 : ensuring that an assert instruction has successfully executed.
444 :
445 : Parameters:
446 : - index:
447 : - result_meta_vaddr: virtual address of the object where metadata about the last processed sibling instruction will be stored upon successful execution (the length of the arrays in the result).
448 : Has the type solana_program::instruction::ProcessedSiblingInstruction
449 : https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/sdk/program/src/instruction.rs#L672-L681
450 : - result_program_id_vaddr: virtual address where the pubkey of the program ID of the last processed sibling instruction will be stored upon successful execution
451 : - result_data_vaddr: virtual address where the instruction data of the last processed sibling instruction will be stored upon successful execution. The length of the data will be stored in ProcessedSiblingInstruction.data_len
452 : - result_accounts_vaddr: virtual address where an array of account meta structures will be stored into upon successful execution. The length of the data will be stored in ProcessedSiblingInstruction.accounts_len
453 : Each account meta has the type solana_program::instruction::AccountMeta
454 : https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/sdk/program/src/instruction.rs#L525-L548
455 :
456 : Result:
457 : If a processed sibling instruction is found then 1 will be written into r0, and the result_* data structures
458 : above will be populated with the last processed sibling instruction.
459 : If there is no processed sibling instruction, 0 will be written into r0.
460 :
461 : Syscall entrypoint: https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/programs/bpf_loader/src/syscalls/mod.rs#L1402
462 : */
463 : int
464 : fd_vm_syscall_sol_get_processed_sibling_instruction(
465 : void * _vm,
466 : ulong index,
467 : ulong result_meta_vaddr,
468 : ulong result_program_id_vaddr,
469 : ulong result_data_vaddr,
470 : ulong result_accounts_vaddr,
471 : ulong * ret
472 0 : ) {
473 :
474 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
475 :
476 : /* Consume base compute cost
477 : https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/programs/bpf_loader/src/syscalls/mod.rs#L1414 */
478 0 : FD_VM_CU_UPDATE( vm, FD_VM_SYSCALL_BASE_COST );
479 :
480 : /*
481 : Get the current instruction stack height.
482 :
483 : Top-level instructions in Agave's invocation stack have a depth of 1
484 : https://github.com/anza-xyz/agave/blob/d87e23d8d91c32d5f2be2bb3557c730bee1e9434/sdk/program/src/instruction.rs#L732-L733
485 : Ours have a depth of 0, so we need to add 1 to account for the difference
486 : */
487 0 : ulong stack_height = fd_ulong_sat_add( vm->instr_ctx->depth, 1UL );
488 :
489 : /* Reverse iterate through the instruction trace, ignoring anything except instructions on the same level.
490 : https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/programs/bpf_loader/src/syscalls/mod.rs#L1422 */
491 0 : ulong instruction_trace_length = vm->instr_ctx->txn_ctx->instr_trace_length;
492 0 : ulong reverse_index_at_stack_height = 0UL;
493 0 : fd_exec_instr_trace_entry_t * trace_entry = NULL;
494 0 : for( ulong index_in_trace = instruction_trace_length; index_in_trace > 0UL; index_in_trace-- ) {
495 :
496 : /* https://github.com/anza-xyz/agave/blob/v2.0.8/programs/bpf_loader/src/syscalls/mod.rs#L1432-L1434
497 : This error can never happen */
498 :
499 0 : fd_exec_instr_trace_entry_t * candidate_trace_entry = &vm->instr_ctx->txn_ctx->instr_trace[ index_in_trace - 1UL ];
500 0 : if( FD_LIKELY( candidate_trace_entry->stack_height < stack_height ) ) {
501 0 : break;
502 0 : }
503 :
504 0 : if( FD_UNLIKELY( candidate_trace_entry->stack_height == stack_height ) ) {
505 0 : if( FD_UNLIKELY( fd_ulong_sat_add( index, 1UL ) == reverse_index_at_stack_height ) ) {
506 0 : trace_entry = candidate_trace_entry;
507 0 : break;
508 0 : }
509 0 : reverse_index_at_stack_height = fd_ulong_sat_add( reverse_index_at_stack_height, 1UL );
510 0 : }
511 0 : }
512 :
513 : /* If we have found an entry, then copy the instruction into the result addresses
514 : https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/programs/bpf_loader/src/syscalls/mod.rs#L1440-L1533
515 : */
516 0 : if( FD_LIKELY( trace_entry != NULL ) ) {
517 0 : fd_instr_info_t * instr_info = trace_entry->instr_info;
518 :
519 0 : fd_vm_syscall_processed_sibling_instruction_t * result_meta_haddr = FD_VM_MEM_HADDR_ST(
520 0 : vm,
521 0 : result_meta_vaddr,
522 0 : FD_VM_SYSCALL_PROCESSED_SIBLING_INSTRUCTION_ALIGN,
523 0 : FD_VM_SYSCALL_PROCESSED_SIBLING_INSTRUCTION_SIZE );
524 :
525 : /* https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/programs/bpf_loader/src/syscalls/mod.rs#L1447 */
526 0 : if( ( result_meta_haddr->data_len == trace_entry->instr_info->data_sz ) &&
527 0 : ( result_meta_haddr->accounts_len == trace_entry->instr_info->acct_cnt ) ) {
528 :
529 0 : fd_pubkey_t * result_program_id_haddr = FD_VM_MEM_HADDR_ST(
530 0 : vm,
531 0 : result_program_id_vaddr,
532 0 : FD_VM_ALIGN_RUST_PUBKEY,
533 0 : sizeof(fd_pubkey_t) );
534 :
535 0 : uchar * result_data_haddr = FD_VM_MEM_SLICE_HADDR_ST(
536 0 : vm,
537 0 : result_data_vaddr,
538 0 : FD_VM_ALIGN_RUST_U8,
539 0 : result_meta_haddr->data_len);
540 :
541 0 : ulong accounts_meta_total_size = fd_ulong_sat_mul( result_meta_haddr->accounts_len, FD_VM_RUST_ACCOUNT_META_SIZE );
542 0 : fd_vm_rust_account_meta_t * result_accounts_haddr = FD_VM_MEM_SLICE_HADDR_ST(
543 0 : vm,
544 0 : result_accounts_vaddr,
545 0 : FD_VM_RUST_ACCOUNT_META_ALIGN,
546 0 : accounts_meta_total_size);
547 :
548 : /* Check for memory overlaps
549 : https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/programs/bpf_loader/src/syscalls/mod.rs#L1469 */
550 :
551 0 : FD_VM_MEM_CHECK_NON_OVERLAPPING( vm,
552 0 : (ulong)result_meta_haddr, FD_VM_SYSCALL_PROCESSED_SIBLING_INSTRUCTION_SIZE,
553 0 : (ulong)result_program_id_haddr, sizeof(fd_pubkey_t) );
554 0 : FD_VM_MEM_CHECK_NON_OVERLAPPING( vm,
555 0 : (ulong)result_meta_haddr, FD_VM_SYSCALL_PROCESSED_SIBLING_INSTRUCTION_SIZE,
556 0 : (ulong)result_accounts_haddr, accounts_meta_total_size );
557 0 : FD_VM_MEM_CHECK_NON_OVERLAPPING( vm,
558 0 : (ulong)result_meta_haddr, FD_VM_SYSCALL_PROCESSED_SIBLING_INSTRUCTION_SIZE,
559 0 : (ulong)result_data_haddr, result_meta_haddr->data_len );
560 0 : FD_VM_MEM_CHECK_NON_OVERLAPPING( vm,
561 0 : (ulong)result_program_id_haddr, sizeof(fd_pubkey_t),
562 0 : (ulong)result_data_haddr, result_meta_haddr->data_len );
563 0 : FD_VM_MEM_CHECK_NON_OVERLAPPING( vm,
564 0 : (ulong)result_program_id_haddr, sizeof(fd_pubkey_t),
565 0 : (ulong)result_accounts_haddr, accounts_meta_total_size );
566 0 : FD_VM_MEM_CHECK_NON_OVERLAPPING( vm,
567 0 : (ulong)result_data_haddr, result_meta_haddr->data_len,
568 0 : (ulong)result_accounts_haddr, accounts_meta_total_size );
569 :
570 : /* Copy the instruction into the result addresses
571 : https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/programs/bpf_loader/src/syscalls/mod.rs#L1506-L1528
572 :
573 : Note: we assume that the instr accounts are already correct at this point.
574 : Agave has many error checks. */
575 0 : fd_memcpy( result_program_id_haddr->key, instr_info->program_id_pubkey.key, FD_PUBKEY_FOOTPRINT );
576 0 : fd_memcpy( result_data_haddr, instr_info->data, instr_info->data_sz );
577 0 : for( ulong i = 0UL; i < instr_info->acct_cnt; i++ ) {
578 0 : fd_memcpy( result_accounts_haddr[ i ].pubkey,
579 0 : vm->instr_ctx->txn_ctx->account_keys[ instr_info->acct_txn_idxs[ i ] ].key,
580 0 : FD_PUBKEY_FOOTPRINT );
581 0 : result_accounts_haddr[ i ].is_signer = !!(instr_info->acct_flags[ i ] & FD_INSTR_ACCT_FLAGS_IS_SIGNER);
582 0 : result_accounts_haddr[ i ].is_writable = !!(instr_info->acct_flags[ i ] & FD_INSTR_ACCT_FLAGS_IS_WRITABLE);
583 0 : }
584 0 : }
585 :
586 : /* Copy the actual metadata into the result meta struct
587 : https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/programs/bpf_loader/src/syscalls/mod.rs#L1529-L1531 */
588 0 : result_meta_haddr->data_len = instr_info->data_sz;
589 0 : result_meta_haddr->accounts_len = instr_info->acct_cnt;
590 :
591 : /* Return true as we found a sibling instruction
592 : https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/programs/bpf_loader/src/syscalls/mod.rs#L1532 */
593 0 : *ret = 1UL;
594 0 : return FD_VM_SUCCESS;
595 0 : }
596 :
597 : /* Return false if we didn't find a sibling instruction
598 : https://github.com/anza-xyz/agave/blob/70089cce5119c9afaeb2986e2ecaa6d4505ec15d/programs/bpf_loader/src/syscalls/mod.rs#L1534 */
599 0 : *ret = 0UL;
600 0 : return FD_VM_SUCCESS;
601 0 : }
602 :
603 : // https://github.com/anza-xyz/agave/blob/master/programs/bpf_loader/src/syscalls/sysvar.rs#L75
604 : int
605 : fd_vm_syscall_sol_get_epoch_rewards_sysvar( /**/ void * _vm,
606 : /**/ ulong out_vaddr,
607 : FD_PARAM_UNUSED ulong r2,
608 : FD_PARAM_UNUSED ulong r3,
609 : FD_PARAM_UNUSED ulong r4,
610 : FD_PARAM_UNUSED ulong r5,
611 0 : /**/ ulong * _ret ) {
612 0 : fd_vm_t * vm = (fd_vm_t *)_vm;
613 :
614 0 : fd_exec_instr_ctx_t const * instr_ctx = vm->instr_ctx;
615 0 : if( FD_UNLIKELY( !instr_ctx ) ) return FD_VM_SYSCALL_ERR_OUTSIDE_RUNTIME;
616 :
617 0 : FD_VM_CU_UPDATE( vm, fd_ulong_sat_add( FD_VM_SYSVAR_BASE_COST, FD_SYSVAR_EPOCH_REWARDS_FOOTPRINT ) );
618 :
619 0 : void * out = FD_VM_MEM_HADDR_ST( vm, out_vaddr, FD_SYSVAR_EPOCH_REWARDS_ALIGN, FD_SYSVAR_EPOCH_REWARDS_FOOTPRINT );
620 :
621 0 : fd_sysvar_epoch_rewards_t var[1];
622 0 : fd_sysvar_epoch_rewards_new ( var );
623 0 : fd_sysvar_epoch_rewards_read( var,
624 0 : instr_ctx->txn_ctx->sysvar_cache,
625 0 : instr_ctx->txn_ctx->acc_mgr,
626 0 : instr_ctx->txn_ctx->funk_txn );
627 :
628 0 : memcpy( out, var, FD_SYSVAR_EPOCH_REWARDS_FOOTPRINT );
629 :
630 0 : *_ret = 0UL;
631 0 : return FD_VM_SUCCESS;
632 0 : }
|