cosmwasm_vm/
config.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
use std::{collections::HashSet, path::PathBuf};

use serde::{Deserialize, Serialize};

use crate::Size;

const DEFAULT_MEMORY_LIMIT: u32 = 512; // in pages
/// As of March 2023, on Juno mainnet the largest value for production contracts
/// is 485. Most are between 100 and 300.
const DEFAULT_TABLE_SIZE_LIMIT: u32 = 2500; // entries

/// We keep this number high since failing early gives less detailed error messages. Especially
/// when a user accidentally includes wasm-bindgen, they get a bunch of unsupported imports.
const DEFAULT_MAX_IMPORTS: usize = 100;

const DEFAULT_MAX_FUNCTIONS: usize = 20_000;

const DEFAULT_MAX_FUNCTION_PARAMS: usize = 100;

const DEFAULT_MAX_TOTAL_FUNCTION_PARAMS: usize = 10_000;

const DEFAULT_MAX_FUNCTION_RESULTS: usize = 1;

/// Various configurations for the VM.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Config {
    /// Configuration for limitations placed on Wasm files.
    /// This defines a few limits on the Wasm file that are checked during static validation before
    /// storing the Wasm file.
    pub wasm_limits: WasmLimits,

    /// Configuration for the cache.
    pub cache: CacheOptions,
}

impl Config {
    pub fn new(cache: CacheOptions) -> Self {
        Self {
            wasm_limits: WasmLimits::default(),
            cache,
        }
    }
}

/// Limits for static validation of Wasm files. These are checked before storing the Wasm file.
/// All limits are optional because they are coming from the Go-side and have default values.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[non_exhaustive]
pub struct WasmLimits {
    /// Maximum number of memory pages that a module can request.
    ///
    /// Every Wasm memory has an initial size and an optional maximum size,
    /// both measured in Wasm pages. This limit applies to the initial size.
    pub initial_memory_limit_pages: Option<u32>,
    /// The upper limit for the `max` value of each table. CosmWasm contracts have
    /// initial=max for 1 table. See
    ///
    /// ```plain
    /// $ wasm-objdump --section=table -x packages/vm/testdata/hackatom.wasm
    /// Section Details:
    ///
    /// Table[1]:
    /// - table[0] type=funcref initial=161 max=161
    /// ```
    ///
    pub table_size_limit_elements: Option<u32>,
    /// If the contract has more than this amount of imports, it will be rejected
    /// during static validation before even looking into the imports.
    pub max_imports: Option<usize>,

    /// The maximum number of functions a contract can have.
    /// Any contract with more functions than this will be rejected during static validation.
    pub max_functions: Option<usize>,

    /// The maximum number of parameters a Wasm function can have.
    pub max_function_params: Option<usize>,
    /// The maximum total number of parameters of all functions in the Wasm.
    /// For each function in the Wasm, take the number of parameters and sum all of these up.
    /// If that sum exceeds this limit, the Wasm will be rejected during static validation.
    ///
    /// Be careful when adjusting this limit, as it prevents an attack where a small Wasm file
    /// explodes in size when compiled.
    pub max_total_function_params: Option<usize>,

    /// The maximum number of results a Wasm function type can have.
    pub max_function_results: Option<usize>,
}

impl WasmLimits {
    pub fn initial_memory_limit_pages(&self) -> u32 {
        self.initial_memory_limit_pages
            .unwrap_or(DEFAULT_MEMORY_LIMIT)
    }

    pub fn table_size_limit_elements(&self) -> u32 {
        self.table_size_limit_elements
            .unwrap_or(DEFAULT_TABLE_SIZE_LIMIT)
    }

    pub fn max_imports(&self) -> usize {
        self.max_imports.unwrap_or(DEFAULT_MAX_IMPORTS)
    }

    pub fn max_functions(&self) -> usize {
        self.max_functions.unwrap_or(DEFAULT_MAX_FUNCTIONS)
    }

    pub fn max_function_params(&self) -> usize {
        self.max_function_params
            .unwrap_or(DEFAULT_MAX_FUNCTION_PARAMS)
    }

    pub fn max_total_function_params(&self) -> usize {
        self.max_total_function_params
            .unwrap_or(DEFAULT_MAX_TOTAL_FUNCTION_PARAMS)
    }

    pub fn max_function_results(&self) -> usize {
        self.max_function_results
            .unwrap_or(DEFAULT_MAX_FUNCTION_RESULTS)
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CacheOptions {
    /// The base directory of this cache.
    ///
    /// If this does not exist, it will be created. Not sure if this behaviour
    /// is desired but wasmd relies on it.
    pub base_dir: PathBuf,
    pub available_capabilities: HashSet<String>,
    /// Memory limit for the cache, in bytes.
    pub memory_cache_size_bytes: Size,
    /// Memory limit for instances, in bytes. Use a value that is divisible by the Wasm page size 65536,
    /// e.g. full MiBs.
    pub instance_memory_limit_bytes: Size,
}

impl CacheOptions {
    pub fn new(
        base_dir: impl Into<PathBuf>,
        available_capabilities: impl Into<HashSet<String>>,
        memory_cache_size_bytes: Size,
        instance_memory_limit_bytes: Size,
    ) -> Self {
        Self {
            base_dir: base_dir.into(),
            available_capabilities: available_capabilities.into(),
            memory_cache_size_bytes,
            instance_memory_limit_bytes,
        }
    }
}