agave_syscalls/
sysvar.rs

1use {
2    super::*, crate::translate_mut,
3    solana_program_runtime::execution_budget::SVMTransactionExecutionCost, solana_sbpf::ebpf,
4};
5
6fn get_sysvar<T: std::fmt::Debug + SysvarSerialize + Clone>(
7    sysvar: Result<Arc<T>, InstructionError>,
8    var_addr: u64,
9    check_aligned: bool,
10    memory_mapping: &mut MemoryMapping,
11    invoke_context: &mut InvokeContext,
12) -> Result<u64, Error> {
13    consume_compute_meter(
14        invoke_context,
15        invoke_context
16            .get_execution_cost()
17            .sysvar_base_cost
18            .saturating_add(size_of::<T>() as u64),
19    )?;
20
21    if var_addr >= ebpf::MM_INPUT_START
22        && invoke_context
23            .get_feature_set()
24            .stricter_abi_and_runtime_constraints
25    {
26        return Err(SyscallError::InvalidPointer.into());
27    }
28    translate_mut!(
29        memory_mapping,
30        check_aligned,
31        let var: &mut T = map(var_addr)?;
32    );
33
34    // this clone looks unnecessary now, but it exists to zero out trailing alignment bytes
35    // it is unclear whether this should ever matter
36    // but there are tests using MemoryMapping that expect to see this
37    // we preserve the previous behavior out of an abundance of caution
38    let sysvar: Arc<T> = sysvar?;
39    *var = T::clone(sysvar.as_ref());
40
41    Ok(SUCCESS)
42}
43
44declare_builtin_function!(
45    /// Get a Clock sysvar
46    SyscallGetClockSysvar,
47    fn rust(
48        invoke_context: &mut InvokeContext,
49        var_addr: u64,
50        _arg2: u64,
51        _arg3: u64,
52        _arg4: u64,
53        _arg5: u64,
54        memory_mapping: &mut MemoryMapping,
55    ) -> Result<u64, Error> {
56        get_sysvar(
57            invoke_context.get_sysvar_cache().get_clock(),
58            var_addr,
59            invoke_context.get_check_aligned(),
60            memory_mapping,
61            invoke_context,
62        )
63    }
64);
65
66declare_builtin_function!(
67    /// Get a EpochSchedule sysvar
68    SyscallGetEpochScheduleSysvar,
69    fn rust(
70        invoke_context: &mut InvokeContext,
71        var_addr: u64,
72        _arg2: u64,
73        _arg3: u64,
74        _arg4: u64,
75        _arg5: u64,
76        memory_mapping: &mut MemoryMapping,
77    ) -> Result<u64, Error> {
78        get_sysvar(
79            invoke_context.get_sysvar_cache().get_epoch_schedule(),
80            var_addr,
81            invoke_context.get_check_aligned(),
82            memory_mapping,
83            invoke_context,
84        )
85    }
86);
87
88declare_builtin_function!(
89    /// Get a EpochRewards sysvar
90    SyscallGetEpochRewardsSysvar,
91    fn rust(
92        invoke_context: &mut InvokeContext,
93        var_addr: u64,
94        _arg2: u64,
95        _arg3: u64,
96        _arg4: u64,
97        _arg5: u64,
98        memory_mapping: &mut MemoryMapping,
99    ) -> Result<u64, Error> {
100        get_sysvar(
101            invoke_context.get_sysvar_cache().get_epoch_rewards(),
102            var_addr,
103            invoke_context.get_check_aligned(),
104            memory_mapping,
105            invoke_context,
106        )
107    }
108);
109
110declare_builtin_function!(
111    /// Get a Fees sysvar
112    SyscallGetFeesSysvar,
113    fn rust(
114        invoke_context: &mut InvokeContext,
115        var_addr: u64,
116        _arg2: u64,
117        _arg3: u64,
118        _arg4: u64,
119        _arg5: u64,
120        memory_mapping: &mut MemoryMapping,
121    ) -> Result<u64, Error> {
122        #[allow(deprecated)]
123        {
124            get_sysvar(
125                invoke_context.get_sysvar_cache().get_fees(),
126                var_addr,
127                invoke_context.get_check_aligned(),
128                memory_mapping,
129                invoke_context,
130            )
131        }
132    }
133);
134
135declare_builtin_function!(
136    /// Get a Rent sysvar
137    SyscallGetRentSysvar,
138    fn rust(
139        invoke_context: &mut InvokeContext,
140        var_addr: u64,
141        _arg2: u64,
142        _arg3: u64,
143        _arg4: u64,
144        _arg5: u64,
145        memory_mapping: &mut MemoryMapping,
146    ) -> Result<u64, Error> {
147        get_sysvar(
148            invoke_context.get_sysvar_cache().get_rent(),
149            var_addr,
150            invoke_context.get_check_aligned(),
151            memory_mapping,
152            invoke_context,
153        )
154    }
155);
156
157declare_builtin_function!(
158    /// Get a Last Restart Slot sysvar
159    SyscallGetLastRestartSlotSysvar,
160    fn rust(
161        invoke_context: &mut InvokeContext,
162        var_addr: u64,
163        _arg2: u64,
164        _arg3: u64,
165        _arg4: u64,
166        _arg5: u64,
167        memory_mapping: &mut MemoryMapping,
168    ) -> Result<u64, Error> {
169        get_sysvar(
170            invoke_context.get_sysvar_cache().get_last_restart_slot(),
171            var_addr,
172            invoke_context.get_check_aligned(),
173            memory_mapping,
174            invoke_context,
175        )
176    }
177);
178
179const SYSVAR_NOT_FOUND: u64 = 2;
180const OFFSET_LENGTH_EXCEEDS_SYSVAR: u64 = 1;
181
182// quoted language from SIMD0127
183// because this syscall can both return error codes and abort, well-ordered error checking is crucial
184declare_builtin_function!(
185    /// Get a slice of a Sysvar in-memory representation
186    SyscallGetSysvar,
187    fn rust(
188        invoke_context: &mut InvokeContext,
189        sysvar_id_addr: u64,
190        var_addr: u64,
191        offset: u64,
192        length: u64,
193        _arg5: u64,
194        memory_mapping: &mut MemoryMapping,
195    ) -> Result<u64, Error> {
196        let check_aligned = invoke_context.get_check_aligned();
197        let SVMTransactionExecutionCost {
198            sysvar_base_cost,
199            cpi_bytes_per_unit,
200            mem_op_base_cost,
201            ..
202        } = *invoke_context.get_execution_cost();
203
204        // Abort: "Compute budget is exceeded."
205        let sysvar_id_cost = 32_u64.checked_div(cpi_bytes_per_unit).unwrap_or(0);
206        let sysvar_buf_cost = length.checked_div(cpi_bytes_per_unit).unwrap_or(0);
207        consume_compute_meter(
208            invoke_context,
209            sysvar_base_cost
210                .saturating_add(sysvar_id_cost)
211                .saturating_add(std::cmp::max(sysvar_buf_cost, mem_op_base_cost)),
212        )?;
213
214        if var_addr >= ebpf::MM_INPUT_START
215            && invoke_context
216                .get_feature_set()
217                .stricter_abi_and_runtime_constraints
218        {
219            return Err(SyscallError::InvalidPointer.into());
220        }
221        // Abort: "Not all bytes in VM memory range `[var_addr, var_addr + length)` are writable."
222        translate_mut!(
223            memory_mapping,
224            check_aligned,
225            let var: &mut [u8] = map(var_addr, length)?;
226        );
227
228        // Abort: "Not all bytes in VM memory range `[sysvar_id, sysvar_id + 32)` are readable."
229        let sysvar_id = translate_type::<Pubkey>(memory_mapping, sysvar_id_addr, check_aligned)?;
230
231        // Abort: "`offset + length` is not in `[0, 2^64)`."
232        let offset_length = offset
233            .checked_add(length)
234            .ok_or(InstructionError::ArithmeticOverflow)?;
235
236        // Abort: "`var_addr + length` is not in `[0, 2^64)`."
237        let _ = var_addr
238            .checked_add(length)
239            .ok_or(InstructionError::ArithmeticOverflow)?;
240
241        let cache = invoke_context.get_sysvar_cache();
242
243        // "`2` if the sysvar data is not present in the Sysvar Cache."
244        let Some(ref sysvar_buf) = cache.sysvar_id_to_buffer(sysvar_id) else { return Ok(SYSVAR_NOT_FOUND) };
245
246        // "`1` if `offset + length` is greater than the length of the sysvar data."
247        if let Some(sysvar_slice) = sysvar_buf.get(offset as usize..offset_length as usize) {
248            var.copy_from_slice(sysvar_slice);
249        } else {
250            return Ok(OFFSET_LENGTH_EXCEEDS_SYSVAR);
251        }
252
253        Ok(SUCCESS)
254    }
255);