soroban_env_host/budget/
wasmi_helper.rs

1use crate::{
2    budget::{AsBudget, Budget},
3    host::error::TryBorrowOrErr,
4    xdr::ContractCostType,
5    Host, HostError,
6};
7use wasmi::{errors, FuelConsumptionMode, FuelCosts, ResourceLimiter};
8
9pub(crate) struct WasmiLimits {
10    pub table_elements: u32,
11    pub instances: usize,
12    // The `tables` limit is only relevant if `wasmi_reference_type` is enabled
13    pub tables: usize,
14    // The `memories` limit is irrelevant. At the current version of WASM, at
15    // most one memory may be defined or imported in a single module
16    pub memories: usize,
17}
18
19pub(crate) const WASMI_LIMITS_CONFIG: WasmiLimits = WasmiLimits {
20    table_elements: 1000,
21    instances: 1,
22    tables: 1,
23    memories: 1,
24};
25
26impl ResourceLimiter for Host {
27    fn memory_growing(
28        &mut self,
29        current: usize,
30        desired: usize,
31        maximum: Option<usize>,
32    ) -> Result<bool, errors::MemoryError> {
33        let host_limit = self
34            .as_budget()
35            .get_mem_bytes_remaining()
36            .map_err(|_| errors::MemoryError::OutOfBoundsGrowth)?;
37
38        let delta = (desired as u64).saturating_sub(current as u64);
39        let allow = if delta > host_limit {
40            false
41        } else {
42            match maximum {
43                Some(max) => desired <= max,
44                None => true,
45            }
46        };
47
48        if allow {
49            #[cfg(any(test, feature = "testutils", feature = "bench"))]
50            {
51                self.as_budget()
52                    .track_wasm_mem_alloc(delta)
53                    .map_err(|_| errors::MemoryError::OutOfBoundsGrowth)?;
54            }
55
56            self.as_budget()
57                .charge(ContractCostType::MemAlloc, Some(delta))
58                .map(|_| true)
59                .map_err(|_| errors::MemoryError::OutOfBoundsGrowth)
60        } else {
61            Err(errors::MemoryError::OutOfBoundsGrowth)
62        }
63    }
64
65    fn table_growing(
66        &mut self,
67        current: u32,
68        desired: u32,
69        maximum: Option<u32>,
70    ) -> Result<bool, errors::TableError> {
71        let allow = if desired > WASMI_LIMITS_CONFIG.table_elements {
72            false
73        } else {
74            match maximum {
75                Some(max) => desired <= max,
76                None => true,
77            }
78        };
79        if allow {
80            Ok(allow)
81        } else {
82            Err(errors::TableError::GrowOutOfBounds {
83                maximum: maximum.unwrap_or(u32::MAX),
84                current,
85                delta: desired - current,
86            })
87        }
88    }
89
90    fn instances(&self) -> usize {
91        WASMI_LIMITS_CONFIG.instances
92    }
93
94    fn tables(&self) -> usize {
95        WASMI_LIMITS_CONFIG.tables
96    }
97
98    fn memories(&self) -> usize {
99        WASMI_LIMITS_CONFIG.memories
100    }
101}
102
103// These values are calibrated and set by us. Calibration is done with a given
104// wasmi version, and as long as the version is pinned, these values aren't
105// expected to change much.
106pub(crate) fn load_calibrated_fuel_costs() -> FuelCosts {
107    let mut fuel_costs = FuelCosts::default();
108    fuel_costs.base = 1;
109    fuel_costs.entity = 3;
110    fuel_costs.load = 2;
111    fuel_costs.store = 1;
112    fuel_costs.call = 67;
113    fuel_costs
114}
115
116pub(crate) fn get_wasmi_config(budget: &Budget) -> Result<wasmi::Config, HostError> {
117    let mut config = wasmi::Config::default();
118    let fuel_costs = budget.0.try_borrow_or_err()?.fuel_costs;
119
120    // Turn off most optional wasm features, leaving on some post-MVP features
121    // commonly enabled by Rust and Clang. Make sure all unused features are
122    // explicited turned off, so that we don't get "opted in" by a future wasmi
123    // version.
124    config
125        .consume_fuel(true)
126        .wasm_bulk_memory(true)
127        .wasm_mutable_global(true)
128        .wasm_sign_extension(true)
129        .wasm_saturating_float_to_int(false)
130        .wasm_multi_value(false)
131        .wasm_reference_types(false)
132        .wasm_tail_call(false)
133        .wasm_extended_const(false)
134        .floats(false)
135        .fuel_consumption_mode(FuelConsumptionMode::Eager)
136        .set_fuel_costs(fuel_costs);
137
138    Ok(config)
139}