use move_binary_format::{
access::ModuleAccess,
binary_views::BinaryIndexedView,
errors::{Location, PartialVMError, PartialVMResult, VMResult},
file_format::{
CompiledModule, CompiledScript, FunctionDefinitionIndex, SignatureIndex, SignatureToken,
TableIndex,
},
file_format_common::{VERSION_1, VERSION_5},
IndexKind,
};
use move_core_types::{identifier::IdentStr, vm_status::StatusCode};
pub type FnCheckScriptSignature = fn(
&BinaryIndexedView,
bool,
SignatureIndex,
Option<SignatureIndex>,
) -> PartialVMResult<()>;
pub fn verify_script(
script: &CompiledScript,
check_signature: FnCheckScriptSignature,
) -> VMResult<()> {
if script.version >= VERSION_5 {
return Ok(());
}
let resolver = &BinaryIndexedView::Script(script);
let parameters = script.parameters;
let return_ = None;
verify_main_signature_impl(resolver, true, parameters, return_, check_signature)
.map_err(|e| e.finish(Location::Script))
}
pub fn verify_module(
module: &CompiledModule,
check_signature: FnCheckScriptSignature,
) -> VMResult<()> {
if module.version < VERSION_5 {
return Ok(());
}
for (idx, _fdef) in module
.function_defs()
.iter()
.enumerate()
.filter(|(_idx, fdef)| fdef.is_entry)
{
verify_module_function_signature(
module,
FunctionDefinitionIndex(idx as TableIndex),
check_signature,
)?
}
Ok(())
}
pub fn verify_module_function_signature_by_name(
module: &CompiledModule,
name: &IdentStr,
check_signature: FnCheckScriptSignature,
) -> VMResult<()> {
let fdef_opt = module.function_defs().iter().enumerate().find(|(_, fdef)| {
module.identifier_at(module.function_handle_at(fdef.function).name) == name
});
let (idx, _fdef) = fdef_opt.ok_or_else(|| {
PartialVMError::new(StatusCode::VERIFICATION_ERROR)
.with_message("function not found in verify_module_script_function".to_string())
.finish(Location::Module(module.self_id()))
})?;
verify_module_function_signature(
module,
FunctionDefinitionIndex(idx as TableIndex),
check_signature,
)
}
fn verify_module_function_signature(
module: &CompiledModule,
idx: FunctionDefinitionIndex,
check_signature: FnCheckScriptSignature,
) -> VMResult<()> {
let fdef = module.function_def_at(idx);
let resolver = &BinaryIndexedView::Module(module);
let fhandle = module.function_handle_at(fdef.function);
let parameters = fhandle.parameters;
let return_ = fhandle.return_;
verify_main_signature_impl(
resolver,
fdef.is_entry,
parameters,
Some(return_),
check_signature,
)
.map_err(|e| {
e.at_index(IndexKind::FunctionDefinition, idx.0)
.finish(Location::Module(module.self_id()))
})
}
fn verify_main_signature_impl(
resolver: &BinaryIndexedView,
is_entry: bool,
parameters_idx: SignatureIndex,
return_idx: Option<SignatureIndex>,
check_signature: FnCheckScriptSignature,
) -> PartialVMResult<()> {
let deprecated_logic = resolver.version() < VERSION_5 && is_entry;
if deprecated_logic {
legacy_script_signature_checks(resolver, is_entry, parameters_idx, return_idx)?;
}
check_signature(resolver, is_entry, parameters_idx, return_idx)
}
pub fn no_additional_script_signature_checks(
_resolver: &BinaryIndexedView,
_is_entry: bool,
_parameters: SignatureIndex,
_return_type: Option<SignatureIndex>,
) -> PartialVMResult<()> {
Ok(())
}
pub fn legacy_script_signature_checks(
resolver: &BinaryIndexedView,
_is_entry: bool,
parameters_idx: SignatureIndex,
return_idx: Option<SignatureIndex>,
) -> PartialVMResult<()> {
use SignatureToken as S;
let empty_vec = &vec![];
let parameters = &resolver.signature_at(parameters_idx).0;
let return_types = return_idx
.map(|idx| &resolver.signature_at(idx).0)
.unwrap_or(empty_vec);
let all_args_have_valid_type = if resolver.version() <= VERSION_1 {
parameters
.iter()
.skip_while(|typ| matches!(typ, S::Reference(inner) if matches!(&**inner, S::Signer)))
.all(|typ| typ.is_valid_for_constant())
} else {
parameters
.iter()
.skip_while(|typ| matches!(typ, S::Signer))
.all(|typ| typ.is_valid_for_constant())
};
let has_valid_return_type = return_types.is_empty();
if !all_args_have_valid_type || !has_valid_return_type {
Err(PartialVMError::new(
StatusCode::INVALID_MAIN_FUNCTION_SIGNATURE,
))
} else {
Ok(())
}
}