1use {
2 crate::{
3 compute::ComputeMeter,
4 errors::{SbpfVmError, SbpfVmResult},
5 memory::Memory,
6 syscalls::SyscallHandler,
7 },
8 sbpf_common::{
9 errors::ExecutionError, execute::Vm, inst_handler::OPCODE_TO_HANDLER,
10 instruction::Instruction,
11 },
12 serde::{Deserialize, Serialize},
13};
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct SbpfVmConfig {
18 pub max_call_depth: usize,
19 pub compute_unit_limit: u64,
20 pub heap_size: usize,
21}
22
23impl Default for SbpfVmConfig {
24 fn default() -> Self {
25 Self {
26 max_call_depth: 64,
27 compute_unit_limit: 1_400_000,
28 heap_size: Memory::DEFAULT_HEAP_SIZE,
29 }
30 }
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct CallFrame {
36 pub return_pc: usize,
37 pub saved_registers: [u64; 4], pub saved_frame_pointer: u64,
39}
40
41pub struct SbpfVm<H: SyscallHandler> {
43 pub config: SbpfVmConfig,
44 pub registers: [u64; 11],
45 pub pc: usize,
46 pub call_stack: Vec<CallFrame>,
47 pub memory: Memory,
48 pub program: Vec<Instruction>,
49 pub halted: bool,
50 pub exit_code: Option<u64>,
51 pub compute_meter: ComputeMeter,
52 pub syscall_handler: H,
53}
54
55impl<H: SyscallHandler> SbpfVm<H> {
56 pub fn new(
57 program: Vec<Instruction>,
58 input: Vec<u8>,
59 rodata: Vec<u8>,
60 syscall_handler: H,
61 ) -> Self {
62 Self::new_with_config(
63 program,
64 input,
65 rodata,
66 syscall_handler,
67 SbpfVmConfig::default(),
68 )
69 }
70
71 pub fn new_with_config(
72 program: Vec<Instruction>,
73 input: Vec<u8>,
74 rodata: Vec<u8>,
75 syscall_handler: H,
76 config: SbpfVmConfig,
77 ) -> Self {
78 let memory = Memory::new(
79 input,
80 rodata,
81 Memory::stack_size(config.max_call_depth),
82 config.heap_size,
83 );
84
85 let mut registers = [0u64; 11];
86 registers[1] = Memory::INPUT_START;
87 registers[10] = memory.initial_frame_pointer();
88
89 Self {
90 registers,
91 pc: 0,
92 call_stack: Vec::new(),
93 memory,
94 program,
95 halted: false,
96 exit_code: None,
97 compute_meter: ComputeMeter::new(config.compute_unit_limit),
98 syscall_handler,
99 config,
100 }
101 }
102
103 pub fn reset(&mut self) {
104 self.registers = [0u64; 11];
105 self.registers[1] = Memory::INPUT_START;
106 self.registers[10] = self.memory.initial_frame_pointer();
107 self.pc = 0;
108 self.call_stack.clear();
109 self.halted = false;
110 self.exit_code = None;
111 self.compute_meter.reset();
112 self.memory.reset_heap();
113 }
114
115 pub fn current_instruction(&self) -> SbpfVmResult<&Instruction> {
116 self.program
117 .get(self.pc)
118 .ok_or(SbpfVmError::PcOutOfBounds(self.pc))
119 }
120
121 pub fn set_entrypoint(&mut self, pc: usize) {
122 self.pc = pc;
123 }
124
125 pub fn is_pc_valid(&self) -> bool {
126 self.pc < self.program.len()
127 }
128
129 pub fn get_remaining(&self) -> u64 {
130 self.compute_meter.get_remaining()
131 }
132
133 pub fn step(&mut self) -> SbpfVmResult<()> {
134 if self.halted {
135 return Ok(());
136 }
137
138 if !self.is_pc_valid() {
139 return Err(SbpfVmError::PcOutOfBounds(self.pc));
140 }
141
142 self.compute_meter.consume(1)?;
143
144 let inst = self.current_instruction()?.clone();
145 self.execute_instruction(&inst)?;
146
147 Ok(())
148 }
149
150 fn execute_instruction(&mut self, inst: &Instruction) -> SbpfVmResult<()> {
151 if let Some(handler) = OPCODE_TO_HANDLER.get(&inst.opcode) {
152 (handler.execute)(self, inst)?;
153 Ok(())
154 } else {
155 Err(SbpfVmError::InvalidInstruction)
156 }
157 }
158
159 pub fn run(&mut self) -> SbpfVmResult<()> {
160 let mut steps = 0;
161
162 while !self.halted && steps < self.config.compute_unit_limit {
163 self.step()?;
164 steps += 1;
165 }
166
167 if !self.halted && steps >= self.config.compute_unit_limit {
168 return Err(SbpfVmError::ExecutionLimitReached(
169 self.config.compute_unit_limit,
170 ));
171 }
172
173 Ok(())
174 }
175}
176
177impl<H: SyscallHandler> Vm for SbpfVm<H> {
178 fn get_register(&self, reg: usize) -> u64 {
179 self.registers[reg]
180 }
181
182 fn set_register(&mut self, reg: usize, value: u64) {
183 self.registers[reg] = value;
184 }
185
186 fn get_pc(&self) -> usize {
187 self.pc
188 }
189
190 fn set_pc(&mut self, pc: usize) {
191 self.pc = pc;
192 }
193
194 fn read_u8(&self, addr: u64) -> Result<u8, ExecutionError> {
195 self.memory
196 .read_u8(addr)
197 .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
198 }
199
200 fn read_u16(&self, addr: u64) -> Result<u16, ExecutionError> {
201 self.memory
202 .read_u16(addr)
203 .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
204 }
205
206 fn read_u32(&self, addr: u64) -> Result<u32, ExecutionError> {
207 self.memory
208 .read_u32(addr)
209 .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
210 }
211
212 fn read_u64(&self, addr: u64) -> Result<u64, ExecutionError> {
213 self.memory
214 .read_u64(addr)
215 .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
216 }
217
218 fn write_u8(&mut self, addr: u64, value: u8) -> Result<(), ExecutionError> {
219 self.memory
220 .write_u8(addr, value)
221 .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
222 }
223
224 fn write_u16(&mut self, addr: u64, value: u16) -> Result<(), ExecutionError> {
225 self.memory
226 .write_u16(addr, value)
227 .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
228 }
229
230 fn write_u32(&mut self, addr: u64, value: u32) -> Result<(), ExecutionError> {
231 self.memory
232 .write_u32(addr, value)
233 .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
234 }
235
236 fn write_u64(&mut self, addr: u64, value: u64) -> Result<(), ExecutionError> {
237 self.memory
238 .write_u64(addr, value)
239 .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
240 }
241
242 fn get_call_depth(&self) -> usize {
243 self.call_stack.len()
244 }
245
246 fn max_call_depth(&self) -> usize {
247 self.config.max_call_depth
248 }
249
250 fn push_frame(
251 &mut self,
252 return_pc: usize,
253 saved_registers: [u64; 4],
254 saved_frame_pointer: u64,
255 ) -> Result<(), ExecutionError> {
256 self.call_stack.push(CallFrame {
257 return_pc,
258 saved_registers,
259 saved_frame_pointer,
260 });
261 Ok(())
262 }
263
264 fn pop_frame(&mut self) -> Option<(usize, [u64; 4], u64)> {
265 self.call_stack.pop().map(|frame| {
266 (
267 frame.return_pc,
268 frame.saved_registers,
269 frame.saved_frame_pointer,
270 )
271 })
272 }
273
274 fn halt(&mut self, exit_code: u64) {
275 self.halted = true;
276 self.exit_code = Some(exit_code);
277 }
278
279 fn get_stack_frame_size(&self) -> u64 {
280 Memory::STACK_FRAME_SIZE
281 }
282
283 fn handle_syscall(&mut self, name: &str) -> Result<u64, ExecutionError> {
284 let registers = [
285 self.registers[1],
286 self.registers[2],
287 self.registers[3],
288 self.registers[4],
289 self.registers[5],
290 ];
291 self.syscall_handler
292 .handle(
293 name,
294 registers,
295 &mut self.memory,
296 self.compute_meter.clone(),
297 )
298 .map_err(|e| ExecutionError::SyscallError(e.to_string()))
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 use {
305 super::*,
306 crate::syscalls::MockSyscallHandler,
307 either::Either,
308 sbpf_common::{
309 inst_param::{Number, Register},
310 opcode::Opcode,
311 },
312 };
313
314 fn make_test_instruction(
315 opcode: sbpf_common::opcode::Opcode,
316 dst: Option<sbpf_common::inst_param::Register>,
317 src: Option<sbpf_common::inst_param::Register>,
318 off: Option<Either<String, i16>>,
319 imm: Option<Either<String, Number>>,
320 ) -> Instruction {
321 Instruction {
322 opcode,
323 dst,
324 src,
325 off,
326 imm,
327 span: 0..0,
328 }
329 }
330
331 #[test]
332 fn test_vm_initialization() {
333 let program = vec![make_test_instruction(Opcode::Exit, None, None, None, None)];
334 let vm = SbpfVm::new(
335 program,
336 vec![1, 2, 3, 4],
337 vec![],
338 MockSyscallHandler::default(),
339 );
340
341 assert_eq!(vm.pc, 0);
342 assert_eq!(vm.registers[1], Memory::INPUT_START);
343 assert_eq!(
344 vm.registers[10],
345 Memory::STACK_START + Memory::STACK_FRAME_SIZE
346 );
347 assert!(!vm.halted);
348 assert_eq!(vm.exit_code, None);
349 }
350
351 #[test]
352 fn test_vm_reset() {
353 let program = vec![make_test_instruction(Opcode::Exit, None, None, None, None)];
354 let mut vm = SbpfVm::new(
355 program,
356 vec![1, 2, 3, 4],
357 vec![],
358 MockSyscallHandler::default(),
359 );
360
361 vm.registers[0] = 11;
363 vm.pc = 10;
364 vm.halted = true;
365 vm.exit_code = Some(1);
366
367 vm.reset();
369
370 assert_eq!(vm.pc, 0);
371 assert_eq!(vm.registers[0], 0);
372 assert_eq!(vm.registers[1], Memory::INPUT_START);
373 assert!(!vm.halted);
374 assert_eq!(vm.exit_code, None);
375 }
376
377 #[test]
378 fn test_current_instruction() {
379 let program = vec![
380 make_test_instruction(
381 Opcode::Mov64Imm,
382 Some(Register { n: 0 }),
383 None,
384 None,
385 Some(Either::Right(Number::Int(123))),
386 ),
387 make_test_instruction(Opcode::Exit, None, None, None, None),
388 ];
389 let vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
390
391 let inst = vm.current_instruction().unwrap();
392 assert_eq!(inst.opcode, Opcode::Mov64Imm);
393 }
394
395 #[test]
396 fn test_load_store() {
397 let program = vec![
403 make_test_instruction(
404 Opcode::Lddw,
405 Some(Register { n: 1 }),
406 None,
407 None,
408 Some(Either::Right(Number::Int(0x12345u64 as i64))),
409 ),
410 make_test_instruction(
411 Opcode::Mov64Reg,
412 Some(Register { n: 2 }),
413 Some(Register { n: 10 }),
414 None,
415 None,
416 ),
417 make_test_instruction(
418 Opcode::Sub64Imm,
419 Some(Register { n: 2 }),
420 None,
421 None,
422 Some(Either::Right(Number::Int(8))),
423 ),
424 make_test_instruction(
425 Opcode::Stxdw,
426 Some(Register { n: 2 }),
427 Some(Register { n: 1 }),
428 Some(Either::Right(0)),
429 None,
430 ),
431 make_test_instruction(
432 Opcode::Ldxdw,
433 Some(Register { n: 3 }),
434 Some(Register { n: 2 }),
435 Some(Either::Right(0)),
436 None,
437 ),
438 ];
439
440 let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
441
442 for _ in 0..5 {
443 vm.step().unwrap();
444 }
445
446 assert_eq!(vm.registers[3], 0x12345);
447 }
448
449 #[test]
450 fn test_alu64_operations() {
451 let program = vec![
455 make_test_instruction(
456 Opcode::Mov64Imm,
457 Some(Register { n: 1 }),
458 None,
459 None,
460 Some(Either::Right(Number::Int(10))),
461 ),
462 make_test_instruction(
463 Opcode::Add64Imm,
464 Some(Register { n: 1 }),
465 None,
466 None,
467 Some(Either::Right(Number::Int(5))),
468 ),
469 make_test_instruction(
470 Opcode::Mul64Imm,
471 Some(Register { n: 1 }),
472 None,
473 None,
474 Some(Either::Right(Number::Int(2))),
475 ),
476 ];
477
478 let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
479
480 vm.step().unwrap();
481 assert_eq!(vm.registers[1], 10);
482
483 vm.step().unwrap();
484 assert_eq!(vm.registers[1], 15);
485
486 vm.step().unwrap();
487 assert_eq!(vm.registers[1], 30);
488 }
489
490 #[test]
491 fn test_memory_regions() {
492 let input = vec![1, 2, 3, 4, 5, 6, 7, 8];
494 let rodata = vec![10, 20, 30, 40];
495
496 let program = vec![make_test_instruction(Opcode::Exit, None, None, None, None)];
497 let vm = SbpfVm::new(program, input, rodata, MockSyscallHandler::default());
498
499 assert_eq!(vm.memory.read_u8(Memory::INPUT_START).unwrap(), 1);
500 assert_eq!(
501 vm.memory.read_u64(Memory::INPUT_START).unwrap(),
502 0x0807060504030201u64
503 );
504
505 assert_eq!(vm.memory.read_u8(Memory::RODATA_START).unwrap(), 10);
507 }
508
509 #[test]
510 fn test_program_without_exit() {
511 let program = vec![
512 make_test_instruction(
513 Opcode::Mov64Imm,
514 Some(Register { n: 0 }),
515 None,
516 None,
517 Some(Either::Right(Number::Int(10))),
518 ),
519 make_test_instruction(
520 Opcode::Add64Imm,
521 Some(Register { n: 0 }),
522 None,
523 None,
524 Some(Either::Right(Number::Int(8))),
525 ),
526 ];
528
529 let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
530
531 vm.step().unwrap();
532 assert_eq!(vm.pc, 1);
533
534 vm.step().unwrap();
535 assert_eq!(vm.pc, 2);
536
537 let result = vm.step();
538 assert!(result.is_err());
539 assert!(matches!(result, Err(SbpfVmError::PcOutOfBounds(2))));
540 }
541
542 #[test]
543 fn test_step_complete_program() {
544 let program = vec![
550 make_test_instruction(
551 Opcode::Mov64Imm,
552 Some(Register { n: 1 }),
553 None,
554 None,
555 Some(Either::Right(Number::Int(10))),
556 ),
557 make_test_instruction(
558 Opcode::Add64Imm,
559 Some(Register { n: 1 }),
560 None,
561 None,
562 Some(Either::Right(Number::Int(5))),
563 ),
564 make_test_instruction(
565 Opcode::Mul64Imm,
566 Some(Register { n: 1 }),
567 None,
568 None,
569 Some(Either::Right(Number::Int(3))),
570 ),
571 make_test_instruction(
572 Opcode::Sub64Imm,
573 Some(Register { n: 1 }),
574 None,
575 None,
576 Some(Either::Right(Number::Int(7))),
577 ),
578 make_test_instruction(Opcode::Exit, None, None, None, None),
579 ];
580
581 let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
582
583 vm.step().unwrap();
584 assert_eq!(vm.pc, 1);
585 assert_eq!(vm.registers[1], 10);
586 assert_eq!(vm.compute_meter.get_consumed(), 1);
587 assert!(!vm.halted);
588
589 vm.step().unwrap();
590 assert_eq!(vm.pc, 2);
591 assert_eq!(vm.registers[1], 15);
592 assert_eq!(vm.compute_meter.get_consumed(), 2);
593 assert!(!vm.halted);
594
595 vm.step().unwrap();
596 assert_eq!(vm.pc, 3);
597 assert_eq!(vm.registers[1], 45);
598 assert_eq!(vm.compute_meter.get_consumed(), 3);
599 assert!(!vm.halted);
600
601 vm.step().unwrap();
602 assert_eq!(vm.pc, 4);
603 assert_eq!(vm.registers[1], 38);
604 assert_eq!(vm.compute_meter.get_consumed(), 4);
605 assert!(!vm.halted);
606
607 vm.step().unwrap();
608 assert_eq!(vm.pc, 4);
609 assert_eq!(vm.registers[1], 38);
610 assert_eq!(vm.compute_meter.get_consumed(), 5);
611 assert!(vm.halted);
612 }
613
614 #[test]
615 fn test_run_complete_program() {
616 let program = vec![
622 make_test_instruction(
623 Opcode::Mov64Imm,
624 Some(Register { n: 1 }),
625 None,
626 None,
627 Some(Either::Right(Number::Int(10))),
628 ),
629 make_test_instruction(
630 Opcode::Add64Imm,
631 Some(Register { n: 1 }),
632 None,
633 None,
634 Some(Either::Right(Number::Int(5))),
635 ),
636 make_test_instruction(
637 Opcode::Mul64Imm,
638 Some(Register { n: 1 }),
639 None,
640 None,
641 Some(Either::Right(Number::Int(3))),
642 ),
643 make_test_instruction(
644 Opcode::Sub64Imm,
645 Some(Register { n: 1 }),
646 None,
647 None,
648 Some(Either::Right(Number::Int(7))),
649 ),
650 make_test_instruction(Opcode::Exit, None, None, None, None),
651 ];
652
653 let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
654
655 vm.run().unwrap();
656
657 assert!(vm.halted);
658 assert_eq!(vm.registers[1], 38);
659 assert_eq!(vm.pc, 4);
660 assert_eq!(vm.compute_meter.get_consumed(), 5);
661 }
662
663 #[test]
664 fn test_program_with_input() {
665 let mut input = Vec::new();
672 input.extend_from_slice(&10u64.to_le_bytes());
673 input.extend_from_slice(&20u64.to_le_bytes());
674
675 let program = vec![
676 make_test_instruction(
677 Opcode::Ldxdw,
678 Some(Register { n: 2 }),
679 Some(Register { n: 1 }),
680 Some(Either::Right(0)),
681 None,
682 ),
683 make_test_instruction(
684 Opcode::Ldxdw,
685 Some(Register { n: 3 }),
686 Some(Register { n: 1 }),
687 Some(Either::Right(8)),
688 None,
689 ),
690 make_test_instruction(
691 Opcode::Mov64Reg,
692 Some(Register { n: 4 }),
693 Some(Register { n: 2 }),
694 None,
695 None,
696 ),
697 make_test_instruction(
698 Opcode::Add64Reg,
699 Some(Register { n: 4 }),
700 Some(Register { n: 3 }),
701 None,
702 None,
703 ),
704 make_test_instruction(Opcode::Exit, None, None, None, None),
705 ];
706
707 let mut vm = SbpfVm::new(program, input, vec![], MockSyscallHandler::default());
708
709 vm.run().unwrap();
710
711 assert!(vm.halted);
712 assert_eq!(vm.registers[2], 10);
713 assert_eq!(vm.registers[3], 20);
714 assert_eq!(vm.registers[4], 30);
715 assert_eq!(vm.compute_meter.get_consumed(), 5);
716 }
717
718 #[test]
719 fn test_program_with_internal_function_call() {
720 let program = vec![
728 make_test_instruction(
729 Opcode::Call,
730 None,
731 None,
732 None,
733 Some(Either::Right(Number::Int(2))),
734 ),
735 make_test_instruction(
736 Opcode::Lddw,
737 Some(Register { n: 2 }),
738 None,
739 None,
740 Some(Either::Right(Number::Int(0x2))),
741 ),
742 make_test_instruction(Opcode::Exit, None, None, None, None),
743 make_test_instruction(
744 Opcode::Lddw,
745 Some(Register { n: 1 }),
746 None,
747 None,
748 Some(Either::Right(Number::Int(0x1))),
749 ),
750 make_test_instruction(Opcode::Exit, None, None, None, None),
751 ];
752
753 let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
754
755 vm.run().unwrap();
756
757 assert!(vm.halted);
758 assert_eq!(vm.registers[1], 0x1);
759 assert_eq!(vm.registers[2], 0x2);
760 }
761
762 #[test]
763 fn test_vm_jmp32_imm() {
764 let program = vec![
771 make_test_instruction(
772 Opcode::Lddw,
773 Some(Register { n: 1 }),
774 None,
775 None,
776 Some(Either::Right(Number::Int(0x0000_0001_0000_0005))),
777 ),
778 make_test_instruction(
779 Opcode::Jeq32Imm,
780 Some(Register { n: 1 }),
781 None,
782 Some(Either::Right(2)),
783 Some(Either::Right(Number::Int(5))),
784 ),
785 make_test_instruction(
786 Opcode::Mov64Imm,
787 Some(Register { n: 2 }),
788 None,
789 None,
790 Some(Either::Right(Number::Int(1))),
791 ),
792 make_test_instruction(Opcode::Exit, None, None, None, None),
793 make_test_instruction(
794 Opcode::Mov64Imm,
795 Some(Register { n: 2 }),
796 None,
797 None,
798 Some(Either::Right(Number::Int(2))),
799 ),
800 make_test_instruction(Opcode::Exit, None, None, None, None),
801 ];
802
803 let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
804 vm.run().unwrap();
805
806 assert!(vm.halted);
807 assert_eq!(vm.registers[2], 2);
808 }
809
810 #[test]
811 fn test_vm_jmp32_reg() {
812 let program = vec![
820 make_test_instruction(
821 Opcode::Mov64Imm,
822 Some(Register { n: 1 }),
823 None,
824 None,
825 Some(Either::Right(Number::Int(-1))),
826 ),
827 make_test_instruction(
828 Opcode::Mov64Imm,
829 Some(Register { n: 2 }),
830 None,
831 None,
832 Some(Either::Right(Number::Int(0))),
833 ),
834 make_test_instruction(
835 Opcode::Jslt32Reg,
836 Some(Register { n: 1 }),
837 Some(Register { n: 2 }),
838 Some(Either::Right(2)),
839 None,
840 ),
841 make_test_instruction(
842 Opcode::Mov64Imm,
843 Some(Register { n: 3 }),
844 None,
845 None,
846 Some(Either::Right(Number::Int(1))),
847 ),
848 make_test_instruction(Opcode::Exit, None, None, None, None),
849 make_test_instruction(
850 Opcode::Mov64Imm,
851 Some(Register { n: 3 }),
852 None,
853 None,
854 Some(Either::Right(Number::Int(2))),
855 ),
856 make_test_instruction(Opcode::Exit, None, None, None, None),
857 ];
858
859 let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
860 vm.run().unwrap();
861
862 assert!(vm.halted);
863 assert_eq!(vm.registers[3], 2);
864 }
865}