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