1use {
2 crate::execution_cost::ExecutionCost,
3 blake3::Hasher as Blake3Hasher,
4 sbpf_vm::{
5 compute::ComputeMeter,
6 errors::{SbpfVmError, SbpfVmResult},
7 memory::Memory,
8 syscalls::SyscallHandler,
9 },
10 sha2::{Digest, Sha256},
11 sha3::Keccak256,
12 solana_address::Address,
13 solana_clock::Clock,
14 solana_epoch_schedule::EpochSchedule,
15 solana_rent::Rent,
16 std::mem::size_of,
17};
18
19const MAX_SEED_LEN: usize = 32;
20const MAX_SEEDS: usize = 16;
21
22#[derive(Debug)]
24pub struct DebuggerSyscallHandler {
25 pub costs: ExecutionCost,
26 pub current_program_id: Address,
27 pub clock: Clock,
28 pub rent: Rent,
29 pub epoch_schedule: EpochSchedule,
30}
31
32impl DebuggerSyscallHandler {
33 pub fn new(costs: ExecutionCost, current_program_id: Address) -> Self {
34 Self {
35 costs,
36 current_program_id,
37 clock: Clock::default(),
38 rent: Rent::default(),
39 epoch_schedule: EpochSchedule::default(),
40 }
41 }
42
43 fn sol_log(
44 &mut self,
45 registers: [u64; 5],
46 memory: &Memory,
47 compute: &ComputeMeter,
48 ) -> SbpfVmResult<u64> {
49 let msg_ptr = registers[0];
50 let msg_len = registers[1];
51
52 let cost = self.costs.syscall_base_cost.max(msg_len);
53 compute.consume(cost)?;
54
55 let msg_bytes = memory.read_bytes(msg_ptr, msg_len as usize)?;
56 let msg = String::from_utf8_lossy(msg_bytes);
57 println!("Program log: {}", msg);
58 Ok(0)
59 }
60
61 fn sol_log_64(&mut self, registers: [u64; 5], compute: &ComputeMeter) -> SbpfVmResult<u64> {
62 let cost = self.costs.log_64_units;
63 compute.consume(cost)?;
64 println!(
65 "Program log: {:#x}, {:#x}, {:#x}, {:#x}, {:#x}",
66 registers[0], registers[1], registers[2], registers[3], registers[4]
67 );
68 Ok(0)
69 }
70
71 fn sol_log_pubkey(
72 &mut self,
73 registers: [u64; 5],
74 memory: &Memory,
75 compute: &ComputeMeter,
76 ) -> SbpfVmResult<u64> {
77 let cost = self.costs.log_pubkey_units;
78 compute.consume(cost)?;
79
80 let pubkey_ptr = registers[0];
81 let pubkey_bytes = memory.read_bytes(pubkey_ptr, 32)?;
82 let pubkey_base58 = bs58::encode(pubkey_bytes).into_string();
83 println!("Program log: {}", pubkey_base58);
84 Ok(0)
85 }
86
87 fn sol_log_compute_units(&mut self, compute: &ComputeMeter) -> SbpfVmResult<u64> {
88 let cost = self.costs.syscall_base_cost;
89 compute.consume(cost)?;
90
91 let remaining = compute.get_remaining();
92 println!("Program consumption: {} units remaining", remaining);
93 Ok(0)
94 }
95
96 fn sol_remaining_compute_units(&mut self, compute: &ComputeMeter) -> SbpfVmResult<u64> {
97 let cost = self.costs.syscall_base_cost;
98 compute.consume(cost)?;
99 Ok(compute.get_remaining())
100 }
101
102 fn mem_op_consume(&self, n: u64, compute: &ComputeMeter) -> SbpfVmResult<()> {
103 let cost = self.costs.mem_op_base_cost.max(
104 n.checked_div(self.costs.cpi_bytes_per_unit)
105 .unwrap_or(u64::MAX),
106 );
107 compute.consume(cost)
108 }
109
110 fn is_nonoverlapping(src: u64, src_len: u64, dst: u64, dst_len: u64) -> bool {
111 if src > dst {
112 src.saturating_sub(dst) >= dst_len
113 } else {
114 dst.saturating_sub(src) >= src_len
115 }
116 }
117
118 fn sol_memcpy(
119 &mut self,
120 registers: [u64; 5],
121 memory: &mut Memory,
122 compute: &ComputeMeter,
123 ) -> SbpfVmResult<u64> {
124 let dst = registers[0];
125 let src = registers[1];
126 let n = registers[2];
127
128 self.mem_op_consume(n, compute)?;
129
130 if !Self::is_nonoverlapping(src, n, dst, n) {
131 return Err(SbpfVmError::OverlappingMemoryRegions);
132 }
133
134 let data = memory.read_bytes(src, n as usize)?.to_vec();
135 memory.write_bytes(dst, &data)?;
136 Ok(0)
137 }
138
139 fn sol_memmove(
140 &mut self,
141 registers: [u64; 5],
142 memory: &mut Memory,
143 compute: &ComputeMeter,
144 ) -> SbpfVmResult<u64> {
145 let dst = registers[0];
146 let src = registers[1];
147 let n = registers[2];
148
149 self.mem_op_consume(n, compute)?;
150
151 let data = memory.read_bytes(src, n as usize)?.to_vec();
152 memory.write_bytes(dst, &data)?;
153
154 Ok(0)
155 }
156
157 fn sol_memset(
158 &mut self,
159 registers: [u64; 5],
160 memory: &mut Memory,
161 compute: &ComputeMeter,
162 ) -> SbpfVmResult<u64> {
163 let dst = registers[0];
164 let c = registers[1] as u8;
165 let n = registers[2];
166
167 self.mem_op_consume(n, compute)?;
168
169 let data = vec![c; n as usize];
170 memory.write_bytes(dst, &data)?;
171
172 Ok(0)
173 }
174
175 fn sol_memcmp(
176 &mut self,
177 registers: [u64; 5],
178 memory: &mut Memory,
179 compute: &ComputeMeter,
180 ) -> SbpfVmResult<u64> {
181 let s1 = registers[0];
182 let s2 = registers[1];
183 let n = registers[2];
184 let result_ptr = registers[3];
185
186 self.mem_op_consume(n, compute)?;
187
188 let s1_bytes = memory.read_bytes(s1, n as usize)?;
189 let s2_bytes = memory.read_bytes(s2, n as usize)?;
190
191 let mut result: i32 = 0;
192 for i in 0..n as usize {
193 if s1_bytes[i] != s2_bytes[i] {
194 result = (s1_bytes[i] as i32).saturating_sub(s2_bytes[i] as i32);
195 break;
196 }
197 }
198
199 memory.write_u32(result_ptr, result as u32)?;
200
201 Ok(0)
202 }
203
204 fn abort(&mut self) -> SbpfVmResult<u64> {
205 Err(SbpfVmError::Abort)
206 }
207
208 fn sol_panic(
209 &mut self,
210 registers: [u64; 5],
211 memory: &Memory,
212 compute: &ComputeMeter,
213 ) -> SbpfVmResult<u64> {
214 let file_ptr = registers[0];
215 let file_len = registers[1];
216 let line = registers[2];
217 let column = registers[3];
218
219 compute.consume(file_len)?;
220
221 let file_bytes = memory.read_bytes(file_ptr, file_len as usize)?;
222 let file = String::from_utf8_lossy(file_bytes);
223
224 eprintln!("Program panicked at {}:{}:{}", file, line, column);
225
226 Err(SbpfVmError::Abort)
227 }
228
229 fn read_slices(
230 &self,
231 memory: &Memory,
232 vals_addr: u64,
233 vals_len: u64,
234 ) -> SbpfVmResult<Vec<(u64, u64)>> {
235 let mut slices = Vec::with_capacity(vals_len as usize);
236 for i in 0..vals_len {
237 let slice_addr = vals_addr.saturating_add(i.saturating_mul(16));
238 let ptr = memory.read_u64(slice_addr)?;
239 let len = memory.read_u64(slice_addr.saturating_add(8))?;
240 slices.push((ptr, len));
241 }
242 Ok(slices)
243 }
244
245 fn hash_slices<H: Digest>(
246 &mut self,
247 memory: &mut Memory,
248 compute: &ComputeMeter,
249 vals_addr: u64,
250 vals_len: u64,
251 result_addr: u64,
252 ) -> SbpfVmResult<u64> {
253 if vals_len > self.costs.sha256_max_slices {
254 return Err(SbpfVmError::TooManySlices);
255 }
256
257 compute.consume(self.costs.sha256_base_cost)?;
258
259 let mut hasher = H::new();
260 if vals_len > 0 {
261 for (ptr, len) in self.read_slices(memory, vals_addr, vals_len)? {
262 let cost = self
263 .costs
264 .mem_op_base_cost
265 .max(self.costs.sha256_byte_cost.saturating_mul(len / 2));
266 compute.consume(cost)?;
267 hasher.update(memory.read_bytes(ptr, len as usize)?);
268 }
269 }
270
271 memory.write_bytes(result_addr, &hasher.finalize())?;
272 Ok(0)
273 }
274
275 fn sol_sha256(
276 &mut self,
277 registers: [u64; 5],
278 memory: &mut Memory,
279 compute: &ComputeMeter,
280 ) -> SbpfVmResult<u64> {
281 self.hash_slices::<Sha256>(memory, compute, registers[0], registers[1], registers[2])
282 }
283
284 fn sol_keccak256(
285 &mut self,
286 registers: [u64; 5],
287 memory: &mut Memory,
288 compute: &ComputeMeter,
289 ) -> SbpfVmResult<u64> {
290 self.hash_slices::<Keccak256>(memory, compute, registers[0], registers[1], registers[2])
291 }
292
293 fn sol_blake3(
294 &mut self,
295 registers: [u64; 5],
296 memory: &mut Memory,
297 compute: &ComputeMeter,
298 ) -> SbpfVmResult<u64> {
299 self.hash_slices::<Blake3Hasher>(memory, compute, registers[0], registers[1], registers[2])
300 }
301
302 fn read_seeds(
303 &self,
304 memory: &Memory,
305 seeds_addr: u64,
306 seeds_len: u64,
307 ) -> SbpfVmResult<Vec<Vec<u8>>> {
308 if seeds_len as usize > MAX_SEEDS {
309 return Err(SbpfVmError::MaxSeedLengthExceeded);
310 }
311
312 let mut seeds = Vec::with_capacity(seeds_len as usize);
313 for i in 0..seeds_len {
314 let slice_addr = seeds_addr.saturating_add(i.saturating_mul(16));
315 let ptr = memory.read_u64(slice_addr)?;
316 let len = memory.read_u64(slice_addr.saturating_add(8))?;
317
318 if len as usize > MAX_SEED_LEN {
319 return Err(SbpfVmError::MaxSeedLengthExceeded);
320 }
321
322 let seed = memory.read_bytes(ptr, len as usize)?.to_vec();
323 seeds.push(seed);
324 }
325 Ok(seeds)
326 }
327
328 fn sol_create_program_address(
329 &mut self,
330 registers: [u64; 5],
331 memory: &mut Memory,
332 compute: &ComputeMeter,
333 ) -> SbpfVmResult<u64> {
334 let seeds_addr = registers[0];
335 let seeds_len = registers[1];
336 let program_id_addr = registers[2];
337 let address_addr = registers[3];
338
339 let cost = self.costs.create_program_address_units;
340 compute.consume(cost)?;
341
342 let seeds = self.read_seeds(memory, seeds_addr, seeds_len)?;
343 let program_id = Address::from(
344 <[u8; 32]>::try_from(memory.read_bytes(program_id_addr, 32)?)
345 .map_err(|_| SbpfVmError::InvalidSliceConversion)?,
346 );
347
348 let seed_slices: Vec<&[u8]> = seeds.iter().map(|s| s.as_slice()).collect();
349 match Address::create_program_address(&seed_slices, &program_id) {
350 Ok(addr) => {
351 memory.write_bytes(address_addr, addr.as_ref())?;
352 Ok(0)
353 }
354 Err(_) => Ok(1),
355 }
356 }
357
358 fn sol_try_find_program_address(
359 &mut self,
360 registers: [u64; 5],
361 memory: &mut Memory,
362 compute: &ComputeMeter,
363 ) -> SbpfVmResult<u64> {
364 let seeds_addr = registers[0];
365 let seeds_len = registers[1];
366 let program_id_addr = registers[2];
367 let address_addr = registers[3];
368 let bump_seed_addr = registers[4];
369
370 let cost = self.costs.create_program_address_units;
371 compute.consume(cost)?;
372
373 let seeds = self.read_seeds(memory, seeds_addr, seeds_len)?;
374 let program_id = Address::from(
375 <[u8; 32]>::try_from(memory.read_bytes(program_id_addr, 32)?)
376 .map_err(|_| SbpfVmError::InvalidSliceConversion)?,
377 );
378
379 let seed_slices: Vec<&[u8]> = seeds.iter().map(|s| s.as_slice()).collect();
380 match Address::try_find_program_address(&seed_slices, &program_id) {
381 Some((addr, bump)) => {
382 memory.write_u8(bump_seed_addr, bump)?;
383 memory.write_bytes(address_addr, addr.as_ref())?;
384 Ok(0)
385 }
386 None => Ok(1),
387 }
388 }
389
390 fn write_sysvar_bytes<T>(
391 &self,
392 memory: &mut Memory,
393 addr: u64,
394 sysvar: &T,
395 ) -> SbpfVmResult<()> {
396 let bytes =
397 unsafe { std::slice::from_raw_parts(sysvar as *const T as *const u8, size_of::<T>()) };
398 memory.write_bytes(addr, bytes)
399 }
400
401 fn sol_get_clock_sysvar(
402 &mut self,
403 registers: [u64; 5],
404 memory: &mut Memory,
405 compute: &ComputeMeter,
406 ) -> SbpfVmResult<u64> {
407 let cost = self
408 .costs
409 .sysvar_base_cost
410 .saturating_add(size_of::<Clock>() as u64);
411 compute.consume(cost)?;
412 let clock = self.clock.clone();
413 self.write_sysvar_bytes(memory, registers[0], &clock)?;
414 Ok(0)
415 }
416
417 fn sol_get_rent_sysvar(
418 &mut self,
419 registers: [u64; 5],
420 memory: &mut Memory,
421 compute: &ComputeMeter,
422 ) -> SbpfVmResult<u64> {
423 let cost = self
424 .costs
425 .sysvar_base_cost
426 .saturating_add(size_of::<Rent>() as u64);
427 compute.consume(cost)?;
428 let rent = self.rent.clone();
429 self.write_sysvar_bytes(memory, registers[0], &rent)?;
430 Ok(0)
431 }
432
433 fn sol_get_epoch_schedule_sysvar(
434 &mut self,
435 registers: [u64; 5],
436 memory: &mut Memory,
437 compute: &ComputeMeter,
438 ) -> SbpfVmResult<u64> {
439 let cost = self
440 .costs
441 .sysvar_base_cost
442 .saturating_add(size_of::<EpochSchedule>() as u64);
443 compute.consume(cost)?;
444 let epoch_schedule = self.epoch_schedule.clone();
445 self.write_sysvar_bytes(memory, registers[0], &epoch_schedule)?;
446 Ok(0)
447 }
448}
449
450impl SyscallHandler for DebuggerSyscallHandler {
451 fn handle(
452 &mut self,
453 name: &str,
454 registers: [u64; 5],
455 memory: &mut Memory,
456 compute: ComputeMeter,
457 ) -> SbpfVmResult<u64> {
458 match name {
459 "sol_log_" => self.sol_log(registers, memory, &compute),
461 "sol_log_64_" => self.sol_log_64(registers, &compute),
462 "sol_log_pubkey" => self.sol_log_pubkey(registers, memory, &compute),
463 "sol_log_compute_units_" => self.sol_log_compute_units(&compute),
464 "sol_remaining_compute_units" => self.sol_remaining_compute_units(&compute),
465
466 "sol_memcpy_" => self.sol_memcpy(registers, memory, &compute),
468 "sol_memmove_" => self.sol_memmove(registers, memory, &compute),
469 "sol_memset_" => self.sol_memset(registers, memory, &compute),
470 "sol_memcmp_" => self.sol_memcmp(registers, memory, &compute),
471
472 "abort" => self.abort(),
474 "sol_panic_" => self.sol_panic(registers, memory, &compute),
475
476 "sol_sha256" => self.sol_sha256(registers, memory, &compute),
478 "sol_keccak256" => self.sol_keccak256(registers, memory, &compute),
479 "sol_blake3" => self.sol_blake3(registers, memory, &compute),
480
481 "sol_create_program_address" => {
483 self.sol_create_program_address(registers, memory, &compute)
484 }
485 "sol_try_find_program_address" => {
486 self.sol_try_find_program_address(registers, memory, &compute)
487 }
488
489 "sol_get_clock_sysvar" => self.sol_get_clock_sysvar(registers, memory, &compute),
491 "sol_get_rent_sysvar" => self.sol_get_rent_sysvar(registers, memory, &compute),
492 "sol_get_epoch_schedule_sysvar" => {
493 self.sol_get_epoch_schedule_sysvar(registers, memory, &compute)
494 }
495
496 _ => {
498 let cost = self.costs.syscall_base_cost;
499 compute.consume(cost)?;
500 eprintln!("Unknown syscall: {}", name);
501 Ok(0)
502 }
503 }
504 }
505}
506
507#[cfg(test)]
508mod tests {
509 use super::*;
510
511 fn test_handler() -> DebuggerSyscallHandler {
512 DebuggerSyscallHandler::new(ExecutionCost::default(), Address::new_unique())
513 }
514
515 #[test]
516 fn test_sol_log_64_cu_consumption() {
517 let mut handler = test_handler();
518 let compute = ComputeMeter::new(1000);
519 handler.sol_log_64([1, 2, 3, 4, 5], &compute).unwrap();
520 assert_eq!(compute.borrow().consumed, 100);
521 }
522
523 #[test]
524 fn test_sol_log_64_budget_exceeded() {
525 let mut handler = test_handler();
526 let compute = ComputeMeter::new(50);
527 let result = handler.sol_log_64([1, 2, 3, 4, 5], &compute);
528 assert!(matches!(
529 result,
530 Err(SbpfVmError::ComputeBudgetExceeded { .. })
531 ));
532 }
533}