1use revm_primitives::MAX_INITCODE_SIZE;
2
3use crate::{
4 instructions::utility::{read_i16, read_u16},
5 opcode,
6 primitives::{
7 bitvec::prelude::{bitvec, BitVec, Lsb0},
8 eof::{EofDecodeError, TypesSection},
9 legacy::JumpTable,
10 Bytecode, Bytes, Eof, LegacyAnalyzedBytecode,
11 },
12 OPCODE_INFO_JUMPTABLE, STACK_LIMIT,
13};
14use core::{convert::identity, mem};
15use std::{borrow::Cow, fmt, sync::Arc, vec, vec::Vec};
16
17#[inline]
23pub fn to_analysed(bytecode: Bytecode) -> Bytecode {
24 let (bytes, len) = match bytecode {
25 Bytecode::LegacyRaw(bytecode) => {
26 let len = bytecode.len();
27 let mut padded_bytecode = Vec::with_capacity(len + 33);
28 padded_bytecode.extend_from_slice(&bytecode);
29 padded_bytecode.resize(len + 33, 0);
30 (Bytes::from(padded_bytecode), len)
31 }
32 n => return n,
33 };
34 let jump_table = analyze(bytes.as_ref());
35
36 Bytecode::LegacyAnalyzed(LegacyAnalyzedBytecode::new(bytes, len, jump_table))
37}
38
39fn analyze(code: &[u8]) -> JumpTable {
41 let mut jumps: BitVec<u8> = bitvec![u8, Lsb0; 0; code.len()];
42
43 let range = code.as_ptr_range();
44 let start = range.start;
45 let mut iterator = start;
46 let end = range.end;
47 while iterator < end {
48 let opcode = unsafe { *iterator };
49 if opcode::JUMPDEST == opcode {
50 unsafe { jumps.set_unchecked(iterator.offset_from(start) as usize, true) }
52 iterator = unsafe { iterator.offset(1) };
53 } else {
54 let push_offset = opcode.wrapping_sub(opcode::PUSH1);
55 if push_offset < 32 {
56 iterator = unsafe { iterator.offset((push_offset + 2) as isize) };
58 } else {
59 iterator = unsafe { iterator.offset(1) };
61 }
62 }
63 }
64
65 JumpTable(Arc::new(jumps))
66}
67
68pub fn validate_raw_eof(raw: Bytes) -> Result<Eof, EofError> {
70 validate_raw_eof_inner(raw, Some(CodeType::ReturnContract))
71}
72
73#[inline]
75pub fn validate_raw_eof_inner(
76 raw: Bytes,
77 first_code_type: Option<CodeType>,
78) -> Result<Eof, EofError> {
79 if raw.len() > MAX_INITCODE_SIZE {
80 return Err(EofError::Decode(EofDecodeError::InvalidEOFSize));
81 }
82 let eof = Eof::decode(raw)?;
83 validate_eof_inner(&eof, first_code_type)?;
84 Ok(eof)
85}
86
87pub fn validate_eof(eof: &Eof) -> Result<(), EofError> {
94 validate_eof_inner(eof, Some(CodeType::ReturnContract))
95}
96
97#[inline]
98pub fn validate_eof_inner(eof: &Eof, first_code_type: Option<CodeType>) -> Result<(), EofError> {
99 if !eof.body.is_data_filled {
101 return Err(EofError::Validation(EofValidationError::DataNotFilled));
102 }
103 if eof.body.container_section.is_empty() {
104 validate_eof_codes(eof, first_code_type)?;
105 return Ok(());
106 }
107
108 let mut stack = Vec::with_capacity(4);
109 stack.push((Cow::Borrowed(eof), first_code_type));
110
111 while let Some((eof, code_type)) = stack.pop() {
112 let tracker_containers = validate_eof_codes(&eof, code_type)?;
114 for (container, code_type) in eof
116 .body
117 .container_section
118 .iter()
119 .zip(tracker_containers.into_iter())
120 {
121 stack.push((Cow::Owned(Eof::decode(container.clone())?), Some(code_type)));
122 }
123 }
124
125 Ok(())
126}
127
128#[inline]
132pub fn validate_eof_codes(
133 eof: &Eof,
134 this_code_type: Option<CodeType>,
135) -> Result<Vec<CodeType>, EofValidationError> {
136 if eof.body.code_section.len() != eof.body.types_section.len() {
137 return Err(EofValidationError::InvalidTypesSection);
138 }
139
140 if eof.body.code_section.is_empty() {
141 return Err(EofValidationError::NoCodeSections);
143 }
144
145 let first_types = &eof.body.types_section[0];
148 if first_types.inputs != 0 || !first_types.is_non_returning() {
149 return Err(EofValidationError::InvalidTypesSection);
150 }
151
152 let mut tracker: AccessTracker = AccessTracker::new(
154 this_code_type,
155 eof.body.code_section.len(),
156 eof.body.container_section.len(),
157 );
158
159 while let Some(index) = tracker.processing_stack.pop() {
160 let code = &eof.body.code_section[index];
162 validate_eof_code(
163 code,
164 eof.header.data_size as usize,
165 index,
166 eof.body.container_section.len(),
167 &eof.body.types_section,
168 &mut tracker,
169 )?;
170 }
171
172 if !tracker.codes.into_iter().all(identity) {
174 return Err(EofValidationError::CodeSectionNotAccessed);
175 }
176 if !tracker.subcontainers.iter().all(|i| i.is_some()) {
178 return Err(EofValidationError::SubContainerNotAccessed);
179 }
180
181 if tracker.this_container_code_type == Some(CodeType::ReturnContract)
182 && !eof.body.is_data_filled
183 {
184 return Err(EofValidationError::DataNotFilled);
185 }
186
187 Ok(tracker
188 .subcontainers
189 .into_iter()
190 .map(|i| i.unwrap())
191 .collect())
192}
193
194#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
196pub enum EofError {
197 Decode(EofDecodeError),
198 Validation(EofValidationError),
199}
200
201impl From<EofDecodeError> for EofError {
202 fn from(err: EofDecodeError) -> Self {
203 EofError::Decode(err)
204 }
205}
206
207impl From<EofValidationError> for EofError {
208 fn from(err: EofValidationError) -> Self {
209 EofError::Validation(err)
210 }
211}
212
213impl fmt::Display for EofError {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 match self {
216 EofError::Decode(e) => write!(f, "Bytecode decode error: {}", e),
217 EofError::Validation(e) => write!(f, "Bytecode validation error: {}", e),
218 }
219 }
220}
221
222#[cfg(feature = "std")]
223impl std::error::Error for EofError {}
224
225#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
226pub enum EofValidationError {
227 FalsePositive,
228 UnknownOpcode,
230 OpcodeDisabled,
232 InstructionNotForwardAccessed,
236 MissingImmediateBytes,
238 MissingRJUMPVImmediateBytes,
240 JumpToImmediateBytes,
242 BackwardJumpToImmediateBytes,
244 RJUMPVZeroMaxIndex,
246 JumpZeroOffset,
248 EOFCREATEInvalidIndex,
250 CodeSectionOutOfBounds,
252 CALLFNonReturningFunction,
254 StackOverflow,
256 JUMPFEnoughOutputs,
258 JUMPFStackHigherThanOutputs,
260 DataLoadOutOfBounds,
262 RETFBiggestStackNumMoreThenOutputs,
264 StackUnderflow,
266 TypesStackUnderflow,
268 JumpUnderflow,
270 JumpOverflow,
272 BackwardJumpBiggestNumMismatch,
274 BackwardJumpSmallestNumMismatch,
276 LastInstructionNotTerminating,
278 CodeSectionNotAccessed,
280 InvalidTypesSection,
282 InvalidFirstTypesSection,
285 MaxStackMismatch,
287 NoCodeSections,
289 SubContainerCalledInTwoModes,
292 SubContainerNotAccessed,
294 DataNotFilled,
296 NonReturningSectionIsReturning,
299}
300
301#[derive(Clone, Debug, PartialEq, Eq)]
302pub struct AccessTracker {
303 pub this_container_code_type: Option<CodeType>,
305 pub codes: Vec<bool>,
307 pub processing_stack: Vec<usize>,
309 pub subcontainers: Vec<Option<CodeType>>,
316}
317
318impl AccessTracker {
319 pub fn new(
327 this_container_code_type: Option<CodeType>,
328 codes_size: usize,
329 subcontainers_size: usize,
330 ) -> Self {
331 if codes_size == 0 {
332 panic!("There should be at least one code section");
333 }
334 let mut this = Self {
335 this_container_code_type,
336 codes: vec![false; codes_size],
337 processing_stack: Vec::with_capacity(4),
338 subcontainers: vec![None; subcontainers_size],
339 };
340 this.codes[0] = true;
341 this.processing_stack.push(0);
342 this
343 }
344
345 pub fn access_code(&mut self, index: usize) {
351 let was_accessed = mem::replace(&mut self.codes[index], true);
352 if !was_accessed {
353 self.processing_stack.push(index);
354 }
355 }
356
357 pub fn set_subcontainer_type(
358 &mut self,
359 index: usize,
360 new_code_type: CodeType,
361 ) -> Result<(), EofValidationError> {
362 let Some(container) = self.subcontainers.get_mut(index) else {
363 panic!("It should not be possible")
364 };
365
366 let Some(code_type) = container else {
367 *container = Some(new_code_type);
368 return Ok(());
369 };
370
371 if *code_type != new_code_type {
372 return Err(EofValidationError::SubContainerCalledInTwoModes);
373 }
374 Ok(())
375 }
376}
377
378#[derive(Clone, Copy, Debug, PartialEq, Eq)]
381pub enum CodeType {
382 ReturnContract,
384 ReturnOrStop,
386}
387
388impl CodeType {
389 pub fn is_initcode(&self) -> bool {
391 matches!(self, CodeType::ReturnContract)
392 }
393}
394
395impl fmt::Display for EofValidationError {
396 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397 let s = match self {
398 Self::FalsePositive => "False positive",
399 Self::UnknownOpcode => "Opcode is not known",
400 Self::OpcodeDisabled => "Opcode is disabled",
401 Self::InstructionNotForwardAccessed => "Should have forward jump",
402 Self::MissingImmediateBytes => "Bytecode is missing bytes",
403 Self::MissingRJUMPVImmediateBytes => "Bytecode is missing bytes after RJUMPV opcode",
404 Self::JumpToImmediateBytes => "Invalid jump",
405 Self::BackwardJumpToImmediateBytes => "Invalid backward jump",
406 Self::RJUMPVZeroMaxIndex => "Used RJUMPV with zero as MaxIndex",
407 Self::JumpZeroOffset => "Used JUMP with zero as offset",
408 Self::EOFCREATEInvalidIndex => "EOFCREATE points to out of bound index",
409 Self::CodeSectionOutOfBounds => "CALLF index is out of bounds",
410 Self::CALLFNonReturningFunction => "CALLF was used on non-returning function",
411 Self::StackOverflow => "CALLF stack overflow",
412 Self::JUMPFEnoughOutputs => "JUMPF needs more outputs",
413 Self::JUMPFStackHigherThanOutputs => "JUMPF stack is too high for outputs",
414 Self::DataLoadOutOfBounds => "DATALOAD is out of bounds",
415 Self::RETFBiggestStackNumMoreThenOutputs => {
416 "RETF biggest stack num is more than outputs"
417 }
418 Self::StackUnderflow => "Stack requirement is above smallest stack items",
419 Self::TypesStackUnderflow => "Smallest stack items is more than output type",
420 Self::JumpUnderflow => "Jump destination is too low",
421 Self::JumpOverflow => "Jump destination is too high",
422 Self::BackwardJumpBiggestNumMismatch => {
423 "Backward jump has different biggest stack item"
424 }
425 Self::BackwardJumpSmallestNumMismatch => {
426 "Backward jump has different smallest stack item"
427 }
428 Self::LastInstructionNotTerminating => {
429 "Last instruction of bytecode is not terminating"
430 }
431 Self::CodeSectionNotAccessed => "Code section was not accessed",
432 Self::InvalidTypesSection => "Invalid types section",
433 Self::InvalidFirstTypesSection => "Invalid first types section",
434 Self::MaxStackMismatch => "Max stack element mismatchs",
435 Self::NoCodeSections => "No code sections",
436 Self::SubContainerCalledInTwoModes => "Sub container called in two modes",
437 Self::SubContainerNotAccessed => "Sub container not accessed",
438 Self::DataNotFilled => "Data not filled",
439 Self::NonReturningSectionIsReturning => "Non returning section is returning",
440 };
441 f.write_str(s)
442 }
443}
444
445#[cfg(feature = "std")]
446impl std::error::Error for EofValidationError {}
447
448pub fn validate_eof_code(
455 code: &[u8],
456 data_size: usize,
457 this_types_index: usize,
458 num_of_containers: usize,
459 types: &[TypesSection],
460 tracker: &mut AccessTracker,
461) -> Result<(), EofValidationError> {
462 let this_types = &types[this_types_index];
463
464 #[derive(Debug, Copy, Clone)]
465 struct InstructionInfo {
466 is_immediate: bool,
468 is_jumpdest: bool,
471 smallest: i32,
473 biggest: i32,
475 }
476
477 impl InstructionInfo {
478 #[inline]
479 fn mark_as_immediate(&mut self) -> Result<(), EofValidationError> {
480 if self.is_jumpdest {
481 return Err(EofValidationError::JumpToImmediateBytes);
483 }
484 self.is_immediate = true;
485 Ok(())
486 }
487 }
488
489 impl Default for InstructionInfo {
490 fn default() -> Self {
491 Self {
492 is_immediate: false,
493 is_jumpdest: false,
494 smallest: i32::MAX,
495 biggest: i32::MIN,
496 }
497 }
498 }
499
500 let mut jumps = vec![InstructionInfo::default(); code.len()];
502 let mut is_after_termination = false;
503
504 let mut next_smallest = this_types.inputs as i32;
505 let mut next_biggest = this_types.inputs as i32;
506
507 let mut is_returning = false;
508
509 let mut i = 0;
510 while i < code.len() {
512 let op = code[i];
513 let opcode = &OPCODE_INFO_JUMPTABLE[op as usize];
514
515 let Some(opcode) = opcode else {
516 return Err(EofValidationError::UnknownOpcode);
518 };
519
520 if opcode.is_disabled_in_eof() {
521 return Err(EofValidationError::OpcodeDisabled);
523 }
524
525 let this_instruction = &mut jumps[i];
526
527 if !is_after_termination {
529 this_instruction.smallest = core::cmp::min(this_instruction.smallest, next_smallest);
530 this_instruction.biggest = core::cmp::max(this_instruction.biggest, next_biggest);
531 }
532
533 let this_instruction = *this_instruction;
534
535 if is_after_termination && !this_instruction.is_jumpdest {
537 return Err(EofValidationError::InstructionNotForwardAccessed);
539 }
540 is_after_termination = opcode.is_terminating();
541
542 if opcode.immediate_size() != 0 {
544 if i + opcode.immediate_size() as usize >= code.len() {
546 return Err(EofValidationError::MissingImmediateBytes);
548 }
549
550 for imm in 1..opcode.immediate_size() as usize + 1 {
552 jumps[i + imm].mark_as_immediate()?;
554 }
555 }
556 let mut stack_io_diff = opcode.io_diff() as i32;
558 let mut stack_requirement = opcode.inputs() as i32;
560 let mut rjumpv_additional_immediates = 0;
562 let mut absolute_jumpdest = vec![];
564 match op {
565 opcode::RJUMP | opcode::RJUMPI => {
566 let offset = unsafe { read_i16(code.as_ptr().add(i + 1)) } as isize;
567 absolute_jumpdest = vec![offset + 3 + i as isize];
568 }
570 opcode::RJUMPV => {
571 let max_index = code[i + 1] as usize;
573 let len = max_index + 1;
574 rjumpv_additional_immediates = len * 2;
576
577 if i + 1 + rjumpv_additional_immediates >= code.len() {
579 return Err(EofValidationError::MissingRJUMPVImmediateBytes);
581 }
582
583 for imm in 0..rjumpv_additional_immediates {
585 jumps[i + 2 + imm].mark_as_immediate()?;
587 }
588
589 let mut jumps = Vec::with_capacity(len);
590 for vtablei in 0..len {
591 let offset =
592 unsafe { read_i16(code.as_ptr().add(i + 2 + 2 * vtablei)) } as isize;
593 jumps.push(offset + i as isize + 2 + rjumpv_additional_immediates as isize);
594 }
595 absolute_jumpdest = jumps
596 }
597 opcode::CALLF => {
598 let section_i: usize = unsafe { read_u16(code.as_ptr().add(i + 1)) } as usize;
599 let Some(target_types) = types.get(section_i) else {
600 return Err(EofValidationError::CodeSectionOutOfBounds);
602 };
603
604 if target_types.is_non_returning() {
606 return Err(EofValidationError::CALLFNonReturningFunction);
607 }
608 stack_requirement = target_types.inputs as i32;
610 stack_io_diff = target_types.io_diff();
612 tracker.access_code(section_i);
614
615 if this_instruction.biggest - stack_requirement + target_types.max_stack_size as i32
618 > STACK_LIMIT as i32
619 {
620 return Err(EofValidationError::StackOverflow);
622 }
623 }
624 opcode::JUMPF => {
625 let target_index = unsafe { read_u16(code.as_ptr().add(i + 1)) } as usize;
626 let Some(target_types) = types.get(target_index) else {
628 return Err(EofValidationError::CodeSectionOutOfBounds);
630 };
631
632 if this_instruction.biggest - target_types.inputs as i32
635 + target_types.max_stack_size as i32
636 > STACK_LIMIT as i32
637 {
638 return Err(EofValidationError::StackOverflow);
640 }
641 tracker.access_code(target_index);
642
643 if target_types.is_non_returning() {
644 stack_requirement = target_types.inputs as i32;
646 } else {
647 is_returning = true;
648 if this_types.outputs < target_types.outputs {
650 return Err(EofValidationError::JUMPFEnoughOutputs);
651 }
652
653 stack_requirement = this_types.outputs as i32 + target_types.inputs as i32
654 - target_types.outputs as i32;
655
656 if this_instruction.biggest > stack_requirement {
658 return Err(EofValidationError::JUMPFStackHigherThanOutputs);
659 }
660
661 if this_instruction.biggest + stack_requirement > STACK_LIMIT as i32 {
663 return Err(EofValidationError::StackOverflow);
664 }
665 }
666 }
667 opcode::EOFCREATE => {
668 let index = code[i + 1] as usize;
669 if index >= num_of_containers {
670 return Err(EofValidationError::EOFCREATEInvalidIndex);
672 }
673 tracker.set_subcontainer_type(index, CodeType::ReturnContract)?;
674 }
675 opcode::RETURNCONTRACT => {
676 let index = code[i + 1] as usize;
677 if index >= num_of_containers {
678 return Err(EofValidationError::EOFCREATEInvalidIndex);
681 }
682 if *tracker
683 .this_container_code_type
684 .get_or_insert(CodeType::ReturnContract)
685 != CodeType::ReturnContract
686 {
687 return Err(EofValidationError::SubContainerCalledInTwoModes);
689 }
690 tracker.set_subcontainer_type(index, CodeType::ReturnOrStop)?;
691 }
692 opcode::RETURN | opcode::STOP => {
693 if *tracker
694 .this_container_code_type
695 .get_or_insert(CodeType::ReturnOrStop)
696 != CodeType::ReturnOrStop
697 {
698 return Err(EofValidationError::SubContainerCalledInTwoModes);
699 }
700 }
701 opcode::DATALOADN => {
702 let index = unsafe { read_u16(code.as_ptr().add(i + 1)) } as isize;
703 if data_size < 32 || index > data_size as isize - 32 {
704 return Err(EofValidationError::DataLoadOutOfBounds);
706 }
707 }
708 opcode::RETF => {
709 stack_requirement = this_types.outputs as i32;
710 is_returning = true;
712
713 if this_instruction.biggest > stack_requirement {
714 return Err(EofValidationError::RETFBiggestStackNumMoreThenOutputs);
715 }
716 }
717 opcode::DUPN => {
718 stack_requirement = code[i + 1] as i32 + 1;
719 }
720 opcode::SWAPN => {
721 stack_requirement = code[i + 1] as i32 + 2;
722 }
723 opcode::EXCHANGE => {
724 let imm = code[i + 1];
725 let n = (imm >> 4) + 1;
726 let m = (imm & 0x0F) + 1;
727 stack_requirement = n as i32 + m as i32 + 1;
728 }
729 _ => {}
730 }
731 if stack_requirement > this_instruction.smallest {
733 return Err(EofValidationError::StackUnderflow);
735 }
736
737 next_smallest = this_instruction.smallest + stack_io_diff;
738 next_biggest = this_instruction.biggest + stack_io_diff;
739
740 for absolute_jump in absolute_jumpdest {
742 if absolute_jump < 0 {
743 return Err(EofValidationError::JumpUnderflow);
745 }
746 if absolute_jump >= code.len() as isize {
747 return Err(EofValidationError::JumpOverflow);
749 }
750 let absolute_jump = absolute_jump as usize;
752
753 let target_jump = &mut jumps[absolute_jump];
754 if target_jump.is_immediate {
755 return Err(EofValidationError::BackwardJumpToImmediateBytes);
757 }
758
759 target_jump.is_jumpdest = true;
761
762 if absolute_jump <= i {
763 if target_jump.biggest != next_biggest {
765 return Err(EofValidationError::BackwardJumpBiggestNumMismatch);
767 }
768 if target_jump.smallest != next_smallest {
769 return Err(EofValidationError::BackwardJumpSmallestNumMismatch);
771 }
772 } else {
773 target_jump.smallest = core::cmp::min(target_jump.smallest, next_smallest);
776 target_jump.biggest = core::cmp::max(target_jump.biggest, next_biggest);
777 }
778 }
779
780 i += 1 + opcode.immediate_size() as usize + rjumpv_additional_immediates;
782 }
783
784 if is_returning == this_types.is_non_returning() {
786 return Err(EofValidationError::NonReturningSectionIsReturning);
788 }
789
790 if !is_after_termination {
792 return Err(EofValidationError::LastInstructionNotTerminating);
794 }
795 let mut max_stack_requirement = 0;
797 for opcode in jumps {
798 max_stack_requirement = core::cmp::max(opcode.biggest, max_stack_requirement);
799 }
800
801 if max_stack_requirement != types[this_types_index].max_stack_size as i32 {
802 return Err(EofValidationError::MaxStackMismatch);
804 }
805
806 Ok(())
807}
808
809#[cfg(test)]
810mod test {
811 use super::*;
812 use revm_primitives::hex;
813
814 #[test]
815 fn test1() {
816 let err =
818 validate_raw_eof(hex!("ef0001010004020001000704000000008000016000e200fffc00").into());
819 assert!(err.is_err(), "{err:#?}");
820 }
821
822 #[test]
823 fn test2() {
824 let err =
826 validate_raw_eof_inner(hex!("ef000101000c02000300040004000204000000008000020002000100010001e30001005fe500025fe4").into(),None);
827 assert!(err.is_ok(), "{err:#?}");
828 }
829
830 #[test]
831 fn test3() {
832 let err =
834 validate_raw_eof_inner(hex!("ef000101000c02000300040008000304000000008000020002000503010003e30001005f5f5f5f5fe500025050e4").into(),None);
835 assert_eq!(
836 err,
837 Err(EofError::Validation(
838 EofValidationError::JUMPFStackHigherThanOutputs
839 ))
840 );
841 }
842
843 #[test]
844 fn test4() {
845 let err = validate_raw_eof(
848 hex!("ef0001010004020001000e04000000008000045f6000e100025f5f6000e1fffd00").into(),
849 );
850 assert_eq!(
851 err,
852 Err(EofError::Validation(
853 EofValidationError::BackwardJumpBiggestNumMismatch
854 ))
855 );
856 }
857
858 #[test]
859 fn test5() {
860 let err = validate_raw_eof(hex!("ef000101000402000100030400000000800000e5ffff").into());
861 assert_eq!(
862 err,
863 Err(EofError::Validation(
864 EofValidationError::CodeSectionOutOfBounds
865 ))
866 );
867 }
868
869 #[test]
870 fn size_limit() {
871 let eof = validate_raw_eof_inner(
872 hex!("ef00010100040200010003040001000080000130500000").into(),
873 Some(CodeType::ReturnOrStop),
874 );
875 assert!(eof.is_ok());
876 }
877
878 #[test]
879 fn test() {
880 let eof = validate_raw_eof_inner(
881 hex!("ef0001010004020001000504ff0300008000023a60cbee1800").into(),
882 None,
883 );
884 assert_eq!(
885 eof,
886 Err(EofError::Validation(EofValidationError::DataNotFilled))
887 );
888 }
889
890 #[test]
891 fn unreachable_code_section() {
892 let eof = validate_raw_eof_inner(
893 hex!("ef000101000c02000300030001000304000000008000000080000000800000e50001fee50002")
894 .into(),
895 None,
896 );
897 assert_eq!(
898 eof,
899 Err(EofError::Validation(
900 EofValidationError::CodeSectionNotAccessed
901 ))
902 );
903 }
904
905 #[test]
906 fn non_returning_sections() {
907 let eof = validate_raw_eof_inner(
908 hex!("ef000101000c02000300040001000304000000008000000080000000000000e300020000e50001")
909 .into(),
910 Some(CodeType::ReturnOrStop),
911 );
912 assert_eq!(
913 eof,
914 Err(EofError::Validation(
915 EofValidationError::NonReturningSectionIsReturning
916 ))
917 );
918 }
919
920 #[test]
921 fn incompatible_container_kind() {
922 let eof = validate_raw_eof_inner(
923 hex!("ef000101000402000100060300010014040000000080000260006000ee00ef00010100040200010001040000000080000000")
924 .into(),
925 Some(CodeType::ReturnOrStop),
926 );
927 assert_eq!(
928 eof,
929 Err(EofError::Validation(
930 EofValidationError::SubContainerCalledInTwoModes
931 ))
932 );
933 }
934}