sui_types/object/
bounded_visitor.rs

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