gear_core/code/
mod.rs

1// This file is part of Gear.
2
3// Copyright (C) 2021-2025 Gear Technologies Inc.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Module for checked code.
20
21use crate::{
22    gas_metering::{CustomConstantCostRules, Rules},
23    ids::{CodeId, prelude::*},
24};
25use alloc::vec::Vec;
26use gear_wasm_instrument::{GEAR_SUPPORTED_FEATURES, InstrumentationBuilder, Module};
27
28mod errors;
29mod instrumented;
30mod metadata;
31mod utils;
32
33pub use errors::*;
34pub use instrumented::*;
35pub use metadata::*;
36pub use utils::{ALLOWED_EXPORTS, MAX_WASM_PAGES_AMOUNT, REQUIRED_EXPORTS};
37
38use utils::CodeTypeSectionSizes;
39
40/// Generic OS page size. Approximated to 4KB as a most common value.
41const GENERIC_OS_PAGE_SIZE: u32 = 4096;
42
43/// Configuration for `Code::try_new_mock_`.
44/// By default all checks enabled.
45pub struct TryNewCodeConfig {
46    /// Instrumentation version
47    pub version: u32,
48    /// Stack height limit
49    pub stack_height: Option<u32>,
50    /// Limit of data section amount
51    pub data_segments_amount_limit: Option<u32>,
52    /// Limit on the number of tables.
53    pub table_amount_limit: Option<u32>,
54    /// Export `STACK_HEIGHT_EXPORT_NAME` global
55    pub export_stack_height: bool,
56    /// Check exports (wasm contains init or handle exports)
57    pub check_exports: bool,
58    /// Check imports (check that all imports are valid syscalls with correct signature)
59    pub check_imports: bool,
60    /// Check and canonize stack end
61    pub check_and_canonize_stack_end: bool,
62    /// Check mutable global exports
63    pub check_mut_global_exports: bool,
64    /// Check start section (not allowed for programs)
65    pub check_start_section: bool,
66    /// Check data section
67    pub check_data_section: bool,
68    /// Check table section
69    pub check_table_section: bool,
70    /// Make wasmparser validation
71    pub make_validation: bool,
72}
73
74impl TryNewCodeConfig {
75    /// New default config without exports checks.
76    pub fn new_no_exports_check() -> Self {
77        Self {
78            check_exports: false,
79            ..Default::default()
80        }
81    }
82}
83
84impl Default for TryNewCodeConfig {
85    fn default() -> Self {
86        Self {
87            version: 1,
88            stack_height: None,
89            data_segments_amount_limit: None,
90            table_amount_limit: None,
91            export_stack_height: false,
92            check_exports: true,
93            check_imports: true,
94            check_and_canonize_stack_end: true,
95            check_mut_global_exports: true,
96            check_start_section: true,
97            check_data_section: true,
98            check_table_section: true,
99            make_validation: true,
100        }
101    }
102}
103
104/// Contains original and instrumented binary code of a program.
105#[derive(Clone, Debug, PartialEq, Eq)]
106pub struct Code {
107    original: Vec<u8>,
108    instrumented: InstrumentedCode,
109    metadata: CodeMetadata,
110}
111
112impl Code {
113    /// Create the code by checking and instrumenting `original_code`.
114    fn try_new_internal<R, GetRulesFn>(
115        original_code: Vec<u8>,
116        get_gas_rules: Option<GetRulesFn>,
117        config: TryNewCodeConfig,
118    ) -> Result<Self, CodeError>
119    where
120        R: Rules,
121        GetRulesFn: FnMut(&Module) -> R,
122    {
123        if config.make_validation {
124            wasmparser::Validator::new_with_features(GEAR_SUPPORTED_FEATURES)
125                .validate_all(&original_code)
126                .map_err(CodeError::Validation)?;
127        }
128
129        let mut module = Module::new(&original_code)?;
130
131        let static_pages = utils::get_static_pages(&module)?;
132
133        // Canonize stack end before any changes in module
134        let stack_end = match config.check_and_canonize_stack_end {
135            true => utils::check_and_canonize_gear_stack_end(&mut module, static_pages)?,
136            false => None,
137        };
138
139        // Not changing steps
140        if config.check_data_section {
141            utils::check_data_section(
142                &module,
143                static_pages,
144                stack_end,
145                config.data_segments_amount_limit,
146            )?;
147        }
148        if config.check_mut_global_exports {
149            utils::check_mut_global_exports(&module)?;
150        }
151        if config.check_start_section {
152            utils::check_start_section(&module)?;
153        }
154        if config.check_exports {
155            utils::check_exports(&module)?;
156        }
157        if config.check_imports {
158            utils::check_imports(&module)?;
159        }
160
161        // Get exports set before instrumentations.
162        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        // Use instrumented module to get section sizes.
175        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        let code = module.serialize()?;
181
182        // Use instrumented code to get section sizes.
183        let CodeTypeSectionSizes {
184            code_section,
185            type_section,
186        } = utils::get_code_type_sections_sizes(&code)?;
187
188        let instantiated_section_sizes = InstantiatedSectionSizes::new(
189            code_section,
190            data_section_size,
191            global_section_size,
192            table_section_size,
193            element_section_size,
194            type_section,
195        );
196
197        let instrumented_code = InstrumentedCode::new(code, instantiated_section_sizes);
198
199        let metadata = CodeMetadata::new(
200            original_code.len() as u32,
201            exports.clone(),
202            static_pages,
203            stack_end,
204            InstrumentationStatus::Instrumented {
205                version: config.version,
206                code_len: instrumented_code.bytes().len() as u32,
207            },
208        );
209
210        Ok(Self {
211            original: original_code,
212            instrumented: instrumented_code,
213            metadata,
214        })
215    }
216
217    /// Create the code by checking and instrumenting `original_code`.
218    /// Main logic of instrumentation can be represented by this example:
219    /// Let's take a code:
220    /// ```wasm
221    /// (module
222    ///    (import "env" "memory" (memory 1))
223    ///    (export "init" (func $init))
224    ///    (func $f1
225    ///       <-- f1 code -->
226    ///    )
227    ///    (func $f2
228    ///       if (i32.eqz (i32.const 0))
229    ///          <-- some code -->
230    ///       else
231    ///          <-- some code -->
232    ///       end
233    ///    )
234    ///    (func $f3
235    ///       <-- f3 code -->
236    ///    )
237    ///    (func $init
238    ///       call $f1
239    ///       call $f2
240    ///       call $f3
241    ///       <-- some code -->
242    ///    )
243    /// )
244    /// ```
245    ///
246    /// After instrumentation code will be like:
247    /// ```wasm
248    /// (module
249    ///   (import "env" "memory" (memory 1))
250    ///   (export "init" (func $init_export))
251    ///   (func $gas_charge
252    ///      <-- gas charge impl --> ;; see utils/wasm-instrument/src/lib.rs
253    ///   )
254    ///   (func $f1
255    ///      i32.const 123
256    ///      call $gas_charge
257    ///      <-- f1 code -->
258    ///   )
259    ///   (func $f2
260    ///      i32.const 123
261    ///      call $gas_charge
262    ///      if (i32.eqz (i32.const 0))
263    ///         i32.const 1
264    ///         call $gas_charge
265    ///         <-- some code -->
266    ///      else
267    ///         i32.const 2
268    ///         call $gas_charge
269    ///         <-- some code -->
270    ///      end
271    ///   )
272    ///   (func $init
273    ///      i32.const 123
274    ///      call $gas_charge
275    ///      ;; stack limit check impl see in wasm_instrument::inject_stack_limiter
276    ///      <-- stack limit check and increase -->
277    ///      call $f1
278    ///      <-- stack limit decrease -->
279    ///      <-- stack limit check and increase -->
280    ///      call $f2
281    ///      <-- stack limit decrease -->
282    ///      <-- some code -->
283    ///   )
284    ///   (func $init_export
285    ///      i32.const 123
286    ///      call $gas_charge
287    ///      <-- stack limit check and increase -->
288    ///      call $init
289    ///      <-- stack limit decrease -->
290    ///   )
291    /// )
292    /// ```
293    pub fn try_new<R, GetRulesFn>(
294        original_code: Vec<u8>,
295        version: u32,
296        get_gas_rules: GetRulesFn,
297        stack_height: Option<u32>,
298        data_segments_amount_limit: Option<u32>,
299    ) -> Result<Self, CodeError>
300    where
301        R: Rules,
302        GetRulesFn: FnMut(&Module) -> R,
303    {
304        Self::try_new_internal(
305            original_code,
306            Some(get_gas_rules),
307            TryNewCodeConfig {
308                version,
309                stack_height,
310                data_segments_amount_limit,
311                ..Default::default()
312            },
313        )
314    }
315
316    /// Create new code for mock goals with const or no instrumentation rules.
317    pub fn try_new_mock_const_or_no_rules(
318        original_code: Vec<u8>,
319        const_rules: bool,
320        config: TryNewCodeConfig,
321    ) -> Result<Self, CodeError> {
322        let get_gas_rules =
323            const_rules.then_some(|_module: &Module| CustomConstantCostRules::default());
324        Self::try_new_internal(original_code, get_gas_rules, config)
325    }
326
327    /// Create new code for mock goals with custom instrumentation rules.
328    pub fn try_new_mock_with_rules<R, GetRulesFn>(
329        original_code: Vec<u8>,
330        get_gas_rules: GetRulesFn,
331        config: TryNewCodeConfig,
332    ) -> Result<Self, CodeError>
333    where
334        R: Rules,
335        GetRulesFn: FnMut(&Module) -> R,
336    {
337        Self::try_new_internal(original_code, Some(get_gas_rules), config)
338    }
339
340    /// Returns the original code.
341    pub fn original_code(&self) -> &[u8] {
342        &self.original
343    }
344
345    /// Returns the instrumented code.
346    pub fn instrumented_code(&self) -> &InstrumentedCode {
347        &self.instrumented
348    }
349
350    /// Returns the code metadata.
351    pub fn metadata(&self) -> &CodeMetadata {
352        &self.metadata
353    }
354
355    /// Consumes this instance and returns the instrumented and raw binary codes.
356    pub fn into_parts(self) -> (Vec<u8>, InstrumentedCode, CodeMetadata) {
357        (self.original, self.instrumented, self.metadata)
358    }
359
360    /// Consumes this instance and returns the instrumented code and metadata struct.
361    pub fn into_instrumented_code_and_metadata(self) -> InstrumentedCodeAndMetadata {
362        InstrumentedCodeAndMetadata {
363            instrumented_code: self.instrumented,
364            metadata: self.metadata,
365        }
366    }
367}
368
369/// The newtype contains the Code instance and the corresponding id (hash).
370#[derive(Clone, Debug)]
371pub struct CodeAndId {
372    code: Code,
373    code_id: CodeId,
374}
375
376impl CodeAndId {
377    /// Calculates the id (hash) of the raw binary code and creates new instance.
378    pub fn new(code: Code) -> Self {
379        let code_id = CodeId::generate(code.original_code());
380        Self { code, code_id }
381    }
382
383    /// Creates the instance from the precalculated hash without checks.
384    pub fn from_parts_unchecked(code: Code, code_id: CodeId) -> Self {
385        debug_assert_eq!(code_id, CodeId::generate(code.original_code()));
386        Self { code, code_id }
387    }
388
389    /// Creates the instance from the hash and incompatible with that hash code.
390    ///
391    /// # Safety
392    /// USE FOR TEST PURPOSES ONLY.
393    pub unsafe fn from_incompatible_parts(code: Code, code_id: CodeId) -> Self {
394        Self { code, code_id }
395    }
396
397    /// Returns corresponding id (hash) for the code.
398    pub fn code_id(&self) -> CodeId {
399        self.code_id
400    }
401
402    /// Returns reference to Code.
403    pub fn code(&self) -> &Code {
404        &self.code
405    }
406
407    /// Decomposes this instance.
408    pub fn into_parts(self) -> (Code, CodeId) {
409        (self.code, self.code_id)
410    }
411}
412
413/// The newtype contains the InstrumentedCode instance and the corresponding metadata.
414#[derive(Clone, Debug)]
415pub struct InstrumentedCodeAndMetadata {
416    /// Instrumented code.
417    pub instrumented_code: InstrumentedCode,
418    /// Code metadata.
419    pub metadata: CodeMetadata,
420}
421
422impl InstrumentedCodeAndMetadata {
423    /// Decomposes this instance into parts.
424    pub fn into_parts(self) -> (InstrumentedCode, CodeMetadata) {
425        (self.instrumented_code, self.metadata)
426    }
427}
428
429impl From<Code> for InstrumentedCodeAndMetadata {
430    fn from(code: Code) -> Self {
431        let (_, instrumented_code, metadata) = code.into_parts();
432        Self {
433            instrumented_code,
434            metadata,
435        }
436    }
437}
438
439#[cfg(test)]
440mod tests {
441    use crate::{
442        code::{
443            Code, CodeError, DataSectionError, ExportError, GENERIC_OS_PAGE_SIZE, ImportError,
444            StackEndError, TryNewCodeConfig, utils::REF_TYPE_SIZE,
445        },
446        gas_metering::CustomConstantCostRules,
447    };
448    use alloc::{format, vec::Vec};
449    use gear_wasm_instrument::{InstrumentationError, ModuleError, STACK_END_EXPORT_NAME};
450
451    fn wat2wasm_with_validate(s: &str, validate: bool) -> Vec<u8> {
452        let code = wat::parse_str(s).unwrap();
453        if validate {
454            wasmparser::validate(&code).unwrap();
455        }
456        code
457    }
458
459    fn wat2wasm(s: &str) -> Vec<u8> {
460        wat2wasm_with_validate(s, true)
461    }
462
463    macro_rules! assert_code_err {
464        ($res:expr, $expected:pat) => {
465            let err = $res.expect_err("Code::try_new must return an error");
466            let expected_err = stringify!($expected);
467            assert!(
468                matches!(err, $expected),
469                "Must receive {:?}, got {:?}",
470                expected_err,
471                err
472            );
473        };
474    }
475
476    fn try_new_code_from_wat_with_params(
477        wat: &str,
478        stack_height: Option<u32>,
479        data_segments_amount_limit: Option<u32>,
480        make_validation: bool,
481    ) -> Result<Code, CodeError> {
482        Code::try_new_mock_const_or_no_rules(
483            wat2wasm(wat),
484            true,
485            TryNewCodeConfig {
486                stack_height,
487                data_segments_amount_limit,
488                make_validation,
489                ..Default::default()
490            },
491        )
492    }
493
494    fn try_new_code_from_wat(wat: &str, stack_height: Option<u32>) -> Result<Code, CodeError> {
495        try_new_code_from_wat_with_params(wat, stack_height, None, true)
496    }
497
498    #[test]
499    fn reject_unknown_exports() {
500        let wat = r#"
501            (module
502                (import "env" "memory" (memory 1))
503                (export "this_import_is_unknown" (func $test))
504                (func $test)
505            )
506        "#;
507
508        assert_code_err!(
509            try_new_code_from_wat(wat, None),
510            CodeError::Export(ExportError::ExcessExport(0))
511        );
512    }
513
514    #[test]
515    fn required_fn_not_found() {
516        let wat = r#"
517            (module
518                (import "env" "memory" (memory 1))
519                (export "handle_signal" (func $handle_signal))
520                (func $handle_signal)
521            )
522        "#;
523
524        assert_code_err!(
525            try_new_code_from_wat(wat, None),
526            CodeError::Export(ExportError::RequiredExportNotFound)
527        );
528    }
529
530    #[test]
531    fn stack_limit_injection_works() {
532        let wat = r#"
533            (module
534                (import "env" "memory" (memory 1))
535                (export "init" (func $init))
536                (func $init)
537            )
538        "#;
539
540        let _ = try_new_code_from_wat(wat, Some(16 * 1024)).unwrap();
541    }
542
543    #[test]
544    fn data_segment_out_of_static_memory() {
545        // Data segment end address is out of static memory.
546        let wat = r#"
547            (module
548                (import "env" "memory" (memory 1))
549                (export "init" (func $init))
550                (func $init)
551                (data (;0;) (i32.const 0x10000) "gear")
552            )
553        "#;
554
555        assert_code_err!(
556            try_new_code_from_wat(wat, None),
557            CodeError::DataSection(DataSectionError::EndAddressOutOfStaticMemory(
558                0x10000, 0x10003, 0x10000
559            ))
560        );
561
562        // Data segment last byte is next byte after static memory
563        let wat = r#"
564            (module
565                (import "env" "memory" (memory 1))
566                (export "init" (func $init))
567                (func $init)
568                (data (;0;) (i32.const 0xfffd) "gear")
569            )
570        "#;
571
572        assert_code_err!(
573            try_new_code_from_wat(wat, None),
574            CodeError::DataSection(DataSectionError::EndAddressOutOfStaticMemory(
575                0xfffd, 0x10000, 0x10000
576            ))
577        );
578    }
579
580    #[test]
581    fn data_segment_out_of_u32() {
582        // Data segment end address is out of possible 32 bits address space.
583        let wat = r#"
584            (module
585                (import "env" "memory" (memory 1))
586                (export "init" (func $init))
587                (func $init)
588                (data (;0;) (i32.const 0xffffffff) "gear")
589            )
590        "#;
591
592        assert_code_err!(
593            try_new_code_from_wat(wat, None),
594            CodeError::DataSection(DataSectionError::EndAddressOverflow(0xffffffff))
595        );
596    }
597
598    #[test]
599    fn data_segment_stack_overlaps() {
600        // Data segment overlaps gear stack.
601        let wat = format!(
602            r#"
603            (module
604                (import "env" "memory" (memory 3))
605                (export "init" (func $init))
606                (func $init)
607                (data (;0;) (i32.const 0x10000) "gear")
608                (export "{STACK_END_EXPORT_NAME}" (global 0))
609                (global (mut i32) (i32.const 0x20000))
610            )"#
611        );
612
613        assert_code_err!(
614            try_new_code_from_wat(wat.as_str(), None),
615            CodeError::DataSection(DataSectionError::GearStackOverlaps(0x10000, 0x20000))
616        );
617    }
618
619    #[test]
620    fn data_section() {
621        let wat = format!(
622            r#"
623            (module
624                (import "env" "memory" (memory 3))
625                (export "init" (func $init))
626                (func $init)
627                (data (i32.const 0x20000) "gear")
628                (data (i32.const 0x10000) "")     ;; empty data segment
629                (data (i32.const 0x1ffff) "gear") ;; overlapping other segments, also ok
630                (data (i32.const 0x2ffff) "g")    ;; one byte before the end of memory
631                (export "{STACK_END_EXPORT_NAME}" (global 0))
632                (global (mut i32) (i32.const 0x10000))
633            )"#
634        );
635
636        try_new_code_from_wat(wat.as_str(), None).expect("Must be ok");
637    }
638
639    #[test]
640    fn check_mutable_global_exports_restriction() {
641        let wat = r#"
642            (module
643                (import "env" "memory" (memory 0))
644                (func $init)
645                (export "init" (func $init))
646                (export "global" (global 0))
647                (global (;0;) (mut i32) (i32.const 0))
648            )"#;
649
650        assert_code_err!(
651            try_new_code_from_wat(wat, None),
652            CodeError::Export(ExportError::MutableGlobalExport(0, 1))
653        );
654    }
655
656    #[test]
657    fn stack_end_initialization() {
658        let wat = format!(
659            r#"
660            (module
661                (import "env" "memory" (memory 1))
662                (import "env" "unknown" (global i32))
663                (func $init)
664                (export "init" (func $init))
665                (export "{STACK_END_EXPORT_NAME}" (global 1))
666                (global (mut i32) (global.get 0))
667            )"#
668        );
669
670        assert_code_err!(
671            try_new_code_from_wat(wat.as_str(), None),
672            CodeError::StackEnd(StackEndError::Initialization)
673        );
674    }
675
676    #[test]
677    fn stack_end_alignment() {
678        let wat = format!(
679            r#"
680            (module
681                (import "env" "memory" (memory 2))
682                (func $init)
683                (export "init" (func $init))
684                (export "{STACK_END_EXPORT_NAME}" (global 0))
685                (global (;0;) (mut i32) (i32.const 0x10001))
686            )"#
687        );
688
689        assert_code_err!(
690            try_new_code_from_wat(wat.as_str(), None),
691            CodeError::StackEnd(StackEndError::NotAligned(0x10001))
692        );
693    }
694
695    #[test]
696    fn stack_end_out_of_static_memory() {
697        let wat = format!(
698            r#"
699            (module
700                (import "env" "memory" (memory 1))
701                (func $init)
702                (export "init" (func $init))
703                (export "{STACK_END_EXPORT_NAME}" (global 0))
704                (global (;0;) (mut i32) (i32.const 0x20000))
705            )"#
706        );
707
708        assert_code_err!(
709            try_new_code_from_wat(wat.as_str(), None),
710            CodeError::StackEnd(StackEndError::OutOfStatic(0x20000, 0x10000))
711        );
712    }
713
714    #[test]
715    fn stack_end() {
716        let wat = format!(
717            r#"
718            (module
719                (import "env" "memory" (memory 1))
720                (func $init)
721                (export "init" (func $init))
722                (export "{STACK_END_EXPORT_NAME}" (global 0))
723                (global (;0;) (mut i32) (i32.const 0x10000))
724            )"#
725        );
726
727        let code = try_new_code_from_wat(wat.as_str(), None).expect("Must be ok");
728        assert_eq!(code.metadata().stack_end(), Some(1.into()));
729    }
730
731    #[test]
732    fn export_to_imported_function() {
733        let wat = r#"
734            (module
735                (import "env" "memory" (memory 1))
736                (import "env" "gr_leave" (func $gr_leave))
737                (export "init" (func $gr_leave))
738                (func)
739            )"#;
740
741        assert_code_err!(
742            try_new_code_from_wat(wat, None),
743            CodeError::Export(ExportError::ExportReferencesToImportFunction(0, 0))
744        );
745    }
746
747    #[test]
748    fn export_to_imported_global() {
749        let wat = r#"
750            (module
751                (import "env" "memory" (memory 1))
752                (import "env" "global" (global i32))
753                (export "init" (func 0))
754                (export "global" (global 0))
755                (func)
756            )"#;
757
758        assert_code_err!(
759            try_new_code_from_wat(wat, None),
760            CodeError::Export(ExportError::ExportReferencesToImportGlobal(1, 0))
761        );
762    }
763
764    #[test]
765    fn multi_memory_import() {
766        let wat = r#"
767            (module
768                (import "env" "memory" (memory 1))
769                (import "env" "memory2" (memory 2))
770                (export "init" (func $init))
771                (func $init)
772            )
773        "#;
774
775        let res = Code::try_new(
776            wat2wasm_with_validate(wat, false),
777            1,
778            |_| CustomConstantCostRules::default(),
779            None,
780            None,
781        );
782
783        assert_code_err!(res, CodeError::Validation(_));
784    }
785
786    #[test]
787    fn global_import() {
788        let wat = r#"
789            (module
790                (import "env" "memory" (memory 1))
791                (import "env" "unknown" (global $unknown i32))
792                (export "init" (func $init))
793                (func $init)
794            )
795        "#;
796
797        assert_code_err!(
798            try_new_code_from_wat(wat, None),
799            CodeError::Import(ImportError::UnexpectedImportKind {
800                kind: &"Global",
801                index: 1
802            })
803        );
804    }
805
806    #[test]
807    fn table_import() {
808        let wat = r#"
809            (module
810                (import "env" "memory" (memory 1))
811                (import "env" "unknown" (table $unknown 10 20 funcref))
812                (export "init" (func $init))
813                (func $init)
814            )
815        "#;
816
817        assert_code_err!(
818            try_new_code_from_wat(wat, None),
819            CodeError::Import(ImportError::UnexpectedImportKind {
820                kind: &"Table",
821                index: 1
822            })
823        );
824    }
825
826    #[test]
827    fn data_segments_amount_limit() {
828        const DATA_SEGMENTS_AMOUNT_LIMIT: u32 = 1024;
829
830        let segment = r#"(data (i32.const 0x0) "gear")"#;
831
832        let wat = format!(
833            r#"
834            (module
835                (import "env" "memory" (memory 1))
836                (func $init)
837                (export "init" (func $init))
838                {}
839            )
840        "#,
841            segment.repeat(1025)
842        );
843
844        assert_code_err!(
845            try_new_code_from_wat_with_params(
846                wat.as_str(),
847                None,
848                DATA_SEGMENTS_AMOUNT_LIMIT.into(),
849                true,
850            ),
851            CodeError::DataSection(DataSectionError::DataSegmentsAmountLimit {
852                limit: DATA_SEGMENTS_AMOUNT_LIMIT,
853                actual: 1025
854            })
855        );
856    }
857
858    #[test]
859    fn data_section_bytes() {
860        // Smoke
861        let wat = r#"
862            (module
863                (import "env" "memory" (memory 3))
864                (func $init)
865                (export "init" (func $init))
866                (data (i32.const 0x20000) "gear")
867            )
868        "#;
869
870        assert_eq!(
871            try_new_code_from_wat(wat, Some(1024))
872                .unwrap()
873                .instrumented_code()
874                .instantiated_section_sizes()
875                .data_section(),
876            GENERIC_OS_PAGE_SIZE,
877        );
878
879        // 2 adjacent
880        let wat = r#"
881            (module
882                (import "env" "memory" (memory 3))
883                (func $init)
884                (export "init" (func $init))
885                (data (i32.const 0x0000) "gear")
886                (data (i32.const 0x1000) "gear")
887            )
888        "#;
889
890        assert_eq!(
891            try_new_code_from_wat(wat, Some(1024))
892                .unwrap()
893                .instrumented_code()
894                .instantiated_section_sizes()
895                .data_section(),
896            GENERIC_OS_PAGE_SIZE * 2,
897        );
898
899        // 2 not adjacent
900        let wat = r#"
901            (module
902                (import "env" "memory" (memory 3))
903                (func $init)
904                (export "init" (func $init))
905                (data (i32.const  0x0000) "gear")
906                (data (i32.const 0x10000) "gear")
907            )
908        "#;
909
910        assert_eq!(
911            try_new_code_from_wat(wat, Some(1024))
912                .unwrap()
913                .instrumented_code()
914                .instantiated_section_sizes()
915                .data_section(),
916            GENERIC_OS_PAGE_SIZE * 2,
917        );
918
919        // 2 zero sized
920        let wat = r#"
921            (module
922                (import "env" "memory" (memory 3))
923                (func $init)
924                (export "init" (func $init))
925                (data (i32.const 0x0) "")
926                (data (i32.const 0x0) "")
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            0,
937        );
938
939        // Overlap
940        let wat = r#"
941            (module
942                (import "env" "memory" (memory 3))
943                (func $init)
944                (export "init" (func $init))
945                (data (i32.const 0x20000) "gear")
946                (data (i32.const 0x20001) "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,
957        );
958
959        // Big segment
960        let wat = format!(
961            r#"
962            (module
963                (import "env" "memory" (memory 3))
964                (func $init)
965                (export "init" (func $init))
966                (data (i32.const 0x20000) "{}")
967            )
968        "#,
969            "a".repeat((GENERIC_OS_PAGE_SIZE + 1) as usize)
970        );
971
972        assert_eq!(
973            try_new_code_from_wat(&wat, Some(1024))
974                .unwrap()
975                .instrumented_code()
976                .instantiated_section_sizes()
977                .data_section(),
978            GENERIC_OS_PAGE_SIZE * 2,
979        );
980
981        // 2 big segments
982        let wat = format!(
983            r#"
984            (module
985                (import "env" "memory" (memory 3))
986                (func $init)
987                (export "init" (func $init))
988                (data (i32.const 0x20000) "{0}")
989                (data (i32.const 0x23000) "{1}")
990            )
991            "#,
992            "a".repeat((GENERIC_OS_PAGE_SIZE * 3) as usize),
993            "b".repeat((GENERIC_OS_PAGE_SIZE) as usize)
994        );
995
996        assert_eq!(
997            try_new_code_from_wat(&wat, Some(1024))
998                .unwrap()
999                .instrumented_code()
1000                .instantiated_section_sizes()
1001                .data_section(),
1002            GENERIC_OS_PAGE_SIZE * 4,
1003        );
1004
1005        // 2 big segments overlap
1006        let wat = format!(
1007            r#"
1008            (module
1009                (import "env" "memory" (memory 3))
1010                (func $init)
1011                (export "init" (func $init))
1012                (data (i32.const 0x20000) "{0}")
1013                (data (i32.const 0x21000) "{1}")
1014            )
1015            "#,
1016            "a".repeat((GENERIC_OS_PAGE_SIZE * 2 + 1) as usize),
1017            "b".repeat((GENERIC_OS_PAGE_SIZE * 2 + 1) as usize)
1018        );
1019
1020        assert_eq!(
1021            try_new_code_from_wat(&wat, Some(1024))
1022                .unwrap()
1023                .instrumented_code()
1024                .instantiated_section_sizes()
1025                .data_section(),
1026            GENERIC_OS_PAGE_SIZE * 4,
1027        );
1028    }
1029
1030    #[test]
1031    fn code_section_bytes() {
1032        const INSTRUMENTATION_CODE_SIZE: u32 = 74;
1033
1034        let wat = r#"
1035            (module
1036                (import "env" "memory" (memory 3))
1037                (func $init)
1038                (export "init" (func $init))
1039                (func $sum (param i32 i32) (result i32)
1040                    local.get 0
1041                    local.get 1
1042                    i32.add
1043                )
1044            )
1045        "#;
1046
1047        assert_eq!(
1048            try_new_code_from_wat(wat, Some(1024))
1049                .unwrap()
1050                .instrumented_code()
1051                .instantiated_section_sizes()
1052                .code_section(),
1053            INSTRUMENTATION_CODE_SIZE + 11,
1054        );
1055    }
1056
1057    #[test]
1058    fn global_section_bytes() {
1059        const INSTRUMENTATION_GLOBALS_SIZE: usize = size_of::<i32>() + size_of::<i64>();
1060
1061        let wat = r#"
1062            (module
1063                (import "env" "memory" (memory 3))
1064                (func $init)
1065                (export "init" (func $init))
1066                (global (mut i32) (i32.const 0))
1067                (global (mut i32) (i32.const 0))
1068                (global (mut i64) (i64.const 0))
1069            )
1070        "#;
1071
1072        assert_eq!(
1073            try_new_code_from_wat(wat, Some(1024))
1074                .unwrap()
1075                .instrumented_code()
1076                .instantiated_section_sizes()
1077                .global_section(),
1078            (INSTRUMENTATION_GLOBALS_SIZE + size_of::<i32>() * 2 + size_of::<i64>()) as u32,
1079        );
1080    }
1081
1082    #[test]
1083    fn element_section_bytes() {
1084        let wat = r#"
1085            (module
1086                (import "env" "memory" (memory 3))
1087                (func $init)
1088                (export "init" (func $init))
1089                (table 10 10 funcref)
1090                (elem (i32.const 1) 0 0 0 0)
1091            )
1092        "#;
1093
1094        assert_eq!(
1095            try_new_code_from_wat(wat, Some(1024))
1096                .unwrap()
1097                .instrumented_code()
1098                .instantiated_section_sizes()
1099                .table_section(),
1100            10 * REF_TYPE_SIZE,
1101        );
1102
1103        assert_eq!(
1104            try_new_code_from_wat(wat, Some(1024))
1105                .unwrap()
1106                .instrumented_code()
1107                .instantiated_section_sizes()
1108                .element_section(),
1109            REF_TYPE_SIZE * 4,
1110        );
1111    }
1112
1113    #[test]
1114    fn type_section_bytes() {
1115        let wat = r#"
1116            (module
1117                (import "env" "memory" (memory 3))
1118                (type (;35;) (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32)))
1119                (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)))
1120                (func $init)
1121                (export "init" (func $init))
1122            )
1123        "#;
1124
1125        assert_eq!(
1126            try_new_code_from_wat(wat, Some(1024))
1127                .unwrap()
1128                .instrumented_code()
1129                .instantiated_section_sizes()
1130                .type_section(),
1131            50,
1132        );
1133    }
1134
1135    #[test]
1136    fn unsupported_instruction() {
1137        // floats
1138        let res = try_new_code_from_wat_with_params(
1139            r#"
1140            (module
1141                (import "env" "memory" (memory 0 1))
1142                (func (result f64)
1143                    f64.const 10
1144                    f64.const 3
1145                    f64.div)
1146                (global i32 (i32.const 42))
1147                (func $init)
1148                (export "init" (func $init))
1149            )
1150            "#,
1151            Some(1024),
1152            None,
1153            // check not only `wasmparser` validator denies forbidden instructions
1154            false,
1155        );
1156
1157        assert!(matches!(
1158            res,
1159            Err(CodeError::Module(ModuleError::UnsupportedInstruction(_))),
1160        ));
1161
1162        // memory grow
1163        let res = try_new_code_from_wat(
1164            r#"
1165            (module
1166                (import "env" "memory" (memory 0 1))
1167                (func (result i32)
1168                    global.get 0
1169                    memory.grow
1170                )
1171                (global i32 (i32.const 42))
1172                (func $init)
1173                (export "init" (func $init))
1174        )"#,
1175            Some(1024),
1176        );
1177
1178        assert!(matches!(
1179            res,
1180            Err(CodeError::Instrumentation(
1181                InstrumentationError::GasInjection
1182            ))
1183        ));
1184    }
1185}