1use {
2 crate::{config::ExecutionCost, cpi::ReturnData},
3 sbpf_vm::{compute::ComputeMeter, errors::SbpfVmResult, memory::Memory},
4 solana_address::Address,
5};
6
7const MAX_RETURN_DATA: usize = 1024;
8
9pub fn sol_set_return_data(
10 registers: [u64; 5],
11 memory: &Memory,
12 compute: &ComputeMeter,
13 costs: &ExecutionCost,
14 program_id: &Address,
15) -> SbpfVmResult<(u64, ReturnData)> {
16 let addr = registers[0];
17 let len = registers[1];
18
19 let cost = len
20 .checked_div(costs.cpi_bytes_per_unit)
21 .unwrap_or(u64::MAX)
22 .saturating_add(costs.syscall_base_cost);
23 compute.consume(cost)?;
24
25 if len > MAX_RETURN_DATA as u64 {
26 return Err(sbpf_vm::errors::SbpfVmError::SyscallError(format!(
27 "Return data too large: {} > {}",
28 len, MAX_RETURN_DATA
29 )));
30 }
31
32 let data = if len == 0 {
33 Vec::new()
34 } else {
35 memory.read_bytes(addr, len as usize)?.to_vec()
36 };
37
38 Ok((0, Some((*program_id, data))))
39}
40
41pub fn sol_get_return_data(
42 registers: [u64; 5],
43 memory: &mut Memory,
44 compute: &ComputeMeter,
45 costs: &ExecutionCost,
46 return_data: &ReturnData,
47) -> SbpfVmResult<u64> {
48 let buf_addr = registers[0];
49 let buf_len = registers[1];
50 let program_id_addr = registers[2];
51
52 compute.consume(costs.syscall_base_cost)?;
53
54 let Some((program_id, data)) = return_data else {
55 return Ok(0);
56 };
57
58 let length = buf_len.min(data.len() as u64);
59
60 if length != 0 {
61 let cost = length
62 .saturating_add(32)
63 .checked_div(costs.cpi_bytes_per_unit)
64 .unwrap_or(u64::MAX);
65 compute.consume(cost)?;
66
67 let from_slice = &data[..length as usize];
68 memory.write_bytes(buf_addr, from_slice)?;
69 memory.write_bytes(program_id_addr, program_id.as_ref())?;
70 }
71
72 Ok(data.len() as u64)
73}
74
75#[cfg(test)]
76mod tests {
77 use {
78 super::*,
79 crate::syscalls::tests::test_helpers::{costs, make_memory, meter},
80 sbpf_vm::{errors::SbpfVmError, memory::Memory},
81 solana_address::Address,
82 };
83
84 fn test_program_id() -> Address {
85 Address::from([7u8; 32])
86 }
87
88 #[test]
89 fn test_set_return_data_basic() {
90 let mut memory = make_memory();
91 let data = b"result";
92 memory.write_bytes(Memory::HEAP_START, data).unwrap();
93
94 let program_id = test_program_id();
95 let registers = [Memory::HEAP_START, data.len() as u64, 0, 0, 0];
96 let (ret, rd) =
97 sol_set_return_data(registers, &memory, &meter(1_000_000), &costs(), &program_id)
98 .unwrap();
99
100 assert_eq!(ret, 0);
101 let (stored_id, stored_data) = rd.unwrap();
102 assert_eq!(stored_id, program_id);
103 assert_eq!(stored_data, data);
104 }
105
106 #[test]
107 fn test_set_return_data_empty() {
108 let memory = make_memory();
109 let program_id = test_program_id();
110 let registers = [Memory::HEAP_START, 0, 0, 0, 0];
111 let (ret, rd) =
112 sol_set_return_data(registers, &memory, &meter(1_000_000), &costs(), &program_id)
113 .unwrap();
114
115 assert_eq!(ret, 0);
116 let (_, stored_data) = rd.unwrap();
117 assert!(stored_data.is_empty());
118 }
119
120 #[test]
121 fn test_set_return_data_too_large() {
122 let memory = make_memory();
123 let program_id = test_program_id();
124 let registers = [Memory::HEAP_START, 1025, 0, 0, 0];
126 let result =
127 sol_set_return_data(registers, &memory, &meter(1_000_000), &costs(), &program_id);
128 assert!(matches!(result, Err(SbpfVmError::SyscallError(_))));
129 }
130
131 #[test]
132 fn test_set_return_data_compute_exhausted() {
133 let memory = make_memory();
134 let program_id = test_program_id();
135 let registers = [Memory::HEAP_START, 0, 0, 0, 0];
137 assert!(matches!(
138 sol_set_return_data(registers, &memory, &meter(99), &costs(), &program_id),
139 Err(SbpfVmError::ComputeBudgetExceeded { .. })
140 ));
141 }
142
143 #[test]
144 fn test_get_return_data_none() {
145 let mut memory = make_memory();
146 let registers = [Memory::HEAP_START, 32, Memory::HEAP_START + 64, 0, 0];
147 let ret = sol_get_return_data(registers, &mut memory, &meter(1_000_000), &costs(), &None)
148 .unwrap();
149 assert_eq!(ret, 0);
150 }
151
152 #[test]
153 fn test_get_return_data_full() {
154 let mut memory = make_memory();
155 let program_id = test_program_id();
156 let data = vec![1u8, 2, 3, 4, 5];
157 let return_data: ReturnData = Some((program_id, data.clone()));
158
159 let buf_addr = Memory::HEAP_START;
160 let pid_addr = Memory::HEAP_START + 64;
161 let registers = [buf_addr, data.len() as u64, pid_addr, 0, 0];
162 let ret = sol_get_return_data(
163 registers,
164 &mut memory,
165 &meter(1_000_000),
166 &costs(),
167 &return_data,
168 )
169 .unwrap();
170
171 assert_eq!(ret, data.len() as u64);
172 assert_eq!(
173 memory.read_bytes(buf_addr, data.len()).unwrap(),
174 data.as_slice()
175 );
176 assert_eq!(
177 memory.read_bytes(pid_addr, 32).unwrap(),
178 program_id.as_ref()
179 );
180 }
181
182 #[test]
183 fn test_get_return_data_truncated_buf() {
184 let mut memory = make_memory();
185 let program_id = test_program_id();
186 let data = vec![10u8, 20, 30, 40, 50];
187 let return_data: ReturnData = Some((program_id, data.clone()));
188
189 let buf_addr = Memory::HEAP_START;
190 let pid_addr = Memory::HEAP_START + 64;
191 let registers = [buf_addr, 3, pid_addr, 0, 0];
193 let ret = sol_get_return_data(
194 registers,
195 &mut memory,
196 &meter(1_000_000),
197 &costs(),
198 &return_data,
199 )
200 .unwrap();
201
202 assert_eq!(ret, 5, "returns full data length even when buf is smaller");
203 assert_eq!(memory.read_bytes(buf_addr, 3).unwrap(), &[10, 20, 30]);
204 }
205
206 #[test]
207 fn test_get_return_data_zero_buf_len() {
208 let mut memory = make_memory();
209 let program_id = test_program_id();
210 let data = vec![1u8, 2, 3];
211 let return_data: ReturnData = Some((program_id, data.clone()));
212
213 let registers = [Memory::HEAP_START, 0, Memory::HEAP_START + 64, 0, 0];
215 let ret = sol_get_return_data(
216 registers,
217 &mut memory,
218 &meter(1_000_000),
219 &costs(),
220 &return_data,
221 )
222 .unwrap();
223 assert_eq!(ret, 3);
224 }
225
226 #[test]
227 fn test_get_return_data_compute_exhausted() {
228 let mut memory = make_memory();
229 let registers = [Memory::HEAP_START, 32, Memory::HEAP_START + 64, 0, 0];
230 assert!(matches!(
232 sol_get_return_data(registers, &mut memory, &meter(99), &costs(), &None),
233 Err(SbpfVmError::ComputeBudgetExceeded { .. })
234 ));
235 }
236}