use move_binary_format::{
access::{ModuleAccess, ScriptAccess},
errors::{verification_error, Location, PartialVMResult, VMResult},
file_format::{
CompiledModule, CompiledScript, Constant, FunctionHandle, FunctionHandleIndex,
FunctionInstantiation, ModuleHandle, Signature, StructFieldInformation, StructHandle,
StructHandleIndex, TableIndex,
},
IndexKind,
};
use move_core_types::{
account_address::AccountAddress, identifier::Identifier, vm_status::StatusCode,
};
use std::{collections::HashSet, hash::Hash};
pub struct DuplicationChecker<'a> {
module: &'a CompiledModule,
}
impl<'a> DuplicationChecker<'a> {
pub fn verify_module(module: &'a CompiledModule) -> VMResult<()> {
Self::verify_module_impl(module).map_err(|e| e.finish(Location::Module(module.self_id())))
}
fn verify_module_impl(module: &'a CompiledModule) -> PartialVMResult<()> {
Self::check_identifiers(module.identifiers())?;
Self::check_address_identifiers(module.address_identifiers())?;
Self::check_constants(module.constant_pool())?;
Self::check_signatures(module.signatures())?;
Self::check_module_handles(module.module_handles())?;
Self::check_module_handles(module.friend_decls())?;
Self::check_struct_handles(module.struct_handles())?;
Self::check_function_handles(module.function_handles())?;
Self::check_function_instantiations(module.function_instantiations())?;
let checker = Self { module };
checker.check_field_handles()?;
checker.check_field_instantiations()?;
checker.check_function_defintions()?;
checker.check_struct_definitions()?;
checker.check_struct_instantiations()
}
pub fn verify_script(module: &'a CompiledScript) -> VMResult<()> {
Self::verify_script_impl(module).map_err(|e| e.finish(Location::Script))
}
fn verify_script_impl(script: &'a CompiledScript) -> PartialVMResult<()> {
Self::check_identifiers(script.identifiers())?;
Self::check_address_identifiers(script.address_identifiers())?;
Self::check_constants(script.constant_pool())?;
Self::check_signatures(script.signatures())?;
Self::check_module_handles(script.module_handles())?;
Self::check_struct_handles(script.struct_handles())?;
Self::check_function_handles(script.function_handles())?;
Self::check_function_instantiations(script.function_instantiations())
}
fn check_identifiers(identifiers: &[Identifier]) -> PartialVMResult<()> {
match Self::first_duplicate_element(identifiers) {
Some(idx) => Err(verification_error(
StatusCode::DUPLICATE_ELEMENT,
IndexKind::Identifier,
idx,
)),
None => Ok(()),
}
}
fn check_address_identifiers(address_identifiers: &[AccountAddress]) -> PartialVMResult<()> {
match Self::first_duplicate_element(address_identifiers) {
Some(idx) => Err(verification_error(
StatusCode::DUPLICATE_ELEMENT,
IndexKind::AddressIdentifier,
idx,
)),
None => Ok(()),
}
}
fn check_constants(constant_pool: &[Constant]) -> PartialVMResult<()> {
match Self::first_duplicate_element(constant_pool) {
Some(idx) => Err(verification_error(
StatusCode::DUPLICATE_ELEMENT,
IndexKind::ConstantPool,
idx,
)),
None => Ok(()),
}
}
fn check_signatures(signatures: &[Signature]) -> PartialVMResult<()> {
match Self::first_duplicate_element(signatures) {
Some(idx) => Err(verification_error(
StatusCode::DUPLICATE_ELEMENT,
IndexKind::Signature,
idx,
)),
None => Ok(()),
}
}
fn check_module_handles(module_handles: &[ModuleHandle]) -> PartialVMResult<()> {
match Self::first_duplicate_element(module_handles) {
Some(idx) => Err(verification_error(
StatusCode::DUPLICATE_ELEMENT,
IndexKind::ModuleHandle,
idx,
)),
None => Ok(()),
}
}
fn check_struct_handles(struct_handles: &[StructHandle]) -> PartialVMResult<()> {
match Self::first_duplicate_element(struct_handles.iter().map(|x| (x.module, x.name))) {
Some(idx) => Err(verification_error(
StatusCode::DUPLICATE_ELEMENT,
IndexKind::StructHandle,
idx,
)),
None => Ok(()),
}
}
fn check_function_instantiations(
function_instantiations: &[FunctionInstantiation],
) -> PartialVMResult<()> {
match Self::first_duplicate_element(function_instantiations) {
Some(idx) => Err(verification_error(
StatusCode::DUPLICATE_ELEMENT,
IndexKind::FunctionInstantiation,
idx,
)),
None => Ok(()),
}
}
fn check_function_handles(function_handles: &[FunctionHandle]) -> PartialVMResult<()> {
match Self::first_duplicate_element(function_handles.iter().map(|x| (x.module, x.name))) {
Some(idx) => Err(verification_error(
StatusCode::DUPLICATE_ELEMENT,
IndexKind::FunctionHandle,
idx,
)),
None => Ok(()),
}
}
fn check_field_handles(&self) -> PartialVMResult<()> {
match Self::first_duplicate_element(self.module.field_handles()) {
Some(idx) => Err(verification_error(
StatusCode::DUPLICATE_ELEMENT,
IndexKind::FieldHandle,
idx,
)),
None => Ok(()),
}
}
fn check_struct_instantiations(&self) -> PartialVMResult<()> {
match Self::first_duplicate_element(self.module.struct_instantiations()) {
Some(idx) => Err(verification_error(
StatusCode::DUPLICATE_ELEMENT,
IndexKind::StructDefInstantiation,
idx,
)),
None => Ok(()),
}
}
fn check_field_instantiations(&self) -> PartialVMResult<()> {
if let Some(idx) = Self::first_duplicate_element(self.module.field_instantiations()) {
return Err(verification_error(
StatusCode::DUPLICATE_ELEMENT,
IndexKind::FieldInstantiation,
idx,
));
}
Ok(())
}
fn check_struct_definitions(&self) -> PartialVMResult<()> {
if let Some(idx) =
Self::first_duplicate_element(self.module.struct_defs().iter().map(|x| x.struct_handle))
{
return Err(verification_error(
StatusCode::DUPLICATE_ELEMENT,
IndexKind::StructDefinition,
idx,
));
}
for (struct_idx, struct_def) in self.module.struct_defs().iter().enumerate() {
let fields = match &struct_def.field_information {
StructFieldInformation::Native => continue,
StructFieldInformation::Declared(fields) => fields,
};
if fields.is_empty() {
return Err(verification_error(
StatusCode::ZERO_SIZED_STRUCT,
IndexKind::StructDefinition,
struct_idx as TableIndex,
));
}
if let Some(idx) = Self::first_duplicate_element(fields.iter().map(|x| x.name)) {
return Err(verification_error(
StatusCode::DUPLICATE_ELEMENT,
IndexKind::FieldDefinition,
idx,
));
}
}
if let Some(idx) = self.module.struct_defs().iter().position(|x| {
self.module.struct_handle_at(x.struct_handle).module != self.module.self_handle_idx()
}) {
return Err(verification_error(
StatusCode::INVALID_MODULE_HANDLE,
IndexKind::StructDefinition,
idx as TableIndex,
));
}
let implemented_struct_handles: HashSet<StructHandleIndex> = self
.module
.struct_defs()
.iter()
.map(|x| x.struct_handle)
.collect();
if let Some(idx) = (0..self.module.struct_handles().len()).position(|x| {
let y = StructHandleIndex::new(x as u16);
self.module.struct_handle_at(y).module == self.module.self_handle_idx()
&& !implemented_struct_handles.contains(&y)
}) {
return Err(verification_error(
StatusCode::UNIMPLEMENTED_HANDLE,
IndexKind::StructHandle,
idx as TableIndex,
));
}
Ok(())
}
fn check_function_defintions(&self) -> PartialVMResult<()> {
if let Some(idx) =
Self::first_duplicate_element(self.module.function_defs().iter().map(|x| x.function))
{
return Err(verification_error(
StatusCode::DUPLICATE_ELEMENT,
IndexKind::FunctionDefinition,
idx,
));
}
for (idx, function_def) in self.module.function_defs().iter().enumerate() {
let acquires = function_def.acquires_global_resources.iter();
if Self::first_duplicate_element(acquires).is_some() {
return Err(verification_error(
StatusCode::DUPLICATE_ACQUIRES_ANNOTATION,
IndexKind::FunctionDefinition,
idx as TableIndex,
));
}
}
if let Some(idx) = self.module.function_defs().iter().position(|x| {
self.module.function_handle_at(x.function).module != self.module.self_handle_idx()
}) {
return Err(verification_error(
StatusCode::INVALID_MODULE_HANDLE,
IndexKind::FunctionDefinition,
idx as TableIndex,
));
}
let implemented_function_handles: HashSet<FunctionHandleIndex> = self
.module
.function_defs()
.iter()
.map(|x| x.function)
.collect();
if let Some(idx) = (0..self.module.function_handles().len()).position(|x| {
let y = FunctionHandleIndex::new(x as u16);
self.module.function_handle_at(y).module == self.module.self_handle_idx()
&& !implemented_function_handles.contains(&y)
}) {
return Err(verification_error(
StatusCode::UNIMPLEMENTED_HANDLE,
IndexKind::FunctionHandle,
idx as TableIndex,
));
}
Ok(())
}
fn first_duplicate_element<T>(iter: T) -> Option<TableIndex>
where
T: IntoIterator,
T::Item: Eq + Hash,
{
let mut uniq = HashSet::new();
for (i, x) in iter.into_iter().enumerate() {
if !uniq.insert(x) {
return Some(i as TableIndex);
}
}
None
}
}