iota_types/object/
bounded_visitor.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use anyhow::bail;
6use move_core_types::{
7    account_address::AccountAddress,
8    annotated_value as A,
9    annotated_visitor::{self, StructDriver, ValueDriver, VecDriver, Visitor},
10    language_storage::TypeTag,
11    u256::U256,
12};
13use once_cell::sync::Lazy;
14use tracing::info;
15
16/// Visitor to deserialize annotated values or structs, bounding the size
17/// budgeted for types and field names in the output. The visitor does not bound
18/// the size of values, because they are assumed to already be bounded by
19/// execution.
20pub struct BoundedVisitor {
21    /// Budget left to spend on field names and types.
22    bound: usize,
23}
24
25#[derive(thiserror::Error, Debug)]
26pub enum Error {
27    #[error(transparent)]
28    Visitor(#[from] annotated_visitor::Error),
29
30    #[error("Deserialized value too large")]
31    OutOfBudget,
32}
33
34/// Environment variable to override the default budget for deserialization.
35/// This can be set at runtime to change the maximum size of values that can be
36/// deserialized.
37const MAX_BOUND_VAR_NAME: &str = "MAX_ANNOTATED_VALUE_SIZE";
38
39/// Default budget for deserialization -- we're okay to spend an extra ~1MiB on
40/// types and field information per value.
41const DEFAULT_MAX_BOUND: usize = 1024 * 1024;
42
43/// Budget for deserialization into an annotated Move value. This sets the
44/// numbers of bytes that we are willing to spend on field names, type names
45/// (etc) when deserializing a Move value into an annotated Move value.
46///
47/// Bounded deserialization is intended for use outside of the validator, and so
48/// uses a fixed bound that needs to be set at startup rather than one that is
49/// configured as part of the protocol.
50///
51/// If the environment variable `MAX_ANNOTATED_VALUE_SIZE` is unset we default
52/// to `DEFAULT_MAX_BOUND` which allows ~1MiB additional space usage on types
53/// and field information per value.
54///
55/// This is read only once and after that the value is cached. To change this
56/// value you will need to restart the process with the new value set (or the
57/// value unset if you wish to use the `DEFAULT_MAX_BOUND` value).
58static MAX_BOUND: Lazy<usize> = Lazy::new(|| {
59    let max_bound_opt = std::env::var(MAX_BOUND_VAR_NAME)
60        .ok()
61        .and_then(|s| s.parse().ok());
62    if let Some(max_bound) = max_bound_opt {
63        info!(
64            "Using custom value for '{}' max bound: {}",
65            MAX_BOUND_VAR_NAME, max_bound
66        );
67        max_bound
68    } else {
69        info!(
70            "Using default value for '{}' -- max bound: {}",
71            MAX_BOUND_VAR_NAME, DEFAULT_MAX_BOUND
72        );
73        DEFAULT_MAX_BOUND
74    }
75});
76
77impl BoundedVisitor {
78    fn new(bound: usize) -> Self {
79        Self { bound }
80    }
81
82    /// Deserialize `bytes` as a `MoveValue` with layout `layout`. Can fail if
83    /// the bytes do not represent a value with this layout, or if the
84    /// deserialized value exceeds the field/type size budget.
85    pub fn deserialize_value(
86        bytes: &[u8],
87        layout: &A::MoveTypeLayout,
88    ) -> anyhow::Result<A::MoveValue> {
89        let mut visitor = Self::default();
90        A::MoveValue::visit_deserialize(bytes, layout, &mut visitor)
91    }
92
93    /// Deserialize `bytes` as a `MoveStruct` with layout `layout`. Can fail if
94    /// the bytes do not represent a struct with this layout, or if the
95    /// deserialized struct exceeds the field/type size budget.
96    pub fn deserialize_struct(
97        bytes: &[u8],
98        layout: &A::MoveStructLayout,
99    ) -> anyhow::Result<A::MoveStruct> {
100        let mut visitor = Self::default();
101        let A::MoveValue::Struct(struct_) =
102            A::MoveStruct::visit_deserialize(bytes, layout, &mut visitor)?
103        else {
104            bail!("Expected to deserialize a struct");
105        };
106        Ok(struct_)
107    }
108
109    /// Deduct `size` from the overall budget. Errors if `size` exceeds the
110    /// current budget.
111    fn debit(&mut self, size: usize) -> Result<(), Error> {
112        if self.bound < size {
113            Err(Error::OutOfBudget)
114        } else {
115            self.bound -= size;
116            Ok(())
117        }
118    }
119
120    /// Deduct the estimated size of `tag` from the overall budget. Errors if
121    /// its size exceeds the current budget. The estimated size is
122    /// proportional to the representation of that type in memory, but does
123    /// not match its exact size.
124    fn debit_type_size(&mut self, tag: &TypeTag) -> Result<(), Error> {
125        use TypeTag as TT;
126        let mut frontier = vec![tag];
127        while let Some(tag) = frontier.pop() {
128            match tag {
129                TT::Bool
130                | TT::U8
131                | TT::U16
132                | TT::U32
133                | TT::U64
134                | TT::U128
135                | TT::U256
136                | TT::Address
137                | TT::Signer => self.debit(8)?,
138
139                TT::Vector(inner) => {
140                    self.debit(8)?;
141                    frontier.push(inner);
142                }
143
144                TT::Struct(tag) => {
145                    self.debit(8 + AccountAddress::LENGTH + tag.module.len() + tag.name.len())?;
146                    frontier.extend(tag.type_params.iter());
147                }
148            }
149        }
150
151        Ok(())
152    }
153}
154
155impl<'b, 'l> Visitor<'b, 'l> for BoundedVisitor {
156    type Value = A::MoveValue;
157    type Error = Error;
158
159    fn visit_u8(
160        &mut self,
161        _driver: &ValueDriver<'_, 'b, 'l>,
162        value: u8,
163    ) -> Result<Self::Value, Self::Error> {
164        Ok(A::MoveValue::U8(value))
165    }
166
167    fn visit_u16(
168        &mut self,
169        _driver: &ValueDriver<'_, 'b, 'l>,
170        value: u16,
171    ) -> Result<Self::Value, Self::Error> {
172        Ok(A::MoveValue::U16(value))
173    }
174
175    fn visit_u32(
176        &mut self,
177        _driver: &ValueDriver<'_, 'b, 'l>,
178        value: u32,
179    ) -> Result<Self::Value, Self::Error> {
180        Ok(A::MoveValue::U32(value))
181    }
182
183    fn visit_u64(
184        &mut self,
185        _driver: &ValueDriver<'_, 'b, 'l>,
186        value: u64,
187    ) -> Result<Self::Value, Self::Error> {
188        Ok(A::MoveValue::U64(value))
189    }
190
191    fn visit_u128(
192        &mut self,
193        _driver: &ValueDriver<'_, 'b, 'l>,
194        value: u128,
195    ) -> Result<Self::Value, Self::Error> {
196        Ok(A::MoveValue::U128(value))
197    }
198
199    fn visit_u256(
200        &mut self,
201        _driver: &ValueDriver<'_, 'b, 'l>,
202        value: U256,
203    ) -> Result<Self::Value, Self::Error> {
204        Ok(A::MoveValue::U256(value))
205    }
206
207    fn visit_bool(
208        &mut self,
209        _driver: &ValueDriver<'_, 'b, 'l>,
210        value: bool,
211    ) -> Result<Self::Value, Self::Error> {
212        Ok(A::MoveValue::Bool(value))
213    }
214
215    fn visit_address(
216        &mut self,
217        _driver: &ValueDriver<'_, 'b, 'l>,
218        value: AccountAddress,
219    ) -> Result<Self::Value, Self::Error> {
220        Ok(A::MoveValue::Address(value))
221    }
222
223    fn visit_signer(
224        &mut self,
225        _driver: &ValueDriver<'_, 'b, 'l>,
226        value: AccountAddress,
227    ) -> Result<Self::Value, Self::Error> {
228        Ok(A::MoveValue::Signer(value))
229    }
230
231    fn visit_vector(
232        &mut self,
233        driver: &mut VecDriver<'_, 'b, 'l>,
234    ) -> Result<Self::Value, Self::Error> {
235        let mut elems = vec![];
236        while let Some(elem) = driver.next_element(self)? {
237            elems.push(elem);
238        }
239
240        Ok(A::MoveValue::Vector(elems))
241    }
242
243    fn visit_struct(
244        &mut self,
245        driver: &mut StructDriver<'_, 'b, 'l>,
246    ) -> Result<Self::Value, Self::Error> {
247        let tag = driver.struct_layout().type_.clone().into();
248
249        self.debit_type_size(&tag)?;
250        for field in driver.struct_layout().fields.iter() {
251            self.debit(field.name.len())?;
252        }
253
254        let mut fields = vec![];
255        while let Some((field, elem)) = driver.next_field(self)? {
256            fields.push((field.name.clone(), elem));
257        }
258
259        let TypeTag::Struct(type_) = tag else {
260            unreachable!("SAFETY: tag was derived from a StructTag.");
261        };
262
263        Ok(A::MoveValue::Struct(A::MoveStruct {
264            type_: *type_,
265            fields,
266        }))
267    }
268
269    fn visit_variant(
270        &mut self,
271        driver: &mut annotated_visitor::VariantDriver<'_, 'b, 'l>,
272    ) -> Result<Self::Value, Self::Error> {
273        let type_ = driver.enum_layout().type_.clone().into();
274
275        self.debit_type_size(&type_)?;
276        self.debit(driver.variant_name().len())?;
277
278        for field in driver.variant_layout() {
279            self.debit(field.name.len())?;
280        }
281
282        let mut fields = vec![];
283        while let Some((field, elem)) = driver.next_field(self)? {
284            fields.push((field.name.clone(), elem));
285        }
286
287        let TypeTag::Struct(type_) = type_ else {
288            unreachable!("SAFETY: type_ was derived from a StructTag.");
289        };
290
291        Ok(A::MoveValue::Variant(A::MoveVariant {
292            type_: *type_,
293            fields,
294            variant_name: driver.variant_name().to_owned(),
295            tag: driver.tag(),
296        }))
297    }
298}
299
300impl Default for BoundedVisitor {
301    fn default() -> Self {
302        Self::new(*MAX_BOUND)
303    }
304}
305
306#[cfg(test)]
307pub(crate) mod tests {
308    use std::str::FromStr;
309
310    use expect_test::expect;
311    use move_core_types::{identifier::Identifier, language_storage::StructTag};
312
313    use super::*;
314
315    #[test]
316    fn test_success() {
317        use A::{MoveTypeLayout as T, MoveValue as V};
318
319        let type_layout = layout_(
320            "0x0::foo::Bar",
321            vec![
322                ("a", T::U64),
323                ("b", T::Vector(Box::new(T::U64))),
324                ("c", layout_("0x0::foo::Baz", vec![("d", T::U64)])),
325            ],
326        );
327
328        let value = value_(
329            "0x0::foo::Bar",
330            vec![
331                ("a", V::U64(42)),
332                ("b", V::Vector(vec![V::U64(43)])),
333                ("c", value_("0x0::foo::Baz", vec![("d", V::U64(44))])),
334            ],
335        );
336
337        let bytes = serialize(value.clone());
338
339        let mut visitor = BoundedVisitor::new(1000);
340        let deser = A::MoveValue::visit_deserialize(&bytes, &type_layout, &mut visitor).unwrap();
341        assert_eq!(value, deser);
342    }
343
344    #[test]
345    fn test_env_variable_override() {
346        use A::{MoveTypeLayout as T, MoveValue as V};
347
348        let type_layout = layout_(
349            "0x0::foo::Bar",
350            vec![
351                ("a", T::U64),
352                ("b", T::Vector(Box::new(T::U64))),
353                ("c", layout_("0x0::foo::Baz", vec![("d", T::U64)])),
354            ],
355        );
356
357        let value = value_(
358            "0x0::foo::Bar",
359            vec![
360                ("a", V::U64(42)),
361                ("b", V::Vector(vec![V::U64(43)])),
362                ("c", value_("0x0::foo::Baz", vec![("d", V::U64(44))])),
363            ],
364        );
365
366        let bytes = serialize(value.clone());
367
368        let before_value = std::env::var(MAX_BOUND_VAR_NAME).ok();
369
370        std::env::set_var(MAX_BOUND_VAR_NAME, "10");
371        let mut visitor = BoundedVisitor::default();
372        let err = A::MoveValue::visit_deserialize(&bytes, &type_layout, &mut visitor).unwrap_err();
373        let expect = expect!["Deserialized value too large"];
374        expect.assert_eq(&err.to_string());
375
376        // Should be unaffected as we already set the value, so this should still fail.
377        std::env::set_var(MAX_BOUND_VAR_NAME, "1000");
378        let mut visitor = BoundedVisitor::default();
379        let err = A::MoveValue::visit_deserialize(&bytes, &type_layout, &mut visitor).unwrap_err();
380        let expect = expect!["Deserialized value too large"];
381        expect.assert_eq(&err.to_string());
382
383        // set the value back to what it was before if it was previously set, otherwise
384        // unset it.
385        if let Some(previous_value) = before_value {
386            std::env::set_var(MAX_BOUND_VAR_NAME, previous_value);
387        } else {
388            std::env::remove_var(MAX_BOUND_VAR_NAME);
389        }
390
391        // Should still fail as the static value is already set.
392        let mut visitor = BoundedVisitor::default();
393        let err = A::MoveValue::visit_deserialize(&bytes, &type_layout, &mut visitor).unwrap_err();
394        let expect = expect!["Deserialized value too large"];
395        expect.assert_eq(&err.to_string());
396    }
397
398    #[test]
399    fn test_too_deep() {
400        use A::{MoveTypeLayout as T, MoveValue as V};
401
402        let mut layout = T::U64;
403        let mut value = V::U64(42);
404
405        const DEPTH: usize = 10;
406        for _ in 0..DEPTH {
407            layout = layout_("0x0::foo::Bar", vec![("f", layout)]);
408            value = value_("0x0::foo::Bar", vec![("f", value)]);
409        }
410
411        let bound = DEPTH * (8 + 32 + "foo".len() + "Bar".len() + "f".len());
412        let bytes = serialize(value.clone());
413
414        let mut visitor = BoundedVisitor::new(bound);
415        let deser = A::MoveValue::visit_deserialize(&bytes, &layout, &mut visitor).unwrap();
416        assert_eq!(deser, value);
417
418        let mut visitor = BoundedVisitor::new(bound - 1);
419        let err = A::MoveValue::visit_deserialize(&bytes, &layout, &mut visitor).unwrap_err();
420
421        let expect = expect!["Deserialized value too large"];
422        expect.assert_eq(&err.to_string());
423    }
424
425    #[test]
426    fn test_too_wide() {
427        use A::{MoveTypeLayout as T, MoveValue as V};
428
429        const WIDTH: usize = 10;
430        let mut idents = vec![];
431        let mut fields = vec![];
432        let mut values = vec![];
433
434        for i in 0..WIDTH {
435            idents.push(format!("f{}", i));
436        }
437
438        for (i, id) in idents.iter().enumerate() {
439            let layout = layout_("0x0::foo::Baz", vec![("f", T::U64)]);
440            let value = value_("0x0::foo::Baz", vec![("f", V::U64(i as u64))]);
441
442            fields.push((id.as_str(), layout));
443            values.push((id.as_str(), value));
444        }
445
446        let layout = layout_("0x0::foo::Bar", fields);
447        let value = value_("0x0::foo::Bar", values);
448
449        let outer = 8 + 32 + "foo".len() + "Bar".len();
450        let inner = WIDTH * ("fx".len() + 8 + 32 + "foo".len() + "Baz".len() + "f".len());
451        let bound = outer + inner;
452
453        let bytes = serialize(value.clone());
454
455        let mut visitor = BoundedVisitor::new(bound);
456        let deser = A::MoveValue::visit_deserialize(&bytes, &layout, &mut visitor).unwrap();
457        assert_eq!(deser, value);
458
459        let mut visitor = BoundedVisitor::new(bound - 1);
460        let err = A::MoveValue::visit_deserialize(&bytes, &layout, &mut visitor).unwrap_err();
461
462        let expect = expect!["Deserialized value too large"];
463        expect.assert_eq(&err.to_string());
464    }
465
466    #[test]
467    fn test_big_types() {
468        use A::{MoveTypeLayout as T, MoveValue as V};
469
470        let big_mod_ = "m".repeat(128);
471        let big_name = "T".repeat(128);
472        let big_type = format!("0x0::{big_mod_}::{big_name}");
473
474        let layout = layout_(big_type.as_str(), vec![("f", T::U64)]);
475        let value = value_(big_type.as_str(), vec![("f", V::U64(42))]);
476
477        let bound = 8 + 32 + big_mod_.len() + big_name.len() + "f".len();
478        let bytes = serialize(value.clone());
479
480        let mut visitor = BoundedVisitor::new(bound);
481        let deser = A::MoveValue::visit_deserialize(&bytes, &layout, &mut visitor).unwrap();
482        assert_eq!(deser, value);
483
484        let mut visitor = BoundedVisitor::new(bound - 1);
485        let err = A::MoveValue::visit_deserialize(&bytes, &layout, &mut visitor).unwrap_err();
486
487        let expect = expect!["Deserialized value too large"];
488        expect.assert_eq(&err.to_string());
489    }
490
491    type Variant<'s> = (&'s str, u16);
492    type FieldLayout<'s> = (&'s str, A::MoveTypeLayout);
493
494    fn ident_(name: &str) -> Identifier {
495        Identifier::new(name).unwrap()
496    }
497
498    /// Create a struct value for test purposes.
499    pub(crate) fn value_(rep: &str, fields: Vec<(&str, A::MoveValue)>) -> A::MoveValue {
500        let type_ = StructTag::from_str(rep).unwrap();
501        let fields = fields
502            .into_iter()
503            .map(|(name, value)| (ident_(name), value))
504            .collect();
505
506        A::MoveValue::Struct(A::MoveStruct::new(type_, fields))
507    }
508
509    /// Create a struct layout for test purposes.
510    pub(crate) fn layout_(rep: &str, fields: Vec<FieldLayout<'_>>) -> A::MoveTypeLayout {
511        let type_ = StructTag::from_str(rep).unwrap();
512        let fields = fields
513            .into_iter()
514            .map(|(name, layout)| A::MoveFieldLayout::new(ident_(name), layout))
515            .collect();
516
517        A::MoveTypeLayout::Struct(Box::new(A::MoveStructLayout { type_, fields }))
518    }
519
520    /// Create a variant value for test purposes.
521    pub(crate) fn variant_(
522        rep: &str,
523        name: &str,
524        tag: u16,
525        fields: Vec<(&str, A::MoveValue)>,
526    ) -> A::MoveValue {
527        let type_ = StructTag::from_str(rep).unwrap();
528        let fields = fields
529            .into_iter()
530            .map(|(name, value)| (ident_(name), value))
531            .collect();
532
533        A::MoveValue::Variant(A::MoveVariant {
534            type_,
535            variant_name: ident_(name),
536            tag,
537            fields,
538        })
539    }
540
541    /// Create an enum layout for test purposes.
542    pub(crate) fn enum_(
543        rep: &str,
544        variants: Vec<(Variant<'_>, Vec<FieldLayout<'_>>)>,
545    ) -> A::MoveTypeLayout {
546        let type_ = StructTag::from_str(rep).unwrap();
547        let variants = variants
548            .into_iter()
549            .map(|((name, tag), fields)| {
550                let fields = fields
551                    .into_iter()
552                    .map(|(name, layout)| A::MoveFieldLayout::new(ident_(name), layout))
553                    .collect();
554                ((ident_(name), tag), fields)
555            })
556            .collect();
557
558        A::MoveTypeLayout::Enum(Box::new(A::MoveEnumLayout { type_, variants }))
559    }
560
561    /// BCS encode Move value.
562    fn serialize(value: A::MoveValue) -> Vec<u8> {
563        value.clone().undecorate().simple_serialize().unwrap()
564    }
565}