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