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