Skip to main content

sbpf_common/
validate.rs

1use crate::{errors::SBPFError, inst_param::Number, instruction::Instruction};
2
3pub fn validate_load_immediate(inst: &Instruction) -> Result<(), SBPFError> {
4    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
5        (Some(_dst), None, None, Some(_imm)) => Ok(()),
6        _ => Err(SBPFError::BytecodeError {
7            error: format!(
8                "{} instruction requires destination register and immediate value",
9                inst.opcode
10            ),
11            span: inst.span.clone(),
12            custom_label: None,
13        }),
14    }
15}
16
17pub fn validate_load_memory(inst: &Instruction) -> Result<(), SBPFError> {
18    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
19        (Some(_dst), Some(_src), Some(_off), None) => Ok(()),
20        _ => Err(SBPFError::BytecodeError {
21            error: format!(
22                "{} instruction requires destination register, source register, and offset",
23                inst.opcode
24            ),
25            span: inst.span.clone(),
26            custom_label: None,
27        }),
28    }
29}
30
31pub fn validate_store_immediate(inst: &Instruction) -> Result<(), SBPFError> {
32    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
33        (Some(_dst), None, Some(_off), Some(_imm)) => Ok(()),
34        _ => Err(SBPFError::BytecodeError {
35            error: format!(
36                "{} instruction requires destination register, offset, and immediate value",
37                inst.opcode
38            ),
39            span: inst.span.clone(),
40            custom_label: None,
41        }),
42    }
43}
44
45pub fn validate_store_register(inst: &Instruction) -> Result<(), SBPFError> {
46    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
47        (Some(_dst), Some(_src), Some(_off), None) => Ok(()),
48        _ => Err(SBPFError::BytecodeError {
49            error: format!(
50                "{} instruction requires destination register, source register, and offset",
51                inst.opcode
52            ),
53            span: inst.span.clone(),
54            custom_label: None,
55        }),
56    }
57}
58
59pub fn validate_unary(inst: &Instruction) -> Result<(), SBPFError> {
60    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
61        (Some(_dst), None, None, None) => Ok(()),
62        _ => Err(SBPFError::BytecodeError {
63            error: format!(
64                "{} instruction requires destination register only",
65                inst.opcode
66            ),
67            span: inst.span.clone(),
68            custom_label: None,
69        }),
70    }
71}
72
73pub fn validate_endian(inst: &Instruction) -> Result<(), SBPFError> {
74    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
75        (Some(_dst), None, None, Some(imm)) => {
76            let valid_bits = matches!(
77                imm,
78                either::Either::Right(Number::Int(bits)) if *bits == 16 || *bits == 32 || *bits == 64
79            );
80
81            if valid_bits {
82                Ok(())
83            } else {
84                Err(SBPFError::BytecodeError {
85                    error: format!(
86                        "{} instruction requires immediate value of 16, 32, or 64",
87                        inst.opcode
88                    ),
89                    span: inst.span.clone(),
90                    custom_label: None,
91                })
92            }
93        }
94        _ => Err(SBPFError::BytecodeError {
95            error: format!(
96                "{} instruction requires destination register and immediate value",
97                inst.opcode
98            ),
99            span: inst.span.clone(),
100            custom_label: None,
101        }),
102    }
103}
104
105pub fn validate_binary_immediate(inst: &Instruction) -> Result<(), SBPFError> {
106    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
107        (Some(_dst), None, None, Some(_imm)) => Ok(()),
108        _ => Err(SBPFError::BytecodeError {
109            error: format!(
110                "{} instruction requires destination register and immediate value",
111                inst.opcode
112            ),
113            span: inst.span.clone(),
114            custom_label: None,
115        }),
116    }
117}
118
119pub fn validate_binary_register(inst: &Instruction) -> Result<(), SBPFError> {
120    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
121        (Some(_dst), Some(_src), None, None) => Ok(()),
122        _ => Err(SBPFError::BytecodeError {
123            error: format!(
124                "{} instruction requires destination register and source register",
125                inst.opcode
126            ),
127            span: inst.span.clone(),
128            custom_label: None,
129        }),
130    }
131}
132
133pub fn validate_jump(inst: &Instruction) -> Result<(), SBPFError> {
134    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
135        (None, None, Some(_off), None) => Ok(()),
136        _ => Err(SBPFError::BytecodeError {
137            error: format!("{} instruction requires offset only", inst.opcode),
138            span: inst.span.clone(),
139            custom_label: None,
140        }),
141    }
142}
143
144pub fn validate_jump_immediate(inst: &Instruction) -> Result<(), SBPFError> {
145    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
146        (Some(_dst), None, Some(_off), Some(_imm)) => Ok(()),
147        _ => Err(SBPFError::BytecodeError {
148            error: format!(
149                "{} instruction requires destination register, offset and immediate value",
150                inst.opcode
151            ),
152            span: inst.span.clone(),
153            custom_label: None,
154        }),
155    }
156}
157
158pub fn validate_jump_register(inst: &Instruction) -> Result<(), SBPFError> {
159    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
160        (Some(_dst), Some(_src), Some(_off), None) => Ok(()),
161        _ => Err(SBPFError::BytecodeError {
162            error: format!(
163                "{} instruction requires destination register, source register, and offset",
164                inst.opcode
165            ),
166            span: inst.span.clone(),
167            custom_label: None,
168        }),
169    }
170}
171
172pub fn validate_call_immediate(inst: &Instruction) -> Result<(), SBPFError> {
173    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
174        (None, Some(_src), None, Some(_imm)) => Ok(()),
175        _ => Err(SBPFError::BytecodeError {
176            error: format!(
177                "{} instruction requires source register and immediate value",
178                inst.opcode
179            ),
180            span: inst.span.clone(),
181            custom_label: None,
182        }),
183    }
184}
185
186pub fn validate_call_register(inst: &Instruction) -> Result<(), SBPFError> {
187    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
188        (Some(_dst), None, None, None) => Ok(()),
189        _ => Err(SBPFError::BytecodeError {
190            error: format!(
191                "{} instruction requires destination register only",
192                inst.opcode
193            ),
194            span: inst.span.clone(),
195            custom_label: None,
196        }),
197    }
198}
199
200pub fn validate_exit(inst: &Instruction) -> Result<(), SBPFError> {
201    match (&inst.dst, &inst.src, &inst.off, &inst.imm) {
202        (None, None, None, None) => Ok(()),
203        _ => Err(SBPFError::BytecodeError {
204            error: format!("{} instruction requires no operands", inst.opcode),
205            span: inst.span.clone(),
206            custom_label: None,
207        }),
208    }
209}
210
211#[cfg(test)]
212mod tests {
213    use {
214        super::*,
215        crate::{
216            inst_param::{Number, Register},
217            instruction::Instruction,
218            opcode::Opcode,
219        },
220        either::Either,
221    };
222
223    #[test]
224    fn test_validate_load_immediate_valid() {
225        let valid_inst = Instruction {
226            opcode: Opcode::Lddw,
227            dst: Some(Register { n: 0 }),
228            src: None,
229            off: None,
230            imm: Some(Either::Right(Number::Int(42))),
231            span: 0..8,
232        };
233        assert!(validate_load_immediate(&valid_inst).is_ok());
234    }
235
236    #[test]
237    fn test_validate_load_immediate_missing_dst() {
238        let inst = Instruction {
239            opcode: Opcode::Lddw,
240            dst: None,
241            src: None,
242            off: None,
243            imm: Some(Either::Right(Number::Int(42))),
244            span: 0..8,
245        };
246        let result = validate_load_immediate(&inst);
247        assert!(result.is_err());
248        if let Err(SBPFError::BytecodeError { error, .. }) = result {
249            assert_eq!(
250                error,
251                format!(
252                    "{} instruction requires destination register and immediate value",
253                    Opcode::Lddw
254                )
255            );
256        }
257    }
258
259    #[test]
260    fn test_validate_load_immediate_missing_imm() {
261        let inst = Instruction {
262            opcode: Opcode::Lddw,
263            dst: Some(Register { n: 0 }),
264            src: None,
265            off: None,
266            imm: None,
267            span: 0..8,
268        };
269        let result = validate_load_immediate(&inst);
270        assert!(result.is_err());
271        if let Err(SBPFError::BytecodeError { error, .. }) = result {
272            assert_eq!(
273                error,
274                format!(
275                    "{} instruction requires destination register and immediate value",
276                    Opcode::Lddw
277                )
278            );
279        }
280    }
281
282    #[test]
283    fn test_validate_load_immediate_has_src() {
284        let inst = Instruction {
285            opcode: Opcode::Lddw,
286            dst: Some(Register { n: 0 }),
287            src: Some(Register { n: 1 }),
288            off: None,
289            imm: Some(Either::Right(Number::Int(42))),
290            span: 0..8,
291        };
292        let result = validate_load_immediate(&inst);
293        assert!(result.is_err());
294        if let Err(SBPFError::BytecodeError { error, .. }) = result {
295            assert_eq!(
296                error,
297                format!(
298                    "{} instruction requires destination register and immediate value",
299                    Opcode::Lddw
300                )
301            );
302        }
303    }
304
305    #[test]
306    fn test_validate_load_immediate_has_offset() {
307        let inst = Instruction {
308            opcode: Opcode::Lddw,
309            dst: Some(Register { n: 0 }),
310            src: None,
311            off: Some(Either::Right(10)),
312            imm: Some(Either::Right(Number::Int(42))),
313            span: 0..8,
314        };
315        let result = validate_load_immediate(&inst);
316        assert!(result.is_err());
317        if let Err(SBPFError::BytecodeError { error, .. }) = result {
318            assert_eq!(
319                error,
320                format!(
321                    "{} instruction requires destination register and immediate value",
322                    Opcode::Lddw
323                )
324            );
325        }
326    }
327
328    #[test]
329    fn test_validate_load_memory_valid() {
330        let valid_inst = Instruction {
331            opcode: Opcode::Ldxw,
332            dst: Some(Register { n: 0 }),
333            src: Some(Register { n: 1 }),
334            off: Some(Either::Right(8)),
335            imm: None,
336            span: 0..8,
337        };
338        assert!(validate_load_memory(&valid_inst).is_ok());
339    }
340
341    #[test]
342    fn test_validate_load_memory_missing_dst() {
343        let inst = Instruction {
344            opcode: Opcode::Ldxw,
345            dst: None,
346            src: Some(Register { n: 1 }),
347            off: Some(Either::Right(8)),
348            imm: None,
349            span: 0..8,
350        };
351        let result = validate_load_memory(&inst);
352        assert!(result.is_err());
353        if let Err(SBPFError::BytecodeError { error, .. }) = result {
354            assert_eq!(
355                error,
356                format!(
357                    "{} instruction requires destination register, source register, and offset",
358                    Opcode::Ldxw
359                )
360            );
361        }
362    }
363
364    #[test]
365    fn test_validate_load_memory_has_imm() {
366        let inst = Instruction {
367            opcode: Opcode::Ldxw,
368            dst: Some(Register { n: 0 }),
369            src: Some(Register { n: 1 }),
370            off: Some(Either::Right(8)),
371            imm: Some(Either::Right(Number::Int(42))),
372            span: 0..8,
373        };
374        let result = validate_load_memory(&inst);
375        assert!(result.is_err());
376        if let Err(SBPFError::BytecodeError { error, .. }) = result {
377            assert_eq!(
378                error,
379                format!(
380                    "{} instruction requires destination register, source register, and offset",
381                    Opcode::Ldxw
382                )
383            );
384        }
385    }
386
387    #[test]
388    fn test_validate_load_memory_missing_src() {
389        let inst = Instruction {
390            opcode: Opcode::Ldxw,
391            dst: Some(Register { n: 0 }),
392            src: None,
393            off: Some(Either::Right(8)),
394            imm: None,
395            span: 0..8,
396        };
397        let result = validate_load_memory(&inst);
398        assert!(result.is_err());
399        if let Err(SBPFError::BytecodeError { error, .. }) = result {
400            assert_eq!(
401                error,
402                format!(
403                    "{} instruction requires destination register, source register, and offset",
404                    Opcode::Ldxw
405                )
406            );
407        }
408    }
409
410    #[test]
411    fn test_validate_store_immediate_valid() {
412        let valid_inst = Instruction {
413            opcode: Opcode::Stw,
414            dst: Some(Register { n: 0 }),
415            src: None,
416            off: Some(Either::Right(8)),
417            imm: Some(Either::Right(Number::Int(42))),
418            span: 0..8,
419        };
420        assert!(validate_store_immediate(&valid_inst).is_ok());
421    }
422
423    #[test]
424    fn test_validate_store_immediate_missing_imm() {
425        let inst = Instruction {
426            opcode: Opcode::Stw,
427            dst: Some(Register { n: 0 }),
428            src: None,
429            off: Some(Either::Right(8)),
430            imm: None,
431            span: 0..8,
432        };
433        let result = validate_store_immediate(&inst);
434        assert!(result.is_err());
435        if let Err(SBPFError::BytecodeError { error, .. }) = result {
436            assert_eq!(
437                error,
438                format!(
439                    "{} instruction requires destination register, offset, and immediate value",
440                    Opcode::Stw
441                )
442            );
443        }
444    }
445
446    #[test]
447    fn test_validate_store_immediate_has_src() {
448        let inst = Instruction {
449            opcode: Opcode::Stw,
450            dst: Some(Register { n: 0 }),
451            src: Some(Register { n: 1 }),
452            off: Some(Either::Right(8)),
453            imm: Some(Either::Right(Number::Int(42))),
454            span: 0..8,
455        };
456        let result = validate_store_immediate(&inst);
457        assert!(result.is_err());
458        if let Err(SBPFError::BytecodeError { error, .. }) = result {
459            assert_eq!(
460                error,
461                format!(
462                    "{} instruction requires destination register, offset, and immediate value",
463                    Opcode::Stw
464                )
465            );
466        }
467    }
468
469    #[test]
470    fn test_validate_store_register_valid() {
471        let valid_inst = Instruction {
472            opcode: Opcode::Stxw,
473            dst: Some(Register { n: 0 }),
474            src: Some(Register { n: 1 }),
475            off: Some(Either::Right(8)),
476            imm: None,
477            span: 0..8,
478        };
479        assert!(validate_store_register(&valid_inst).is_ok());
480    }
481
482    #[test]
483    fn test_validate_store_register_missing_src() {
484        let inst = Instruction {
485            opcode: Opcode::Stxw,
486            dst: Some(Register { n: 0 }),
487            src: None,
488            off: Some(Either::Right(8)),
489            imm: None,
490            span: 0..8,
491        };
492        let result = validate_store_register(&inst);
493        assert!(result.is_err());
494        if let Err(SBPFError::BytecodeError { error, .. }) = result {
495            assert_eq!(
496                error,
497                format!(
498                    "{} instruction requires destination register, source register, and offset",
499                    Opcode::Stxw
500                )
501            );
502        }
503    }
504
505    #[test]
506    fn test_validate_store_register_has_imm() {
507        let inst = Instruction {
508            opcode: Opcode::Stxw,
509            dst: Some(Register { n: 0 }),
510            src: Some(Register { n: 1 }),
511            off: Some(Either::Right(8)),
512            imm: Some(Either::Right(Number::Int(42))),
513            span: 0..8,
514        };
515        let result = validate_store_register(&inst);
516        assert!(result.is_err());
517        if let Err(SBPFError::BytecodeError { error, .. }) = result {
518            assert_eq!(
519                error,
520                format!(
521                    "{} instruction requires destination register, source register, and offset",
522                    Opcode::Stxw
523                )
524            );
525        }
526    }
527
528    #[test]
529    fn test_validate_unary_valid() {
530        let valid_inst = Instruction {
531            opcode: Opcode::Neg64,
532            dst: Some(Register { n: 0 }),
533            src: None,
534            off: None,
535            imm: None,
536            span: 0..8,
537        };
538        assert!(validate_unary(&valid_inst).is_ok());
539    }
540
541    #[test]
542    fn test_validate_unary_missing_dst() {
543        let inst = Instruction {
544            opcode: Opcode::Neg64,
545            dst: None,
546            src: None,
547            off: None,
548            imm: None,
549            span: 0..8,
550        };
551        let result = validate_unary(&inst);
552        assert!(result.is_err());
553        if let Err(SBPFError::BytecodeError { error, .. }) = result {
554            assert_eq!(
555                error,
556                format!(
557                    "{} instruction requires destination register only",
558                    Opcode::Neg64
559                )
560            );
561        }
562    }
563
564    #[test]
565    fn test_validate_unary_has_src() {
566        let inst = Instruction {
567            opcode: Opcode::Neg64,
568            dst: Some(Register { n: 0 }),
569            src: Some(Register { n: 1 }),
570            off: None,
571            imm: None,
572            span: 0..8,
573        };
574        let result = validate_unary(&inst);
575        assert!(result.is_err());
576        if let Err(SBPFError::BytecodeError { error, .. }) = result {
577            assert_eq!(
578                error,
579                format!(
580                    "{} instruction requires destination register only",
581                    Opcode::Neg64
582                )
583            );
584        }
585    }
586
587    #[test]
588    fn test_validate_unary_has_offset() {
589        let inst = Instruction {
590            opcode: Opcode::Neg64,
591            dst: Some(Register { n: 0 }),
592            src: None,
593            off: Some(Either::Right(10)),
594            imm: None,
595            span: 0..8,
596        };
597        let result = validate_unary(&inst);
598        assert!(result.is_err());
599        if let Err(SBPFError::BytecodeError { error, .. }) = result {
600            assert_eq!(
601                error,
602                format!(
603                    "{} instruction requires destination register only",
604                    Opcode::Neg64
605                )
606            );
607        }
608    }
609
610    #[test]
611    fn test_validate_unary_has_imm() {
612        let inst = Instruction {
613            opcode: Opcode::Neg64,
614            dst: Some(Register { n: 0 }),
615            src: None,
616            off: None,
617            imm: Some(Either::Right(Number::Int(42))),
618            span: 0..8,
619        };
620        let result = validate_unary(&inst);
621        assert!(result.is_err());
622        if let Err(SBPFError::BytecodeError { error, .. }) = result {
623            assert_eq!(
624                error,
625                format!(
626                    "{} instruction requires destination register only",
627                    Opcode::Neg64
628                )
629            );
630        }
631    }
632
633    #[test]
634    fn test_validate_endian_valid() {
635        let valid_inst = Instruction {
636            opcode: Opcode::Le,
637            dst: Some(Register { n: 0 }),
638            src: None,
639            off: None,
640            imm: Some(Either::Right(Number::Int(32))),
641            span: 0..8,
642        };
643        assert!(validate_endian(&valid_inst).is_ok());
644    }
645
646    #[test]
647    fn test_validate_endian_invalid_width() {
648        let inst = Instruction {
649            opcode: Opcode::Be,
650            dst: Some(Register { n: 0 }),
651            src: None,
652            off: None,
653            imm: Some(Either::Right(Number::Int(8))),
654            span: 0..8,
655        };
656        let result = validate_endian(&inst);
657        assert!(result.is_err());
658        if let Err(SBPFError::BytecodeError { error, .. }) = result {
659            assert_eq!(
660                error,
661                format!(
662                    "{} instruction requires immediate value of 16, 32, or 64",
663                    Opcode::Be
664                )
665            );
666        }
667    }
668
669    #[test]
670    fn test_validate_endian_missing_imm() {
671        let inst = Instruction {
672            opcode: Opcode::Le,
673            dst: Some(Register { n: 0 }),
674            src: None,
675            off: None,
676            imm: None,
677            span: 0..8,
678        };
679        let result = validate_endian(&inst);
680        assert!(result.is_err());
681        if let Err(SBPFError::BytecodeError { error, .. }) = result {
682            assert_eq!(
683                error,
684                format!(
685                    "{} instruction requires destination register and immediate value",
686                    Opcode::Le
687                )
688            );
689        }
690    }
691
692    #[test]
693    fn test_validate_endian_has_src() {
694        let inst = Instruction {
695            opcode: Opcode::Le,
696            dst: Some(Register { n: 0 }),
697            src: Some(Register { n: 1 }),
698            off: None,
699            imm: Some(Either::Right(Number::Int(16))),
700            span: 0..8,
701        };
702        let result = validate_endian(&inst);
703        assert!(result.is_err());
704        if let Err(SBPFError::BytecodeError { error, .. }) = result {
705            assert_eq!(
706                error,
707                format!(
708                    "{} instruction requires destination register and immediate value",
709                    Opcode::Le
710                )
711            );
712        }
713    }
714
715    #[test]
716    fn test_validate_endian_has_offset() {
717        let inst = Instruction {
718            opcode: Opcode::Be,
719            dst: Some(Register { n: 0 }),
720            src: None,
721            off: Some(Either::Right(10)),
722            imm: Some(Either::Right(Number::Int(64))),
723            span: 0..8,
724        };
725        let result = validate_endian(&inst);
726        assert!(result.is_err());
727        if let Err(SBPFError::BytecodeError { error, .. }) = result {
728            assert_eq!(
729                error,
730                format!(
731                    "{} instruction requires destination register and immediate value",
732                    Opcode::Be
733                )
734            );
735        }
736    }
737
738    #[test]
739    fn test_validate_binary_immediate_valid() {
740        let valid_inst = Instruction {
741            opcode: Opcode::Add64Imm,
742            dst: Some(Register { n: 0 }),
743            src: None,
744            off: None,
745            imm: Some(Either::Right(Number::Int(42))),
746            span: 0..8,
747        };
748        assert!(validate_binary_immediate(&valid_inst).is_ok());
749    }
750
751    #[test]
752    fn test_validate_binary_immediate_missing_dst() {
753        let inst = Instruction {
754            opcode: Opcode::Add64Imm,
755            dst: None,
756            src: None,
757            off: None,
758            imm: Some(Either::Right(Number::Int(42))),
759            span: 0..8,
760        };
761        let result = validate_binary_immediate(&inst);
762        assert!(result.is_err());
763        if let Err(SBPFError::BytecodeError { error, .. }) = result {
764            assert_eq!(
765                error,
766                format!(
767                    "{} instruction requires destination register and immediate value",
768                    Opcode::Add64Imm
769                )
770            );
771        }
772    }
773
774    #[test]
775    fn test_validate_binary_immediate_missing_imm() {
776        let inst = Instruction {
777            opcode: Opcode::Add64Imm,
778            dst: Some(Register { n: 0 }),
779            src: None,
780            off: None,
781            imm: None,
782            span: 0..8,
783        };
784        let result = validate_binary_immediate(&inst);
785        assert!(result.is_err());
786        if let Err(SBPFError::BytecodeError { error, .. }) = result {
787            assert_eq!(
788                error,
789                format!(
790                    "{} instruction requires destination register and immediate value",
791                    Opcode::Add64Imm
792                )
793            );
794        }
795    }
796
797    #[test]
798    fn test_validate_binary_immediate_has_src() {
799        let inst = Instruction {
800            opcode: Opcode::Add64Imm,
801            dst: Some(Register { n: 0 }),
802            src: Some(Register { n: 1 }),
803            off: None,
804            imm: Some(Either::Right(Number::Int(42))),
805            span: 0..8,
806        };
807        let result = validate_binary_immediate(&inst);
808        assert!(result.is_err());
809        if let Err(SBPFError::BytecodeError { error, .. }) = result {
810            assert_eq!(
811                error,
812                format!(
813                    "{} instruction requires destination register and immediate value",
814                    Opcode::Add64Imm
815                )
816            );
817        }
818    }
819
820    #[test]
821    fn test_validate_binary_immediate_has_offset() {
822        let inst = Instruction {
823            opcode: Opcode::Add64Imm,
824            dst: Some(Register { n: 0 }),
825            src: None,
826            off: Some(Either::Right(10)),
827            imm: Some(Either::Right(Number::Int(16))),
828            span: 0..8,
829        };
830        let result = validate_binary_immediate(&inst);
831        assert!(result.is_err());
832        if let Err(SBPFError::BytecodeError { error, .. }) = result {
833            assert_eq!(
834                error,
835                format!(
836                    "{} instruction requires destination register and immediate value",
837                    Opcode::Add64Imm
838                )
839            );
840        }
841    }
842
843    #[test]
844    fn test_validate_binary_register_valid() {
845        let valid_inst = Instruction {
846            opcode: Opcode::Add64Reg,
847            dst: Some(Register { n: 0 }),
848            src: Some(Register { n: 1 }),
849            off: None,
850            imm: None,
851            span: 0..8,
852        };
853        assert!(validate_binary_register(&valid_inst).is_ok());
854    }
855
856    #[test]
857    fn test_validate_binary_register_missing_dst() {
858        let inst = Instruction {
859            opcode: Opcode::Add64Reg,
860            dst: None,
861            src: Some(Register { n: 1 }),
862            off: None,
863            imm: None,
864            span: 0..8,
865        };
866        let result = validate_binary_register(&inst);
867        assert!(result.is_err());
868        if let Err(SBPFError::BytecodeError { error, .. }) = result {
869            assert_eq!(
870                error,
871                format!(
872                    "{} instruction requires destination register and source register",
873                    Opcode::Add64Reg
874                )
875            );
876        }
877    }
878
879    #[test]
880    fn test_validate_binary_register_missing_src() {
881        let inst = Instruction {
882            opcode: Opcode::Add64Reg,
883            dst: Some(Register { n: 0 }),
884            src: None,
885            off: None,
886            imm: None,
887            span: 0..8,
888        };
889        let result = validate_binary_register(&inst);
890        assert!(result.is_err());
891        if let Err(SBPFError::BytecodeError { error, .. }) = result {
892            assert_eq!(
893                error,
894                format!(
895                    "{} instruction requires destination register and source register",
896                    Opcode::Add64Reg
897                )
898            );
899        }
900    }
901
902    #[test]
903    fn test_validate_binary_register_has_offset() {
904        let inst = Instruction {
905            opcode: Opcode::Add64Reg,
906            dst: Some(Register { n: 0 }),
907            src: Some(Register { n: 1 }),
908            off: Some(Either::Right(10)),
909            imm: None,
910            span: 0..8,
911        };
912        let result = validate_binary_register(&inst);
913        assert!(result.is_err());
914        if let Err(SBPFError::BytecodeError { error, .. }) = result {
915            assert_eq!(
916                error,
917                format!(
918                    "{} instruction requires destination register and source register",
919                    Opcode::Add64Reg
920                )
921            );
922        }
923    }
924
925    #[test]
926    fn test_validate_binary_register_has_imm() {
927        let inst = Instruction {
928            opcode: Opcode::Add64Reg,
929            dst: Some(Register { n: 0 }),
930            src: Some(Register { n: 1 }),
931            off: None,
932            imm: Some(Either::Right(Number::Int(42))),
933            span: 0..8,
934        };
935        let result = validate_binary_register(&inst);
936        assert!(result.is_err());
937        if let Err(SBPFError::BytecodeError { error, .. }) = result {
938            assert_eq!(
939                error,
940                format!(
941                    "{} instruction requires destination register and source register",
942                    Opcode::Add64Reg
943                )
944            );
945        }
946    }
947
948    #[test]
949    fn test_validate_jump_valid() {
950        let valid_inst = Instruction {
951            opcode: Opcode::Ja,
952            dst: None,
953            src: None,
954            off: Some(Either::Right(10)),
955            imm: None,
956            span: 0..8,
957        };
958        assert!(validate_jump(&valid_inst).is_ok());
959    }
960
961    #[test]
962    fn test_validate_jump_valid_has_dst() {
963        let inst = Instruction {
964            opcode: Opcode::Ja,
965            dst: Some(Register { n: 0 }),
966            src: None,
967            off: Some(Either::Right(10)),
968            imm: None,
969            span: 0..8,
970        };
971        let result = validate_jump(&inst);
972        assert!(result.is_err());
973        if let Err(SBPFError::BytecodeError { error, .. }) = result {
974            assert_eq!(
975                error,
976                format!("{} instruction requires offset only", Opcode::Ja)
977            );
978        }
979    }
980
981    #[test]
982    fn test_validate_jump_has_src() {
983        let inst = Instruction {
984            opcode: Opcode::Ja,
985            dst: None,
986            src: Some(Register { n: 1 }),
987            off: Some(Either::Right(10)),
988            imm: None,
989            span: 0..8,
990        };
991        let result = validate_jump(&inst);
992        assert!(result.is_err());
993        if let Err(SBPFError::BytecodeError { error, .. }) = result {
994            assert_eq!(
995                error,
996                format!("{} instruction requires offset only", Opcode::Ja)
997            );
998        }
999    }
1000
1001    #[test]
1002    fn test_validate_jump_has_imm() {
1003        let inst = Instruction {
1004            opcode: Opcode::Ja,
1005            dst: None,
1006            src: None,
1007            off: Some(Either::Right(10)),
1008            imm: Some(Either::Right(Number::Int(42))),
1009            span: 0..8,
1010        };
1011        let result = validate_jump(&inst);
1012        assert!(result.is_err());
1013        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1014            assert_eq!(
1015                error,
1016                format!("{} instruction requires offset only", Opcode::Ja)
1017            );
1018        }
1019    }
1020
1021    #[test]
1022    fn test_validate_jump_immediate_valid() {
1023        let valid_inst = Instruction {
1024            opcode: Opcode::JeqImm,
1025            dst: Some(Register { n: 0 }),
1026            src: None,
1027            off: Some(Either::Right(10)),
1028            imm: Some(Either::Right(Number::Int(42))),
1029            span: 0..8,
1030        };
1031        assert!(validate_jump_immediate(&valid_inst).is_ok());
1032    }
1033
1034    #[test]
1035    fn test_validate_jump_immediate_missing_dst() {
1036        let inst = Instruction {
1037            opcode: Opcode::JeqImm,
1038            dst: None,
1039            src: None,
1040            off: Some(Either::Right(10)),
1041            imm: Some(Either::Right(Number::Int(42))),
1042            span: 0..8,
1043        };
1044        let result = validate_jump_immediate(&inst);
1045        assert!(result.is_err());
1046        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1047            assert_eq!(
1048                error,
1049                format!(
1050                    "{} instruction requires destination register, offset and immediate value",
1051                    Opcode::JeqImm
1052                )
1053            );
1054        }
1055    }
1056
1057    #[test]
1058    fn test_validate_jump_immediate_missing_imm() {
1059        let inst = Instruction {
1060            opcode: Opcode::JeqImm,
1061            dst: Some(Register { n: 0 }),
1062            src: None,
1063            off: Some(Either::Right(10)),
1064            imm: None,
1065            span: 0..8,
1066        };
1067        let result = validate_jump_immediate(&inst);
1068        assert!(result.is_err());
1069        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1070            assert_eq!(
1071                error,
1072                format!(
1073                    "{} instruction requires destination register, offset and immediate value",
1074                    Opcode::JeqImm
1075                )
1076            );
1077        }
1078    }
1079
1080    #[test]
1081    fn test_validate_jump_register_valid() {
1082        let valid_inst = Instruction {
1083            opcode: Opcode::JeqReg,
1084            dst: Some(Register { n: 0 }),
1085            src: Some(Register { n: 1 }),
1086            off: Some(Either::Right(10)),
1087            imm: None,
1088            span: 0..8,
1089        };
1090        assert!(validate_jump_register(&valid_inst).is_ok());
1091    }
1092
1093    #[test]
1094    fn test_validate_jump_register_missing_dst() {
1095        let inst = Instruction {
1096            opcode: Opcode::JeqReg,
1097            dst: None,
1098            src: Some(Register { n: 1 }),
1099            off: Some(Either::Right(10)),
1100            imm: None,
1101            span: 0..8,
1102        };
1103        let result = validate_jump_register(&inst);
1104        assert!(result.is_err());
1105        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1106            assert_eq!(
1107                error,
1108                format!(
1109                    "{} instruction requires destination register, source register, and offset",
1110                    Opcode::JeqReg
1111                )
1112            );
1113        }
1114    }
1115
1116    #[test]
1117    fn test_validate_jump_register_missing_src() {
1118        let inst = Instruction {
1119            opcode: Opcode::JeqReg,
1120            dst: Some(Register { n: 0 }),
1121            src: None,
1122            off: Some(Either::Right(10)),
1123            imm: None,
1124            span: 0..8,
1125        };
1126        let result = validate_jump_register(&inst);
1127        assert!(result.is_err());
1128        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1129            assert_eq!(
1130                error,
1131                format!(
1132                    "{} instruction requires destination register, source register, and offset",
1133                    Opcode::JeqReg
1134                )
1135            );
1136        }
1137    }
1138
1139    #[test]
1140    fn test_validate_jump_register_missing_offset() {
1141        let inst = Instruction {
1142            opcode: Opcode::JeqReg,
1143            dst: Some(Register { n: 0 }),
1144            src: Some(Register { n: 1 }),
1145            off: None,
1146            imm: None,
1147            span: 0..8,
1148        };
1149        let result = validate_jump_register(&inst);
1150        assert!(result.is_err());
1151        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1152            assert_eq!(
1153                error,
1154                format!(
1155                    "{} instruction requires destination register, source register, and offset",
1156                    Opcode::JeqReg
1157                )
1158            );
1159        }
1160    }
1161
1162    #[test]
1163    fn test_validate_jump_register_has_imm() {
1164        let inst = Instruction {
1165            opcode: Opcode::JeqReg,
1166            dst: Some(Register { n: 0 }),
1167            src: Some(Register { n: 1 }),
1168            off: Some(Either::Right(10)),
1169            imm: Some(Either::Right(Number::Int(42))),
1170            span: 0..8,
1171        };
1172        let result = validate_jump_register(&inst);
1173        assert!(result.is_err());
1174        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1175            assert_eq!(
1176                error,
1177                format!(
1178                    "{} instruction requires destination register, source register, and offset",
1179                    Opcode::JeqReg
1180                )
1181            );
1182        }
1183    }
1184
1185    #[test]
1186    fn test_validate_call_immediate_valid() {
1187        let valid_inst = Instruction {
1188            opcode: Opcode::Call,
1189            dst: None,
1190            src: Some(Register { n: 1 }),
1191            off: None,
1192            imm: Some(Either::Right(Number::Int(100))),
1193            span: 0..8,
1194        };
1195        assert!(validate_call_immediate(&valid_inst).is_ok());
1196    }
1197
1198    #[test]
1199    fn test_validate_call_immediate_missing_imm() {
1200        let inst = Instruction {
1201            opcode: Opcode::Call,
1202            dst: None,
1203            src: None,
1204            off: None,
1205            imm: None,
1206            span: 0..8,
1207        };
1208        let result = validate_call_immediate(&inst);
1209        assert!(result.is_err());
1210        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1211            assert_eq!(
1212                error,
1213                format!(
1214                    "{} instruction requires source register and immediate value",
1215                    Opcode::Call
1216                )
1217            );
1218        }
1219    }
1220
1221    #[test]
1222    fn test_validate_call_immediate_has_dst() {
1223        let inst = Instruction {
1224            opcode: Opcode::Call,
1225            dst: Some(Register { n: 0 }),
1226            src: None,
1227            off: None,
1228            imm: Some(Either::Right(Number::Int(100))),
1229            span: 0..8,
1230        };
1231        let result = validate_call_immediate(&inst);
1232        assert!(result.is_err());
1233        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1234            assert_eq!(
1235                error,
1236                format!(
1237                    "{} instruction requires source register and immediate value",
1238                    Opcode::Call
1239                )
1240            );
1241        }
1242    }
1243
1244    #[test]
1245    fn test_validate_call_immediate_has_offset() {
1246        let inst = Instruction {
1247            opcode: Opcode::Call,
1248            dst: None,
1249            src: None,
1250            off: Some(Either::Right(10)),
1251            imm: Some(Either::Right(Number::Int(100))),
1252            span: 0..8,
1253        };
1254        let result = validate_call_immediate(&inst);
1255        assert!(result.is_err());
1256        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1257            assert_eq!(
1258                error,
1259                format!(
1260                    "{} instruction requires source register and immediate value",
1261                    Opcode::Call
1262                )
1263            );
1264        }
1265    }
1266
1267    #[test]
1268    fn test_validate_call_register_valid() {
1269        let valid_inst = Instruction {
1270            opcode: Opcode::Callx,
1271            dst: Some(Register { n: 1 }),
1272            src: None,
1273            off: None,
1274            imm: None,
1275            span: 0..8,
1276        };
1277        assert!(validate_call_register(&valid_inst).is_ok());
1278    }
1279
1280    #[test]
1281    fn test_validate_call_register_missing_dst() {
1282        let inst = Instruction {
1283            opcode: Opcode::Callx,
1284            dst: None,
1285            src: None,
1286            off: None,
1287            imm: None,
1288            span: 0..8,
1289        };
1290        let result = validate_call_register(&inst);
1291        assert!(result.is_err());
1292        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1293            assert_eq!(
1294                error,
1295                format!(
1296                    "{} instruction requires destination register only",
1297                    Opcode::Callx
1298                )
1299            );
1300        }
1301    }
1302
1303    #[test]
1304    fn test_validate_call_register_has_src() {
1305        let inst = Instruction {
1306            opcode: Opcode::Callx,
1307            dst: Some(Register { n: 0 }),
1308            src: Some(Register { n: 1 }),
1309            off: None,
1310            imm: None,
1311            span: 0..8,
1312        };
1313        let result = validate_call_register(&inst);
1314        assert!(result.is_err());
1315        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1316            assert_eq!(
1317                error,
1318                format!(
1319                    "{} instruction requires destination register only",
1320                    Opcode::Callx
1321                )
1322            );
1323        }
1324    }
1325
1326    #[test]
1327    fn test_validate_call_register_has_offset() {
1328        let inst = Instruction {
1329            opcode: Opcode::Callx,
1330            dst: Some(Register { n: 1 }),
1331            src: None,
1332            off: Some(Either::Right(10)),
1333            imm: None,
1334            span: 0..8,
1335        };
1336        let result = validate_call_register(&inst);
1337        assert!(result.is_err());
1338        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1339            assert_eq!(
1340                error,
1341                format!(
1342                    "{} instruction requires destination register only",
1343                    Opcode::Callx
1344                )
1345            );
1346        }
1347    }
1348
1349    #[test]
1350    fn test_validate_call_register_has_imm() {
1351        let inst = Instruction {
1352            opcode: Opcode::Callx,
1353            dst: Some(Register { n: 1 }),
1354            src: None,
1355            off: None,
1356            imm: Some(Either::Right(Number::Int(100))),
1357            span: 0..8,
1358        };
1359        let result = validate_call_register(&inst);
1360        assert!(result.is_err());
1361        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1362            assert_eq!(
1363                error,
1364                format!(
1365                    "{} instruction requires destination register only",
1366                    Opcode::Callx
1367                )
1368            );
1369        }
1370    }
1371
1372    #[test]
1373    fn test_validate_exit_valid() {
1374        let valid_inst = Instruction {
1375            opcode: Opcode::Exit,
1376            dst: None,
1377            src: None,
1378            off: None,
1379            imm: None,
1380            span: 0..8,
1381        };
1382        assert!(validate_exit(&valid_inst).is_ok());
1383    }
1384
1385    #[test]
1386    fn test_validate_exit_has_dst() {
1387        let inst = Instruction {
1388            opcode: Opcode::Exit,
1389            dst: Some(Register { n: 0 }),
1390            src: None,
1391            off: None,
1392            imm: None,
1393            span: 0..8,
1394        };
1395        let result = validate_exit(&inst);
1396        assert!(result.is_err());
1397        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1398            assert_eq!(
1399                error,
1400                format!("{} instruction requires no operands", Opcode::Exit)
1401            );
1402        }
1403    }
1404
1405    #[test]
1406    fn test_validate_exit_has_src() {
1407        let inst = Instruction {
1408            opcode: Opcode::Exit,
1409            dst: None,
1410            src: Some(Register { n: 1 }),
1411            off: None,
1412            imm: None,
1413            span: 0..8,
1414        };
1415        let result = validate_exit(&inst);
1416        assert!(result.is_err());
1417        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1418            assert_eq!(
1419                error,
1420                format!("{} instruction requires no operands", Opcode::Exit)
1421            );
1422        }
1423    }
1424
1425    #[test]
1426    fn test_validate_exit_has_offset() {
1427        let inst = Instruction {
1428            opcode: Opcode::Exit,
1429            dst: None,
1430            src: None,
1431            off: Some(Either::Right(10)),
1432            imm: None,
1433            span: 0..8,
1434        };
1435        let result = validate_exit(&inst);
1436        assert!(result.is_err());
1437        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1438            assert_eq!(
1439                error,
1440                format!("{} instruction requires no operands", Opcode::Exit)
1441            );
1442        }
1443    }
1444
1445    #[test]
1446    fn test_validate_exit_has_imm() {
1447        let inst = Instruction {
1448            opcode: Opcode::Exit,
1449            dst: None,
1450            src: None,
1451            off: None,
1452            imm: Some(Either::Right(Number::Int(0))),
1453            span: 0..8,
1454        };
1455        let result = validate_exit(&inst);
1456        assert!(result.is_err());
1457        if let Err(SBPFError::BytecodeError { error, .. }) = result {
1458            assert_eq!(
1459                error,
1460                format!("{} instruction requires no operands", Opcode::Exit)
1461            );
1462        }
1463    }
1464}