1use crate::{
7 gas_metering::{CustomConstantCostRules, Rules},
8 ids::{CodeId, prelude::*},
9};
10use alloc::vec::Vec;
11use gear_wasm_instrument::{GEAR_SUPPORTED_FEATURES, InstrumentationBuilder, Module};
12
13mod errors;
14mod instrumented;
15mod metadata;
16mod utils;
17
18pub use errors::*;
19pub use gear_wasm_instrument::SyscallKind;
20pub use instrumented::*;
21pub use metadata::*;
22pub use utils::{
23 ALLOWED_EXPORTS, MAX_WASM_PAGES_AMOUNT, REQUIRED_EXPORTS, get_custom_section_data,
24};
25
26use utils::CodeTypeSectionSizes;
27
28const GENERIC_OS_PAGE_SIZE: u32 = 4096;
30
31pub struct TryNewCodeConfig {
34 pub version: u32,
36 pub stack_height: Option<u32>,
38 pub data_segments_amount_limit: Option<u32>,
40 pub table_amount_limit: Option<u32>,
42 pub type_section_len_limit: Option<u32>,
44 pub type_section_params_per_type_limit: Option<u32>,
46 pub export_stack_height: bool,
48 pub check_exports: bool,
50 pub check_imports: bool,
52 pub check_and_canonize_stack_end: bool,
54 pub check_mut_global_exports: bool,
56 pub check_start_section: bool,
58 pub check_data_section: bool,
60 pub check_table_section: bool,
62 pub make_validation: bool,
64 pub syscall_kind: SyscallKind,
66}
67
68impl TryNewCodeConfig {
69 pub fn new_no_exports_check() -> Self {
71 Self {
72 check_exports: false,
73 ..Default::default()
74 }
75 }
76}
77
78impl Default for TryNewCodeConfig {
79 fn default() -> Self {
80 Self {
81 version: 1,
82 stack_height: None,
83 data_segments_amount_limit: None,
84 table_amount_limit: None,
85 type_section_len_limit: None,
86 type_section_params_per_type_limit: None,
87 export_stack_height: false,
88 check_exports: true,
89 check_imports: true,
90 check_and_canonize_stack_end: true,
91 check_mut_global_exports: true,
92 check_start_section: true,
93 check_data_section: true,
94 check_table_section: true,
95 make_validation: true,
96 syscall_kind: SyscallKind::default(),
97 }
98 }
99}
100
101#[derive(Clone, Debug, PartialEq, Eq)]
103pub struct Code {
104 original: Vec<u8>,
105 instrumented: InstrumentedCode,
106 metadata: CodeMetadata,
107}
108
109impl Code {
110 fn try_new_internal<R, GetRulesFn>(
112 original_code: Vec<u8>,
113 get_gas_rules: Option<GetRulesFn>,
114 config: TryNewCodeConfig,
115 ) -> Result<Self, CodeError>
116 where
117 R: Rules,
118 GetRulesFn: FnMut(&Module) -> R,
119 {
120 if config.make_validation {
121 wasmparser::Validator::new_with_features(GEAR_SUPPORTED_FEATURES)
122 .validate_all(&original_code)
123 .map_err(CodeError::Validation)?;
124 }
125
126 let mut module = Module::new(&original_code)?;
127
128 let static_pages = utils::get_static_pages(&module)?;
129
130 let stack_end = match config.check_and_canonize_stack_end {
132 true => utils::check_and_canonize_gear_stack_end(&mut module, static_pages)?,
133 false => None,
134 };
135
136 if config.check_data_section {
138 utils::check_data_section(
139 &module,
140 static_pages,
141 stack_end,
142 config.data_segments_amount_limit,
143 )?;
144 }
145 if config.check_mut_global_exports {
146 utils::check_mut_global_exports(&module)?;
147 }
148 if config.check_start_section {
149 utils::check_start_section(&module)?;
150 }
151 if config.check_exports {
152 utils::check_exports(&module)?;
153 }
154 if config.check_imports {
155 utils::check_imports(&module, config.syscall_kind)?;
156 }
157 if let Some(limit) = config.type_section_params_per_type_limit {
158 utils::check_type_section(&module, limit)?;
159 }
160
161 let exports = utils::get_exports(&module);
163
164 let mut instrumentation_builder = InstrumentationBuilder::new("env");
165 if let Some(stack_limit) = config.stack_height {
166 instrumentation_builder.with_stack_limiter(stack_limit, config.export_stack_height);
167 }
168 if let Some(get_gas_rules) = get_gas_rules {
169 instrumentation_builder.with_gas_limiter(get_gas_rules);
170 }
171
172 module = instrumentation_builder.instrument(module)?;
173
174 let data_section_size = utils::get_data_section_size(&module)?;
176 let global_section_size = utils::get_instantiated_global_section_size(&module)?;
177 let table_section_size = utils::get_instantiated_table_section_size(&module);
178 let element_section_size = utils::get_instantiated_element_section_size(&module)?;
179
180 module.strip_custom_sections();
181
182 let code = module.serialize()?;
183
184 let CodeTypeSectionSizes {
186 code_section,
187 type_section,
188 } = utils::get_code_type_sections_sizes(&code, config.type_section_len_limit)?;
189
190 let instantiated_section_sizes = InstantiatedSectionSizes::new(
191 code_section,
192 data_section_size,
193 global_section_size,
194 table_section_size,
195 element_section_size,
196 type_section,
197 );
198
199 let instrumented_code = InstrumentedCode::new(code, instantiated_section_sizes);
200
201 let metadata = CodeMetadata::new(
202 original_code.len() as u32,
203 exports.clone(),
204 static_pages,
205 stack_end,
206 InstrumentationStatus::Instrumented {
207 version: config.version,
208 code_len: instrumented_code.bytes().len() as u32,
209 },
210 );
211
212 Ok(Self {
213 original: original_code,
214 instrumented: instrumented_code,
215 metadata,
216 })
217 }
218
219 #[allow(clippy::too_many_arguments)]
296 pub fn try_new<R, GetRulesFn>(
297 original_code: Vec<u8>,
298 version: u32,
299 get_gas_rules: GetRulesFn,
300 stack_height: Option<u32>,
301 data_segments_amount_limit: Option<u32>,
302 type_section_len_limit: Option<u32>,
303 type_section_params_per_type_limit: Option<u32>,
304 syscall_kind: SyscallKind,
305 ) -> Result<Self, CodeError>
306 where
307 R: Rules,
308 GetRulesFn: FnMut(&Module) -> R,
309 {
310 Self::try_new_internal(
311 original_code,
312 Some(get_gas_rules),
313 TryNewCodeConfig {
314 version,
315 stack_height,
316 data_segments_amount_limit,
317 type_section_len_limit,
318 type_section_params_per_type_limit,
319 syscall_kind,
320 ..Default::default()
321 },
322 )
323 }
324
325 pub fn try_new_mock_const_or_no_rules(
327 original_code: Vec<u8>,
328 const_rules: bool,
329 config: TryNewCodeConfig,
330 ) -> Result<Self, CodeError> {
331 let get_gas_rules =
332 const_rules.then_some(|_module: &Module| CustomConstantCostRules::default());
333 Self::try_new_internal(original_code, get_gas_rules, config)
334 }
335
336 pub fn try_new_mock_with_rules<R, GetRulesFn>(
338 original_code: Vec<u8>,
339 get_gas_rules: GetRulesFn,
340 config: TryNewCodeConfig,
341 ) -> Result<Self, CodeError>
342 where
343 R: Rules,
344 GetRulesFn: FnMut(&Module) -> R,
345 {
346 Self::try_new_internal(original_code, Some(get_gas_rules), config)
347 }
348
349 pub fn original_code(&self) -> &[u8] {
351 &self.original
352 }
353
354 pub fn instrumented_code(&self) -> &InstrumentedCode {
356 &self.instrumented
357 }
358
359 pub fn metadata(&self) -> &CodeMetadata {
361 &self.metadata
362 }
363
364 pub fn into_parts(self) -> (Vec<u8>, InstrumentedCode, CodeMetadata) {
366 (self.original, self.instrumented, self.metadata)
367 }
368
369 pub fn into_instrumented_code_and_metadata(self) -> InstrumentedCodeAndMetadata {
371 InstrumentedCodeAndMetadata {
372 instrumented_code: self.instrumented,
373 metadata: self.metadata,
374 }
375 }
376}
377
378#[derive(Clone, Debug)]
380pub struct CodeAndId {
381 code: Code,
382 code_id: CodeId,
383}
384
385impl CodeAndId {
386 pub fn new(code: Code) -> Self {
388 let code_id = CodeId::generate(code.original_code());
389 Self { code, code_id }
390 }
391
392 pub fn from_parts_unchecked(code: Code, code_id: CodeId) -> Self {
394 debug_assert_eq!(code_id, CodeId::generate(code.original_code()));
395 Self { code, code_id }
396 }
397
398 pub unsafe fn from_incompatible_parts(code: Code, code_id: CodeId) -> Self {
403 Self { code, code_id }
404 }
405
406 pub fn code_id(&self) -> CodeId {
408 self.code_id
409 }
410
411 pub fn code(&self) -> &Code {
413 &self.code
414 }
415
416 pub fn into_parts(self) -> (Code, CodeId) {
418 (self.code, self.code_id)
419 }
420}
421
422#[derive(Clone, Debug)]
424pub struct InstrumentedCodeAndMetadata {
425 pub instrumented_code: InstrumentedCode,
427 pub metadata: CodeMetadata,
429}
430
431impl InstrumentedCodeAndMetadata {
432 pub fn into_parts(self) -> (InstrumentedCode, CodeMetadata) {
434 (self.instrumented_code, self.metadata)
435 }
436}
437
438impl From<Code> for InstrumentedCodeAndMetadata {
439 fn from(code: Code) -> Self {
440 let (_, instrumented_code, metadata) = code.into_parts();
441 Self {
442 instrumented_code,
443 metadata,
444 }
445 }
446}
447
448#[cfg(test)]
449mod tests {
450 use crate::{
451 code::{
452 Code, CodeError, DataSectionError, ExportError, GENERIC_OS_PAGE_SIZE, ImportError,
453 StackEndError, SyscallKind, TryNewCodeConfig, TypeSectionError, utils::REF_TYPE_SIZE,
454 },
455 gas_metering::CustomConstantCostRules,
456 };
457 use alloc::{format, vec::Vec};
458 use gear_wasm_instrument::{InstrumentationError, ModuleError, STACK_END_EXPORT_NAME};
459
460 fn wat2wasm_with_validate(s: &str, validate: bool) -> Vec<u8> {
461 let code = wat::parse_str(s).unwrap();
462 if validate {
463 wasmparser::validate(&code).unwrap();
464 }
465 code
466 }
467
468 fn wat2wasm(s: &str) -> Vec<u8> {
469 wat2wasm_with_validate(s, true)
470 }
471
472 macro_rules! assert_code_err {
473 ($res:expr, $expected:pat) => {
474 let err = $res.expect_err("Code::try_new must return an error");
475 let expected_err = stringify!($expected);
476 assert!(
477 matches!(err, $expected),
478 "Must receive {:?}, got {:?}",
479 expected_err,
480 err
481 );
482 };
483 }
484
485 fn try_new_code_from_wat_with_params(
486 wat: &str,
487 stack_height: Option<u32>,
488 data_segments_amount_limit: Option<u32>,
489 make_validation: bool,
490 ) -> Result<Code, CodeError> {
491 Code::try_new_mock_const_or_no_rules(
492 wat2wasm(wat),
493 true,
494 TryNewCodeConfig {
495 stack_height,
496 data_segments_amount_limit,
497 make_validation,
498 ..Default::default()
499 },
500 )
501 }
502
503 fn try_new_code_from_wat(wat: &str, stack_height: Option<u32>) -> Result<Code, CodeError> {
504 try_new_code_from_wat_with_params(wat, stack_height, None, true)
505 }
506
507 #[test]
508 fn reject_unknown_exports() {
509 let wat = r#"
510 (module
511 (import "env" "memory" (memory 1))
512 (export "this_import_is_unknown" (func $test))
513 (func $test)
514 )
515 "#;
516
517 assert_code_err!(
518 try_new_code_from_wat(wat, None),
519 CodeError::Export(ExportError::ExcessExport(0))
520 );
521 }
522
523 #[test]
524 fn required_fn_not_found() {
525 let wat = r#"
526 (module
527 (import "env" "memory" (memory 1))
528 (export "handle_signal" (func $handle_signal))
529 (func $handle_signal)
530 )
531 "#;
532
533 assert_code_err!(
534 try_new_code_from_wat(wat, None),
535 CodeError::Export(ExportError::RequiredExportNotFound)
536 );
537 }
538
539 #[test]
540 fn stack_limit_injection_works() {
541 let wat = r#"
542 (module
543 (import "env" "memory" (memory 1))
544 (export "init" (func $init))
545 (func $init)
546 )
547 "#;
548
549 let _ = try_new_code_from_wat(wat, Some(16 * 1024)).unwrap();
550 }
551
552 #[test]
553 fn data_segment_out_of_static_memory() {
554 let wat = r#"
556 (module
557 (import "env" "memory" (memory 1))
558 (export "init" (func $init))
559 (func $init)
560 (data (;0;) (i32.const 0x10000) "gear")
561 )
562 "#;
563
564 assert_code_err!(
565 try_new_code_from_wat(wat, None),
566 CodeError::DataSection(DataSectionError::EndAddressOutOfStaticMemory(
567 0x10000, 0x10003, 0x10000
568 ))
569 );
570
571 let wat = r#"
573 (module
574 (import "env" "memory" (memory 1))
575 (export "init" (func $init))
576 (func $init)
577 (data (;0;) (i32.const 0xfffd) "gear")
578 )
579 "#;
580
581 assert_code_err!(
582 try_new_code_from_wat(wat, None),
583 CodeError::DataSection(DataSectionError::EndAddressOutOfStaticMemory(
584 0xfffd, 0x10000, 0x10000
585 ))
586 );
587 }
588
589 #[test]
590 fn data_segment_out_of_u32() {
591 let wat = r#"
593 (module
594 (import "env" "memory" (memory 1))
595 (export "init" (func $init))
596 (func $init)
597 (data (;0;) (i32.const 0xffffffff) "gear")
598 )
599 "#;
600
601 assert_code_err!(
602 try_new_code_from_wat(wat, None),
603 CodeError::DataSection(DataSectionError::EndAddressOverflow(0xffffffff))
604 );
605 }
606
607 #[test]
608 fn data_segment_stack_overlaps() {
609 let wat = format!(
611 r#"
612 (module
613 (import "env" "memory" (memory 3))
614 (export "init" (func $init))
615 (func $init)
616 (data (;0;) (i32.const 0x10000) "gear")
617 (export "{STACK_END_EXPORT_NAME}" (global 0))
618 (global (mut i32) (i32.const 0x20000))
619 )"#
620 );
621
622 assert_code_err!(
623 try_new_code_from_wat(wat.as_str(), None),
624 CodeError::DataSection(DataSectionError::GearStackOverlaps(0x10000, 0x20000))
625 );
626 }
627
628 #[test]
629 fn data_section() {
630 let wat = format!(
631 r#"
632 (module
633 (import "env" "memory" (memory 3))
634 (export "init" (func $init))
635 (func $init)
636 (data (i32.const 0x20000) "gear")
637 (data (i32.const 0x10000) "") ;; empty data segment
638 (data (i32.const 0x1ffff) "gear") ;; overlapping other segments, also ok
639 (data (i32.const 0x2ffff) "g") ;; one byte before the end of memory
640 (export "{STACK_END_EXPORT_NAME}" (global 0))
641 (global (mut i32) (i32.const 0x10000))
642 )"#
643 );
644
645 try_new_code_from_wat(wat.as_str(), None).expect("Must be ok");
646 }
647
648 #[test]
649 fn check_mutable_global_exports_restriction() {
650 let wat = r#"
651 (module
652 (import "env" "memory" (memory 0))
653 (func $init)
654 (export "init" (func $init))
655 (export "global" (global 0))
656 (global (;0;) (mut i32) (i32.const 0))
657 )"#;
658
659 assert_code_err!(
660 try_new_code_from_wat(wat, None),
661 CodeError::Export(ExportError::MutableGlobalExport(0, 1))
662 );
663 }
664
665 #[test]
666 fn stack_end_initialization() {
667 let wat = format!(
668 r#"
669 (module
670 (import "env" "memory" (memory 1))
671 (import "env" "unknown" (global i32))
672 (func $init)
673 (export "init" (func $init))
674 (export "{STACK_END_EXPORT_NAME}" (global 1))
675 (global (mut i32) (global.get 0))
676 )"#
677 );
678
679 assert_code_err!(
680 try_new_code_from_wat(wat.as_str(), None),
681 CodeError::StackEnd(StackEndError::Initialization)
682 );
683 }
684
685 #[test]
686 fn stack_end_alignment() {
687 let wat = format!(
688 r#"
689 (module
690 (import "env" "memory" (memory 2))
691 (func $init)
692 (export "init" (func $init))
693 (export "{STACK_END_EXPORT_NAME}" (global 0))
694 (global (;0;) (mut i32) (i32.const 0x10001))
695 )"#
696 );
697
698 assert_code_err!(
699 try_new_code_from_wat(wat.as_str(), None),
700 CodeError::StackEnd(StackEndError::NotAligned(0x10001))
701 );
702 }
703
704 #[test]
705 fn stack_end_out_of_static_memory() {
706 let wat = format!(
707 r#"
708 (module
709 (import "env" "memory" (memory 1))
710 (func $init)
711 (export "init" (func $init))
712 (export "{STACK_END_EXPORT_NAME}" (global 0))
713 (global (;0;) (mut i32) (i32.const 0x20000))
714 )"#
715 );
716
717 assert_code_err!(
718 try_new_code_from_wat(wat.as_str(), None),
719 CodeError::StackEnd(StackEndError::OutOfStatic(0x20000, 0x10000))
720 );
721 }
722
723 #[test]
724 fn stack_end() {
725 let wat = format!(
726 r#"
727 (module
728 (import "env" "memory" (memory 1))
729 (func $init)
730 (export "init" (func $init))
731 (export "{STACK_END_EXPORT_NAME}" (global 0))
732 (global (;0;) (mut i32) (i32.const 0x10000))
733 )"#
734 );
735
736 let code = try_new_code_from_wat(wat.as_str(), None).expect("Must be ok");
737 assert_eq!(code.metadata().stack_end(), Some(1.into()));
738 }
739
740 #[test]
741 fn export_to_imported_function() {
742 let wat = r#"
743 (module
744 (import "env" "memory" (memory 1))
745 (import "env" "gr_leave" (func $gr_leave))
746 (export "init" (func $gr_leave))
747 (func)
748 )"#;
749
750 assert_code_err!(
751 try_new_code_from_wat(wat, None),
752 CodeError::Export(ExportError::ExportReferencesToImportFunction(0, 0))
753 );
754 }
755
756 #[test]
757 fn export_to_imported_global() {
758 let wat = r#"
759 (module
760 (import "env" "memory" (memory 1))
761 (import "env" "global" (global i32))
762 (export "init" (func 0))
763 (export "global" (global 0))
764 (func)
765 )"#;
766
767 assert_code_err!(
768 try_new_code_from_wat(wat, None),
769 CodeError::Export(ExportError::ExportReferencesToImportGlobal(1, 0))
770 );
771 }
772
773 #[test]
774 fn multi_memory_import() {
775 let wat = r#"
776 (module
777 (import "env" "memory" (memory 1))
778 (import "env" "memory2" (memory 2))
779 (export "init" (func $init))
780 (func $init)
781 )
782 "#;
783
784 let res = Code::try_new(
785 wat2wasm_with_validate(wat, false),
786 1,
787 |_| CustomConstantCostRules::default(),
788 None,
789 None,
790 None,
791 None,
792 SyscallKind::Vara,
793 );
794
795 assert_code_err!(res, CodeError::Validation(_));
796 }
797
798 #[test]
799 fn global_import() {
800 let wat = r#"
801 (module
802 (import "env" "memory" (memory 1))
803 (import "env" "unknown" (global $unknown i32))
804 (export "init" (func $init))
805 (func $init)
806 )
807 "#;
808
809 assert_code_err!(
810 try_new_code_from_wat(wat, None),
811 CodeError::Import(ImportError::UnexpectedImportKind {
812 kind: &"Global",
813 index: 1
814 })
815 );
816 }
817
818 #[test]
819 fn table_import() {
820 let wat = r#"
821 (module
822 (import "env" "memory" (memory 1))
823 (import "env" "unknown" (table $unknown 10 20 funcref))
824 (export "init" (func $init))
825 (func $init)
826 )
827 "#;
828
829 assert_code_err!(
830 try_new_code_from_wat(wat, None),
831 CodeError::Import(ImportError::UnexpectedImportKind {
832 kind: &"Table",
833 index: 1
834 })
835 );
836 }
837
838 #[test]
839 fn data_segments_amount_limit() {
840 const DATA_SEGMENTS_AMOUNT_LIMIT: u32 = 1024;
841
842 let segment = r#"(data (i32.const 0x0) "gear")"#;
843
844 let wat = format!(
845 r#"
846 (module
847 (import "env" "memory" (memory 1))
848 (func $init)
849 (export "init" (func $init))
850 {}
851 )
852 "#,
853 segment.repeat(1025)
854 );
855
856 assert_code_err!(
857 try_new_code_from_wat_with_params(
858 wat.as_str(),
859 None,
860 DATA_SEGMENTS_AMOUNT_LIMIT.into(),
861 true,
862 ),
863 CodeError::DataSection(DataSectionError::DataSegmentsAmountLimit {
864 limit: DATA_SEGMENTS_AMOUNT_LIMIT,
865 actual: 1025
866 })
867 );
868 }
869
870 #[test]
871 fn type_section_limits() {
872 const TYPE_SECTION_LEN_LIMIT: u32 = 16;
873 const PARAMS_PER_TYPE_LIMIT: u32 = 10;
874
875 fn try_new_with_type_limits(
876 wat: &str,
877 type_section_len_limit: Option<u32>,
878 type_section_params_per_type_limit: Option<u32>,
879 ) -> Result<Code, CodeError> {
880 Code::try_new_mock_const_or_no_rules(
881 wat2wasm(wat),
882 true,
883 TryNewCodeConfig {
884 type_section_len_limit,
885 type_section_params_per_type_limit,
886 stack_height: None,
887 make_validation: true,
888 ..Default::default()
889 },
890 )
891 }
892
893 let wat = r#"
894 (module
895 (import "env" "memory" (memory 1))
896 (func $init)
897 (export "init" (func $init))
898 (type (func (param i64 i64 i32 i32 i64 i32 i64 i64 i64 i32 i32 i64 i32 i64) (result i64)))
899 )"#;
900
901 assert_code_err!(
902 try_new_with_type_limits(wat, TYPE_SECTION_LEN_LIMIT.into(), None,),
903 CodeError::TypeSection(TypeSectionError::LengthLimitExceeded {
904 limit: TYPE_SECTION_LEN_LIMIT,
905 actual: 26,
906 })
907 );
908
909 assert_code_err!(
910 try_new_with_type_limits(wat, None, PARAMS_PER_TYPE_LIMIT.into(),),
911 CodeError::TypeSection(TypeSectionError::ParametersPerTypeLimitExceeded {
912 limit: PARAMS_PER_TYPE_LIMIT,
913 actual: 14,
914 })
915 );
916 }
917
918 #[test]
919 fn data_section_bytes() {
920 let wat = r#"
922 (module
923 (import "env" "memory" (memory 3))
924 (func $init)
925 (export "init" (func $init))
926 (data (i32.const 0x20000) "gear")
927 )
928 "#;
929
930 assert_eq!(
931 try_new_code_from_wat(wat, Some(1024))
932 .unwrap()
933 .instrumented_code()
934 .instantiated_section_sizes()
935 .data_section(),
936 GENERIC_OS_PAGE_SIZE,
937 );
938
939 let wat = r#"
941 (module
942 (import "env" "memory" (memory 3))
943 (func $init)
944 (export "init" (func $init))
945 (data (i32.const 0x0000) "gear")
946 (data (i32.const 0x1000) "gear")
947 )
948 "#;
949
950 assert_eq!(
951 try_new_code_from_wat(wat, Some(1024))
952 .unwrap()
953 .instrumented_code()
954 .instantiated_section_sizes()
955 .data_section(),
956 GENERIC_OS_PAGE_SIZE * 2,
957 );
958
959 let wat = r#"
961 (module
962 (import "env" "memory" (memory 3))
963 (func $init)
964 (export "init" (func $init))
965 (data (i32.const 0x0000) "gear")
966 (data (i32.const 0x10000) "gear")
967 )
968 "#;
969
970 assert_eq!(
971 try_new_code_from_wat(wat, Some(1024))
972 .unwrap()
973 .instrumented_code()
974 .instantiated_section_sizes()
975 .data_section(),
976 GENERIC_OS_PAGE_SIZE * 2,
977 );
978
979 let wat = r#"
981 (module
982 (import "env" "memory" (memory 3))
983 (func $init)
984 (export "init" (func $init))
985 (data (i32.const 0x0) "")
986 (data (i32.const 0x0) "")
987 )
988 "#;
989
990 assert_eq!(
991 try_new_code_from_wat(wat, Some(1024))
992 .unwrap()
993 .instrumented_code()
994 .instantiated_section_sizes()
995 .data_section(),
996 0,
997 );
998
999 let wat = r#"
1001 (module
1002 (import "env" "memory" (memory 3))
1003 (func $init)
1004 (export "init" (func $init))
1005 (data (i32.const 0x20000) "gear")
1006 (data (i32.const 0x20001) "gear")
1007 )
1008 "#;
1009
1010 assert_eq!(
1011 try_new_code_from_wat(wat, Some(1024))
1012 .unwrap()
1013 .instrumented_code()
1014 .instantiated_section_sizes()
1015 .data_section(),
1016 GENERIC_OS_PAGE_SIZE,
1017 );
1018
1019 let wat = format!(
1021 r#"
1022 (module
1023 (import "env" "memory" (memory 3))
1024 (func $init)
1025 (export "init" (func $init))
1026 (data (i32.const 0x20000) "{}")
1027 )
1028 "#,
1029 "a".repeat((GENERIC_OS_PAGE_SIZE + 1) as usize)
1030 );
1031
1032 assert_eq!(
1033 try_new_code_from_wat(&wat, Some(1024))
1034 .unwrap()
1035 .instrumented_code()
1036 .instantiated_section_sizes()
1037 .data_section(),
1038 GENERIC_OS_PAGE_SIZE * 2,
1039 );
1040
1041 let wat = format!(
1043 r#"
1044 (module
1045 (import "env" "memory" (memory 3))
1046 (func $init)
1047 (export "init" (func $init))
1048 (data (i32.const 0x20000) "{0}")
1049 (data (i32.const 0x23000) "{1}")
1050 )
1051 "#,
1052 "a".repeat((GENERIC_OS_PAGE_SIZE * 3) as usize),
1053 "b".repeat((GENERIC_OS_PAGE_SIZE) as usize)
1054 );
1055
1056 assert_eq!(
1057 try_new_code_from_wat(&wat, Some(1024))
1058 .unwrap()
1059 .instrumented_code()
1060 .instantiated_section_sizes()
1061 .data_section(),
1062 GENERIC_OS_PAGE_SIZE * 4,
1063 );
1064
1065 let wat = format!(
1067 r#"
1068 (module
1069 (import "env" "memory" (memory 3))
1070 (func $init)
1071 (export "init" (func $init))
1072 (data (i32.const 0x20000) "{0}")
1073 (data (i32.const 0x21000) "{1}")
1074 )
1075 "#,
1076 "a".repeat((GENERIC_OS_PAGE_SIZE * 2 + 1) as usize),
1077 "b".repeat((GENERIC_OS_PAGE_SIZE * 2 + 1) as usize)
1078 );
1079
1080 assert_eq!(
1081 try_new_code_from_wat(&wat, Some(1024))
1082 .unwrap()
1083 .instrumented_code()
1084 .instantiated_section_sizes()
1085 .data_section(),
1086 GENERIC_OS_PAGE_SIZE * 4,
1087 );
1088 }
1089
1090 #[test]
1091 fn code_section_bytes() {
1092 const INSTRUMENTATION_CODE_SIZE: u32 = 74;
1093
1094 let wat = r#"
1095 (module
1096 (import "env" "memory" (memory 3))
1097 (func $init)
1098 (export "init" (func $init))
1099 (func $sum (param i32 i32) (result i32)
1100 local.get 0
1101 local.get 1
1102 i32.add
1103 )
1104 )
1105 "#;
1106
1107 assert_eq!(
1108 try_new_code_from_wat(wat, Some(1024))
1109 .unwrap()
1110 .instrumented_code()
1111 .instantiated_section_sizes()
1112 .code_section(),
1113 INSTRUMENTATION_CODE_SIZE + 11,
1114 );
1115 }
1116
1117 #[test]
1118 fn global_section_bytes() {
1119 const INSTRUMENTATION_GLOBALS_SIZE: usize = size_of::<i32>() + size_of::<i64>();
1120
1121 let wat = r#"
1122 (module
1123 (import "env" "memory" (memory 3))
1124 (func $init)
1125 (export "init" (func $init))
1126 (global (mut i32) (i32.const 0))
1127 (global (mut i32) (i32.const 0))
1128 (global (mut i64) (i64.const 0))
1129 )
1130 "#;
1131
1132 assert_eq!(
1133 try_new_code_from_wat(wat, Some(1024))
1134 .unwrap()
1135 .instrumented_code()
1136 .instantiated_section_sizes()
1137 .global_section(),
1138 (INSTRUMENTATION_GLOBALS_SIZE + size_of::<i32>() * 2 + size_of::<i64>()) as u32,
1139 );
1140 }
1141
1142 #[test]
1143 fn element_section_bytes() {
1144 let wat = r#"
1145 (module
1146 (import "env" "memory" (memory 3))
1147 (func $init)
1148 (export "init" (func $init))
1149 (table 10 10 funcref)
1150 (elem (i32.const 1) 0 0 0 0)
1151 )
1152 "#;
1153
1154 assert_eq!(
1155 try_new_code_from_wat(wat, Some(1024))
1156 .unwrap()
1157 .instrumented_code()
1158 .instantiated_section_sizes()
1159 .table_section(),
1160 10 * REF_TYPE_SIZE,
1161 );
1162
1163 assert_eq!(
1164 try_new_code_from_wat(wat, Some(1024))
1165 .unwrap()
1166 .instrumented_code()
1167 .instantiated_section_sizes()
1168 .element_section(),
1169 REF_TYPE_SIZE * 4,
1170 );
1171 }
1172
1173 #[test]
1174 fn type_section_bytes() {
1175 let wat = r#"
1176 (module
1177 (import "env" "memory" (memory 3))
1178 (type (;35;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32)))
1179 (type (;36;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32)))
1180 (func $init)
1181 (export "init" (func $init))
1182 )
1183 "#;
1184
1185 assert_eq!(
1186 try_new_code_from_wat(wat, Some(1024))
1187 .unwrap()
1188 .instrumented_code()
1189 .instantiated_section_sizes()
1190 .type_section(),
1191 50,
1192 );
1193 }
1194
1195 #[test]
1196 fn unsupported_instruction() {
1197 let res = try_new_code_from_wat_with_params(
1199 r#"
1200 (module
1201 (import "env" "memory" (memory 0 1))
1202 (func (result f64)
1203 f64.const 10
1204 f64.const 3
1205 f64.div)
1206 (global i32 (i32.const 42))
1207 (func $init)
1208 (export "init" (func $init))
1209 )
1210 "#,
1211 Some(1024),
1212 None,
1213 false,
1215 );
1216
1217 assert!(matches!(
1218 res,
1219 Err(CodeError::Module(ModuleError::UnsupportedInstruction(_))),
1220 ));
1221
1222 let res = try_new_code_from_wat(
1224 r#"
1225 (module
1226 (import "env" "memory" (memory 0 1))
1227 (func (result i32)
1228 global.get 0
1229 memory.grow
1230 )
1231 (global i32 (i32.const 42))
1232 (func $init)
1233 (export "init" (func $init))
1234 )"#,
1235 Some(1024),
1236 );
1237
1238 assert!(matches!(
1239 res,
1240 Err(CodeError::Instrumentation(
1241 InstrumentationError::GasInjection
1242 ))
1243 ));
1244 }
1245
1246 mod custom_section_tests {
1247 use crate::code::get_custom_section_data;
1248 use alloc::{vec, vec::Vec};
1249 use wasm_encoder::{CustomSection, Module, TypeSection};
1250
1251 fn make_wasm_with_custom_sections(sections: &[(&str, &[u8])]) -> Vec<u8> {
1252 let mut module = Module::new();
1253 let mut types = TypeSection::new();
1254 types.ty().function(vec![], vec![]);
1255 module.section(&types);
1256
1257 for (name, data) in sections {
1258 module.section(&CustomSection {
1259 name: (*name).into(),
1260 data: (*data).into(),
1261 });
1262 }
1263
1264 module.finish()
1265 }
1266
1267 #[test]
1268 fn section_found() {
1269 let wasm = make_wasm_with_custom_sections(&[("sails:idl", b"hello idl")]);
1270 let result = get_custom_section_data(&wasm, "sails:idl");
1271
1272 assert_eq!(result.unwrap().unwrap(), b"hello idl");
1273 }
1274
1275 #[test]
1276 fn section_not_found() {
1277 let wasm = make_wasm_with_custom_sections(&[("other", b"data")]);
1278 let result = get_custom_section_data(&wasm, "sails:idl");
1279
1280 assert_eq!(result.unwrap(), None);
1281 }
1282
1283 #[test]
1284 fn first_match_returned() {
1285 let wasm = make_wasm_with_custom_sections(&[
1286 ("sails:idl", b"first"),
1287 ("sails:idl", b"second"),
1288 ]);
1289 let result = get_custom_section_data(&wasm, "sails:idl");
1290
1291 assert_eq!(result.unwrap().unwrap(), b"first");
1292 }
1293
1294 #[test]
1295 fn invalid_wasm() {
1296 let result = get_custom_section_data(b"not wasm at all", "sails:idl");
1297
1298 assert!(result.is_err());
1299 }
1300
1301 #[test]
1302 fn malformed_after_section_errors() {
1303 let mut wasm = make_wasm_with_custom_sections(&[("sails:idl", b"hello idl")]);
1304 wasm.extend_from_slice(b"trailing junk");
1305
1306 let result = get_custom_section_data(&wasm, "sails:idl");
1307
1308 assert!(result.is_err());
1309 }
1310
1311 #[test]
1312 fn empty_input() {
1313 let result = get_custom_section_data(b"", "sails:idl");
1314
1315 assert!(result.is_err());
1316 }
1317 }
1318
1319 #[test]
1320 fn instrumented_code_strips_custom_sections_but_original_keeps_them() {
1321 let wat = r#"
1324 (module
1325 (import "env" "memory" (memory 1))
1326 (export "init" (func $init))
1327 (func $init)
1328 )
1329 "#;
1330 let base_bytes = wat2wasm(wat);
1331
1332 let idl_payload: Vec<u8> = (0..64u8).collect();
1334 let module = gear_wasm_instrument::Module::new(&base_bytes).unwrap();
1335 let mut builder = gear_wasm_instrument::ModuleBuilder::from_module(module);
1336 builder.push_custom_section("sails:idl", idl_payload);
1337 let original_with_idl = builder.build();
1338
1339 assert!(
1341 original_with_idl
1342 .custom_sections
1343 .as_ref()
1344 .is_some_and(|cs| cs.iter().any(|(section, _)| section == "sails:idl")),
1345 "test fixture must contain the sails:idl custom section before instrumentation"
1346 );
1347 let original_with_idl = original_with_idl.serialize().unwrap();
1348
1349 let code = Code::try_new_mock_const_or_no_rules(
1351 original_with_idl.clone(),
1352 true,
1353 TryNewCodeConfig::default(),
1354 )
1355 .expect("valid gear program must instrument");
1356
1357 assert_eq!(code.original_code(), original_with_idl);
1359
1360 let instrumented = gear_wasm_instrument::Module::new(code.instrumented_code().bytes())
1362 .expect("instrumented code must parse");
1363 assert!(
1364 instrumented
1365 .custom_sections
1366 .as_ref()
1367 .is_none_or(|cs| cs.is_empty()),
1368 "InstrumentedCode must have no non-name custom sections"
1369 );
1370 }
1371}