diff --git a/Cargo.lock b/Cargo.lock index 77a3f21ff214d..6de49945dc988 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,9 +10,9 @@ checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" [[package]] name = "addr2line" -version = "0.17.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -4522,15 +4522,15 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", "cfg-if", "libc", - "miniz_oxide 0.5.3", + "miniz_oxide 0.7.1", "object", "rustc-demangle", ] @@ -6170,10 +6170,14 @@ dependencies = [ "bcs 0.1.4", "hex", "itertools", + "memory-stats", "move-binary-format", "move-core-types", "move-package", "move-symbol-pool", + "move-vm-runtime", + "move-vm-test-utils", + "move-vm-types", "once_cell", "project-root", "proptest", @@ -6965,9 +6969,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "git2" @@ -8731,6 +8735,15 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.8" @@ -9721,6 +9734,7 @@ name = "move-vm-runtime" version = "0.1.0" dependencies = [ "anyhow", + "backtrace", "better_any", "fail 0.4.0", "hex", @@ -10181,9 +10195,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.29.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 927105639977a..cecb8aad906d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -530,6 +530,7 @@ log = "0.4.17" lru = "0.7.5" lz4 = "1.24.0" maplit = "1.0.2" +memory-stats = "1.1.0" merlin = "3" mime = "0.3.16" mirai-annotations = "1.12.0" diff --git a/aptos-move/aptos-vm/src/verifier/transaction_arg_validation.rs b/aptos-move/aptos-vm/src/verifier/transaction_arg_validation.rs index 66ab95774d59c..cd14136ad59ec 100644 --- a/aptos-move/aptos-vm/src/verifier/transaction_arg_validation.rs +++ b/aptos-move/aptos-vm/src/verifier/transaction_arg_validation.rs @@ -255,6 +255,7 @@ fn construct_arg( match ty { Bool | U8 | U16 | U32 | U64 | U128 | U256 | Address => Ok(arg), Vector(_) | Struct(_) | StructInstantiation(_, _) => { + let initial_cursor_len = arg.len(); let mut cursor = Cursor::new(&arg[..]); let mut new_arg = vec![]; let mut max_invocations = 10; // Read from config in the future @@ -263,13 +264,14 @@ fn construct_arg( ty, allowed_structs, &mut cursor, + initial_cursor_len, gas_meter, &mut max_invocations, &mut new_arg, )?; // Check cursor has parsed everything // Unfortunately, is_empty is only enabled in nightly, so we check this way. - if cursor.position() != arg.len() as u64 { + if cursor.position() != initial_cursor_len as u64 { return Err(VMStatus::error( StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT, Some(String::from( @@ -298,6 +300,7 @@ pub(crate) fn recursively_construct_arg( ty: &Type, allowed_structs: &ConstructorMap, cursor: &mut Cursor<&[u8]>, + initial_cursor_len: usize, gas_meter: &mut impl GasMeter, max_invocations: &mut u64, arg: &mut Vec, @@ -315,6 +318,7 @@ pub(crate) fn recursively_construct_arg( inner, allowed_structs, cursor, + initial_cursor_len, gas_meter, max_invocations, arg, @@ -340,6 +344,7 @@ pub(crate) fn recursively_construct_arg( constructor, allowed_structs, cursor, + initial_cursor_len, gas_meter, max_invocations, )?); @@ -365,6 +370,7 @@ fn validate_and_construct( constructor: &FunctionId, allowed_structs: &ConstructorMap, cursor: &mut Cursor<&[u8]>, + initial_cursor_len: usize, gas_meter: &mut impl GasMeter, max_invocations: &mut u64, ) -> Result, VMStatus> { @@ -394,8 +400,21 @@ fn validate_and_construct( VMStatus::error(StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT, None) } }; - // short cut for the utf8 constructor, which is a special case + // Short cut for the utf8 constructor, which is a special case. let len = get_len(cursor)?; + if !cursor + .position() + .checked_add(len as u64) + .is_some_and(|l| l <= initial_cursor_len as u64) + { + // We need to make sure we do not allocate more bytes than + // needed. + return Err(VMStatus::error( + StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT, + Some("String argument is too long".to_string()), + )); + } + let mut arg = vec![]; read_n_bytes(len, cursor, &mut arg)?; std::str::from_utf8(&arg).map_err(|_| constructor_error())?; @@ -418,6 +437,7 @@ fn validate_and_construct( ¶m_type.subst(&instantiation.type_arguments).unwrap(), allowed_structs, cursor, + initial_cursor_len, gas_meter, max_invocations, &mut arg, @@ -457,12 +477,28 @@ fn serialize_uleb128(mut x: usize, dest: &mut Vec) { } fn read_n_bytes(n: usize, src: &mut Cursor<&[u8]>, dest: &mut Vec) -> Result<(), VMStatus> { - let len = dest.len(); - dest.resize(len + n, 0); - src.read_exact(&mut dest[len..]).map_err(|_| { + let deserialization_error = |msg: &str| -> VMStatus { VMStatus::error( StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT, - Some(String::from("Couldn't read bytes")), + Some(msg.to_string()), ) - }) + }; + let len = dest.len(); + + // It is safer to limit the length under some big (but still reasonable + // number). + const MAX_NUM_BYTES: usize = 1_000_000; + if !len.checked_add(n).is_some_and(|s| s <= MAX_NUM_BYTES) { + return Err(deserialization_error(&format!( + "Couldn't read bytes: maximum limit of {} bytes exceeded", + MAX_NUM_BYTES + ))); + } + + // Ensure we have enough capacity for resizing. + dest.try_reserve(len + n) + .map_err(|e| deserialization_error(&format!("Couldn't read bytes: {}", e)))?; + dest.resize(len + n, 0); + src.read_exact(&mut dest[len..]) + .map_err(|_| deserialization_error("Couldn't read bytes")) } diff --git a/aptos-move/e2e-move-tests/Cargo.toml b/aptos-move/e2e-move-tests/Cargo.toml index 36eebf6e94bfe..92fd0be38f1b1 100644 --- a/aptos-move/e2e-move-tests/Cargo.toml +++ b/aptos-move/e2e-move-tests/Cargo.toml @@ -34,10 +34,14 @@ aptos-writeset-generator = { workspace = true } bcs = { workspace = true } hex = { workspace = true } itertools = { workspace = true } +memory-stats = { workspace = true } move-binary-format = { workspace = true } move-core-types = { workspace = true } move-package = { workspace = true } move-symbol-pool = { workspace = true } +move-vm-runtime = { workspace = true } +move-vm-test-utils = { workspace = true } +move-vm-types = { workspace = true } once_cell = { workspace = true } project-root = { workspace = true } proptest = { workspace = true } diff --git a/aptos-move/e2e-move-tests/src/tests/mod.rs b/aptos-move/e2e-move-tests/src/tests/mod.rs index eda2c8aac84aa..727280bdb377e 100644 --- a/aptos-move/e2e-move-tests/src/tests/mod.rs +++ b/aptos-move/e2e-move-tests/src/tests/mod.rs @@ -23,6 +23,7 @@ mod memory_quota; mod metadata; mod mint_nft; mod missing_gas_parameter; +mod module_bomb; mod module_event; mod new_integer_types; mod nft_dao; diff --git a/aptos-move/e2e-move-tests/src/tests/module_bomb.mv b/aptos-move/e2e-move-tests/src/tests/module_bomb.mv new file mode 100644 index 0000000000000..02286dc2fd662 Binary files /dev/null and b/aptos-move/e2e-move-tests/src/tests/module_bomb.mv differ diff --git a/aptos-move/e2e-move-tests/src/tests/module_bomb.rs b/aptos-move/e2e-move-tests/src/tests/module_bomb.rs new file mode 100644 index 0000000000000..cec3f5fa66084 --- /dev/null +++ b/aptos-move/e2e-move-tests/src/tests/module_bomb.rs @@ -0,0 +1,36 @@ +// Copyright © Aptos Foundation + +use move_binary_format::CompiledModule; +use move_core_types::identifier::Identifier; +use move_vm_runtime::move_vm::MoveVM; +use move_vm_test_utils::BlankStorage; +use move_vm_types::gas::UnmeteredGasMeter; + +const BLOB: &[u8] = include_bytes!("module_bomb.mv"); + +#[test] +fn test_module_bomb() { + let m = CompiledModule::deserialize(BLOB).unwrap(); + + let mut vms = vec![]; + + for _i in 0..2 { + let vm = MoveVM::new(vec![]).unwrap(); + let storage = BlankStorage; + let mut sess = vm.new_session(&storage); + sess.publish_module( + BLOB.to_vec(), + *m.self_id().address(), + &mut UnmeteredGasMeter, + ) + .unwrap(); + + sess.load_function(&m.self_id(), &Identifier::new("f1").unwrap(), &[]) + .unwrap(); + vms.push(vm); + } + + let stats = memory_stats::memory_stats().unwrap(); + println!("Physical: {}", stats.physical_mem); + println!("Virtual: {}", stats.virtual_mem); +} diff --git a/aptos-move/e2e-move-tests/src/tests/string_args.rs b/aptos-move/e2e-move-tests/src/tests/string_args.rs index 0910f2cf82aec..d7e16350d4488 100644 --- a/aptos-move/e2e-move-tests/src/tests/string_args.rs +++ b/aptos-move/e2e-move-tests/src/tests/string_args.rs @@ -653,3 +653,26 @@ fn string_args_generic_instantiation() { success_generic(vec![string_type, address_type], tests); } + +#[test] +fn huge_string_args_are_not_allowed() { + let mut tests = vec![]; + let mut len: u64 = 1_000_000_000_000; + let mut big_str_arg = vec![]; + loop { + let cur = len & 0x7F; + if cur != len { + big_str_arg.push((cur | 0x80) as u8); + len >>= 7; + } else { + big_str_arg.push(cur as u8); + break; + } + } + tests.push(( + "0xcafe::test::hi", + vec![big_str_arg], + deserialization_failure(), + )); + fail(tests); +} diff --git a/third_party/move/move-vm/runtime/Cargo.toml b/third_party/move/move-vm/runtime/Cargo.toml index 3d0b2141f4e24..40cced7e55c23 100644 --- a/third_party/move/move-vm/runtime/Cargo.toml +++ b/third_party/move/move-vm/runtime/Cargo.toml @@ -11,6 +11,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +backtrace = "0.3.69" better_any = "0.1.1" fail = "0.4.0" move-binary-format = { path = "../../move-binary-format" } diff --git a/third_party/move/move-vm/runtime/src/interpreter.rs b/third_party/move/move-vm/runtime/src/interpreter.rs index 798c31bcc37d4..a686fbf19f5c2 100644 --- a/third_party/move/move-vm/runtime/src/interpreter.rs +++ b/third_party/move/move-vm/runtime/src/interpreter.rs @@ -1040,7 +1040,8 @@ fn check_depth_of_type_impl( | Type::Signer => check_depth!(0), // Even though this is recursive this is OK since the depth of this recursion is // bounded by the depth of the type arguments, which we have already checked. - Type::Reference(ty) | Type::MutableReference(ty) | Type::Vector(ty) => { + Type::Vector(ty) => check_depth_of_type_impl(resolver, ty, max_depth, check_depth!(1))?, + Type::Reference(ty) | Type::MutableReference(ty) => { check_depth_of_type_impl(resolver, ty, max_depth, check_depth!(1))? }, Type::Struct(si) => { @@ -1658,7 +1659,7 @@ impl Frame { } interpreter .operand_stack - .push_ty(Type::Vector(Box::new(ty)))?; + .push_ty(Type::Vector(Arc::new(ty)))?; }, Bytecode::VecLen(si) => { let ty = resolver.instantiate_single_type(*si, ty_args)?; diff --git a/third_party/move/move-vm/runtime/src/loader.rs b/third_party/move/move-vm/runtime/src/loader.rs index e035ca24c202c..4b9a0baf99377 100644 --- a/third_party/move/move-vm/runtime/src/loader.rs +++ b/third_party/move/move-vm/runtime/src/loader.rs @@ -16,7 +16,7 @@ use move_binary_format::{ file_format::{ AbilitySet, Bytecode, CompiledModule, CompiledScript, Constant, ConstantPoolIndex, FieldHandleIndex, FieldInstantiationIndex, FunctionDefinition, FunctionDefinitionIndex, - FunctionHandleIndex, FunctionInstantiationIndex, Signature, SignatureIndex, SignatureToken, + FunctionHandleIndex, FunctionInstantiationIndex, SignatureIndex, SignatureToken, StructDefInstantiationIndex, StructDefinition, StructDefinitionIndex, StructFieldInformation, TableIndex, TypeParameterIndex, Visibility, }, @@ -174,11 +174,11 @@ impl ModuleCache { if let Some(cached) = self.module_at(&id) { return Ok(cached); } - // we need this operation to be transactional, if an error occurs we must // leave a clean state - self.add_module(natives, &module)?; - match Module::new(module, self) { + + let sig_cache = self.add_module(natives, &module)?; + match Module::new(&sig_cache, module, self) { Ok(module) => Ok(Arc::clone(self.modules.insert(id, module))), Err((err, module)) => { // remove all structs and functions that have been pushed @@ -192,7 +192,11 @@ impl ModuleCache { } } - fn add_module(&mut self, natives: &NativeFunctions, module: &CompiledModule) -> VMResult<()> { + fn add_module( + &mut self, + natives: &NativeFunctions, + module: &CompiledModule, + ) -> VMResult>> { let starting_idx = self.structs.len(); for (idx, struct_def) in module.struct_defs().iter().enumerate() { let st = self.make_struct_type(module, struct_def, StructDefinitionIndex(idx as u16)); @@ -227,33 +231,36 @@ impl ModuleCache { } } + let mut sig_cache = vec![]; + for sig in &module.signatures { + let mut tys = vec![]; + for tok in &sig.0 { + tys.push( + self.make_type_while_loading(module, tok) + .map_err(|err: PartialVMError| err.finish(Location::Undefined))?, + ); + } + sig_cache.push(tys); + } + for (idx, func) in module.function_defs().iter().enumerate() { let findex = FunctionDefinitionIndex(idx as TableIndex); let mut function = Function::new(natives, findex, func, module); - function.return_types = function - .return_ - .0 - .iter() - .map(|tok| self.make_type_while_loading(module, tok)) - .collect::>>() - .map_err(|err| err.finish(Location::Undefined))?; - function.local_types = function - .locals - .0 - .iter() - .map(|tok| self.make_type_while_loading(module, tok)) - .collect::>>() - .map_err(|err| err.finish(Location::Undefined))?; - function.parameter_types = function - .parameters - .0 - .iter() - .map(|tok| self.make_type_while_loading(module, tok)) - .collect::>>() - .map_err(|err| err.finish(Location::Undefined))?; + + function.return_types = sig_cache[function.return_idx.0 as usize].clone(); + + function.parameter_types = sig_cache[function.parameters_idx.0 as usize].clone(); + + if let Some(locals_idx) = function.locals_idx { + function.local_types = function.parameter_types.clone(); + function + .local_types + .extend(sig_cache[locals_idx.0 as usize].clone()); + } + self.functions.push(Arc::new(function)); } - Ok(()) + Ok(sig_cache) } fn make_struct_type( @@ -396,7 +403,7 @@ impl ModuleCache { SignatureToken::TypeParameter(idx) => Type::TyParam(*idx), SignatureToken::Vector(inner_tok) => { let inner_type = Self::make_type_internal(module, inner_tok, resolver)?; - Type::Vector(Box::new(inner_type)) + Type::Vector(Arc::new(inner_type)) }, SignatureToken::Reference(inner_tok) => { let inner_type = Self::make_type_internal(module, inner_tok, resolver)?; @@ -430,7 +437,7 @@ impl ModuleCache { module.identifier_at(module_handle.name).to_owned(), ); let def_idx = resolver(struct_name, &module_id)?; - Type::StructInstantiation(def_idx, type_parameters) + Type::StructInstantiation(def_idx, Arc::new(type_parameters)) }, }; Ok(res) @@ -531,7 +538,12 @@ impl ModuleCache { | Type::U16 | Type::U32 | Type::U256 => DepthFormula::constant(1), - Type::Vector(ty) | Type::Reference(ty) | Type::MutableReference(ty) => { + Type::Vector(ty) => { + let mut inner = self.calculate_depth_of_type(ty, depth_cache)?; + inner.scale(1); + inner + }, + Type::Reference(ty) | Type::MutableReference(ty) => { let mut inner = self.calculate_depth_of_type(ty, depth_cache)?; inner.scale(1); inner @@ -814,31 +826,10 @@ impl Loader { .map_err(|err| err.finish(Location::Undefined))?; let func = self.module_cache.read().function_at(idx); - let parameters = func - .parameters - .0 - .iter() - .map(|tok| { - self.module_cache - .read() - .make_type(BinaryIndexedView::Module(module.module()), tok) - }) - .collect::>>() - .map_err(|err| err.finish(Location::Undefined))?; + let param_tys = func.parameter_types.clone(); + let ret_tys = func.return_types.clone(); - let return_ = func - .return_ - .0 - .iter() - .map(|tok| { - self.module_cache - .read() - .make_type(BinaryIndexedView::Module(module.module()), tok) - }) - .collect::>>() - .map_err(|err| err.finish(Location::Undefined))?; - - Ok((module, func, parameters, return_)) + Ok((module, func, param_tys, ret_tys)) } // Matches the actual returned type to the expected type, binding any type args to the @@ -860,8 +851,10 @@ impl Loader { }, // Recursive types we need to recurse the matching types (Type::Reference(ret_inner), Type::Reference(expected_inner)) - | (Type::MutableReference(ret_inner), Type::MutableReference(expected_inner)) - | (Type::Vector(ret_inner), Type::Vector(expected_inner)) => { + | (Type::MutableReference(ret_inner), Type::MutableReference(expected_inner)) => { + Self::match_return_type(ret_inner, expected_inner, map) + }, + (Type::Vector(ret_inner), Type::Vector(expected_inner)) => { Self::match_return_type(ret_inner, expected_inner, map) }, // For structs the both need to be the same struct. @@ -1167,7 +1160,7 @@ impl Loader { TypeTag::U256 => Type::U256, TypeTag::Address => Type::Address, TypeTag::Signer => Type::Signer, - TypeTag::Vector(tt) => Type::Vector(Box::new(self.load_type(tt, data_store)?)), + TypeTag::Vector(tt) => Type::Vector(Arc::new(self.load_type(tt, data_store)?)), TypeTag::Struct(struct_tag) => { let module_id = ModuleId::new(struct_tag.address, struct_tag.module.clone()); self.load_module(&module_id, data_store)?; @@ -1186,7 +1179,7 @@ impl Loader { } self.verify_ty_args(struct_type.type_param_constraints(), &type_params) .map_err(|e| e.finish(Location::Undefined))?; - Type::StructInstantiation(idx, type_params) + Type::StructInstantiation(idx, Arc::new(type_params)) } }, }) @@ -1753,11 +1746,13 @@ impl<'a> Resolver<'a> { Ok(Type::StructInstantiation( struct_inst.def, - struct_inst - .instantiation - .iter() - .map(|ty| self.subst(ty, ty_args)) - .collect::>()?, + Arc::new( + struct_inst + .instantiation + .iter() + .map(|ty| self.subst(ty, ty_args)) + .collect::>()?, + ), )) } @@ -1916,11 +1911,13 @@ impl<'a> Resolver<'a> { match &self.binary { BinaryType::Module(module) => Ok(Type::StructInstantiation( module.field_instantiations[idx.0 as usize].owner, - module.field_instantiations[idx.0 as usize] - .instantiation - .iter() - .map(|ty| ty.subst(args)) - .collect::>>()?, + Arc::new( + module.field_instantiations[idx.0 as usize] + .instantiation + .iter() + .map(|ty| ty.subst(args)) + .collect::>>()?, + ), )), BinaryType::Script(_) => unreachable!("Scripts cannot have field instructions"), } @@ -1998,6 +1995,7 @@ pub(crate) struct Module { impl Module { fn new( + sig_cache: &[Vec], module: CompiledModule, cache: &ModuleCache, ) -> Result { @@ -2056,10 +2054,8 @@ impl Module { let def = struct_inst.def.0 as usize; let struct_def = &structs[def]; let field_count = struct_def.field_count; - let mut instantiation = vec![]; - for ty in &module.signature_at(struct_inst.type_parameters).0 { - instantiation.push(cache.make_type_while_loading(&module, ty)?); - } + let instantiation = sig_cache[struct_inst.type_parameters.0 as usize].clone(); + struct_instantiations.push(StructInstantiation { field_count, def: struct_def.idx, @@ -2109,23 +2105,21 @@ impl Module { | Bytecode::VecPopBack(si) | Bytecode::VecUnpack(si, _) | Bytecode::VecSwap(si) => { - if !single_signature_token_map.contains_key(si) { - let ty = match module.signature_at(*si).0.get(0) { - None => { - return Err(PartialVMError::new( - StatusCode::VERIFIER_INVARIANT_VIOLATION, - ) - .with_message( - "the type argument for vector-related bytecode \ - expects one and only one signature token" - .to_owned(), - )); - }, - Some(sig_token) => sig_token, - }; - single_signature_token_map - .insert(*si, cache.make_type_while_loading(&module, ty)?); - } + // TODO: unify the single token signature map with the more general cache. + let ty = match sig_cache[si.0 as usize].get(0) { + None => { + return Err(PartialVMError::new( + StatusCode::VERIFIER_INVARIANT_VIOLATION, + ) + .with_message( + "the type argument for vector-related bytecode \ + expects one and only one signature token" + .to_owned(), + )); + }, + Some(ty) => ty.clone(), + }; + single_signature_token_map.insert(*si, ty); }, _ => {}, } @@ -2135,10 +2129,9 @@ impl Module { for func_inst in module.function_instantiations() { let handle = function_refs[func_inst.handle.0 as usize]; - let mut instantiation = vec![]; - for ty in &module.signature_at(func_inst.type_parameters).0 { - instantiation.push(cache.make_type_while_loading(&module, ty)?); - } + + let instantiation = sig_cache[func_inst.type_parameters.0 as usize].clone(); + function_instantiations.push(FunctionInstantiation { handle, instantiation, @@ -2156,10 +2149,7 @@ impl Module { let fh_idx = f_inst.handle; let owner = field_handles[fh_idx.0 as usize].owner; let offset = field_handles[fh_idx.0 as usize].offset; - let mut instantiation = vec![]; - for ty in &module.signature_at(f_inst.type_parameters).0 { - instantiation.push(cache.make_type_while_loading(&module, ty)?); - } + let instantiation = sig_cache[f_inst.type_parameters.0 as usize].clone(); field_instantiations.push(FieldInstantiation { offset, owner, @@ -2322,35 +2312,25 @@ impl Script { let scope = Scope::Script(*script_hash); let code: Vec = script.code.code.clone(); - let parameters = script.signature_at(script.parameters).clone(); - let parameter_tys = parameters - .0 - .iter() - .map(|tok| cache.make_type(BinaryIndexedView::Script(&script), tok)) - .collect::>>() - .map_err(|err| err.finish(Location::Undefined))?; - let locals = Signature( - parameters - .0 - .iter() - .chain(script.signature_at(script.code.locals).0.iter()) - .cloned() - .collect(), - ); - let local_tys = locals - .0 - .iter() - .map(|tok| cache.make_type(BinaryIndexedView::Script(&script), tok)) - .collect::>>() - .map_err(|err| err.finish(Location::Undefined))?; - let return_ = Signature(vec![]); - let return_tys = return_ - .0 - .iter() - .map(|tok| cache.make_type(BinaryIndexedView::Script(&script), tok)) - .collect::>>() - .map_err(|err| err.finish(Location::Undefined))?; + let mut sig_cache = vec![]; + for sig in &script.signatures { + let mut tys = vec![]; + for tok in &sig.0 { + tys.push( + cache + .make_type(BinaryIndexedView::Script(&script), tok) + .map_err(|err| err.finish(Location::Undefined))?, + ); + } + sig_cache.push(tys); + } + + let parameter_tys = sig_cache[script.parameters.0 as usize].clone(); + + let mut local_tys = parameter_tys.clone(); + local_tys.extend(sig_cache[script.code.locals.0 as usize].clone()); + let type_parameters = script.type_parameters.clone(); // TODO: main does not have a name. Revisit. let name = Identifier::new("main").unwrap(); @@ -2359,16 +2339,16 @@ impl Script { file_format_version: script.version(), index: FunctionDefinitionIndex(0), code, - parameters, - return_, - locals, + parameters_idx: script.parameters, + return_idx: SignatureIndex(0), + locals_idx: Some(script.code.locals), type_parameters, native, def_is_native, def_is_friend_or_private: false, scope, name, - return_types: return_tys.clone(), + return_types: vec![], local_types: local_tys, parameter_types: parameter_tys.clone(), }); @@ -2385,7 +2365,7 @@ impl Script { | Bytecode::VecUnpack(si, _) | Bytecode::VecSwap(si) => { if !single_signature_token_map.contains_key(si) { - let ty = match script.signature_at(*si).0.get(0) { + let ty = match sig_cache[si.0 as usize].get(0) { None => { return Err(PartialVMError::new( StatusCode::VERIFIER_INVARIANT_VIOLATION, @@ -2397,14 +2377,9 @@ impl Script { ) .finish(Location::Script)); }, - Some(sig_token) => sig_token, + Some(ty) => ty.clone(), }; - single_signature_token_map.insert( - *si, - cache - .make_type(BinaryIndexedView::Script(&script), ty) - .map_err(|e| e.finish(Location::Script))?, - ); + single_signature_token_map.insert(*si, ty); } }, _ => {}, @@ -2418,7 +2393,7 @@ impl Script { function_instantiations, main, parameter_tys, - return_tys, + return_tys: vec![], single_signature_token_map, }) } @@ -2455,9 +2430,9 @@ pub(crate) struct Function { file_format_version: u32, index: FunctionDefinitionIndex, code: Vec, - parameters: Signature, - return_: Signature, - locals: Signature, + parameters_idx: SignatureIndex, + return_idx: SignatureIndex, + locals_idx: Option, type_parameters: Vec, native: Option, def_is_native: bool, @@ -2503,31 +2478,19 @@ impl Function { (None, false) }; let scope = Scope::Module(module_id); - let parameters = module.signature_at(handle.parameters).clone(); // Native functions do not have a code unit - let (code, locals) = match &def.code { - Some(code) => ( - code.code.clone(), - Signature( - parameters - .0 - .iter() - .chain(module.signature_at(code.locals).0.iter()) - .cloned() - .collect(), - ), - ), - None => (vec![], Signature(vec![])), + let (code, locals_idx) = match &def.code { + Some(code) => (code.code.clone(), Some(code.locals)), + None => (vec![], None), }; - let return_ = module.signature_at(handle.return_).clone(); let type_parameters = handle.type_parameters.clone(); Self { file_format_version: module.version(), index, code, - parameters, - return_, - locals, + parameters_idx: handle.parameters, + return_idx: handle.return_, + locals_idx, type_parameters, native, def_is_native, @@ -2572,15 +2535,15 @@ impl Function { } pub(crate) fn local_count(&self) -> usize { - self.locals.len() + self.local_types.len() } pub(crate) fn arg_count(&self) -> usize { - self.parameters.len() + self.parameter_types.len() } pub(crate) fn return_type_count(&self) -> usize { - self.return_.len() + self.return_types.len() } pub(crate) fn name(&self) -> &str { @@ -2838,7 +2801,11 @@ impl Loader { let mut result = 0; while let Some(ty) = todo.pop() { match ty { - Type::Vector(ty) | Type::Reference(ty) | Type::MutableReference(ty) => { + Type::Reference(ty) | Type::MutableReference(ty) => { + result += 1; + todo.push(ty); + }, + Type::Vector(ty) => { result += 1; todo.push(ty); }, diff --git a/third_party/move/move-vm/types/src/loaded_data/runtime_types.rs b/third_party/move/move-vm/types/src/loaded_data/runtime_types.rs index 538be2f1aac88..52ca912623068 100644 --- a/third_party/move/move-vm/types/src/loaded_data/runtime_types.rs +++ b/third_party/move/move-vm/types/src/loaded_data/runtime_types.rs @@ -12,7 +12,7 @@ use move_core_types::{ gas_algebra::AbstractMemorySize, identifier::Identifier, language_storage::ModuleId, vm_status::StatusCode, }; -use std::{cmp::max, collections::BTreeMap, fmt::Debug}; +use std::{cmp::max, collections::BTreeMap, fmt::Debug, sync::Arc}; pub const TYPE_DEPTH_MAX: usize = 256; @@ -138,9 +138,9 @@ pub enum Type { U128, Address, Signer, - Vector(Box), + Vector(Arc), Struct(CachedStructIndex), - StructInstantiation(CachedStructIndex, Vec), + StructInstantiation(CachedStructIndex, Arc>), Reference(Box), MutableReference(Box), TyParam(u16), @@ -175,7 +175,7 @@ impl Type { Type::U256 => Type::U256, Type::Address => Type::Address, Type::Signer => Type::Signer, - Type::Vector(ty) => Type::Vector(Box::new(ty.apply_subst(subst, depth + 1)?)), + Type::Vector(ty) => Type::Vector(Arc::new(ty.apply_subst(subst, depth + 1)?)), Type::Reference(ty) => Type::Reference(Box::new(ty.apply_subst(subst, depth + 1)?)), Type::MutableReference(ty) => { Type::MutableReference(Box::new(ty.apply_subst(subst, depth + 1)?)) @@ -183,10 +183,10 @@ impl Type { Type::Struct(def_idx) => Type::Struct(*def_idx), Type::StructInstantiation(def_idx, instantiation) => { let mut inst = vec![]; - for ty in instantiation { + for ty in &**instantiation { inst.push(ty.apply_subst(subst, depth + 1)?) } - Type::StructInstantiation(*def_idx, inst) + Type::StructInstantiation(*def_idx, Arc::new(inst)) }, }; Ok(res) @@ -220,9 +220,8 @@ impl Type { TyParam(_) | Bool | U8 | U16 | U32 | U64 | U128 | U256 | Address | Signer => { Self::LEGACY_BASE_MEMORY_SIZE }, - Vector(ty) | Reference(ty) | MutableReference(ty) => { - Self::LEGACY_BASE_MEMORY_SIZE + ty.size() - }, + Vector(ty) => Self::LEGACY_BASE_MEMORY_SIZE + ty.size(), + Reference(ty) | MutableReference(ty) => Self::LEGACY_BASE_MEMORY_SIZE + ty.size(), Struct(_) => Self::LEGACY_BASE_MEMORY_SIZE, StructInstantiation(_, tys) => tys .iter() @@ -243,7 +242,7 @@ impl Type { S::U128 => L::U128, S::U256 => L::U256, S::Address => L::Address, - S::Vector(inner) => L::Vector(Box::new(Self::from_const_signature(inner)?)), + S::Vector(inner) => L::Vector(Arc::new(Self::from_const_signature(inner)?)), // Not yet supported S::Struct(_) | S::StructInstantiation(_, _) => { return Err(