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