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