use crate::{
check_duplication::DuplicationChecker, code_unit_verifier::CodeUnitVerifier,
resources::ResourceTransitiveChecker, signature::SignatureChecker,
struct_defs::RecursiveStructDefChecker,
};
use failure::Error;
use std::{collections::BTreeMap, fmt};
use types::language_storage::ModuleId;
use vm::{
access::{ModuleAccess, ScriptAccess},
errors::{BinaryLoaderResult, VMStaticViolation, VerificationError, VerificationStatus},
file_format::{CompiledModule, CompiledProgram, CompiledScript},
resolver::Resolver,
views::{ModuleView, ViewInternals},
IndexKind,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct VerifiedProgram<'a> {
script: VerifiedScript,
modules: Vec<VerifiedModule>,
deps: Vec<&'a VerifiedModule>,
}
impl<'a> VerifiedProgram<'a> {
pub fn new(
program: CompiledProgram,
deps: impl IntoIterator<Item = &'a VerifiedModule>,
) -> Result<Self, Vec<VerificationStatus>> {
let deps: Vec<&VerifiedModule> = deps.into_iter().collect();
Self::new_impl(program, deps)
}
fn new_impl(
program: CompiledProgram,
deps: Vec<&'a VerifiedModule>,
) -> Result<Self, Vec<VerificationStatus>> {
let mut modules = vec![];
for (module_idx, module) in program.modules.into_iter().enumerate() {
let to_statuses = |errors: Vec<VerificationError>| {
errors
.into_iter()
.map(|error| VerificationStatus::Module(module_idx as u16, error))
.collect::<Vec<_>>()
};
let module = match VerifiedModule::new(module) {
Ok(module) => module,
Err((_, errors)) => {
return Err(to_statuses(errors));
}
};
let (module, errors) = {
let deps = deps.iter().copied().chain(&modules);
verify_module_dependencies(module, deps)
};
if !errors.is_empty() {
return Err(to_statuses(errors));
}
modules.push(module);
}
let to_statuses = |errors: Vec<VerificationError>| {
errors
.into_iter()
.map(VerificationStatus::Script)
.collect::<Vec<_>>()
};
let script = match VerifiedScript::new(program.script) {
Ok(script) => script,
Err((_, errors)) => {
let statuses = errors.into_iter().map(VerificationStatus::Script).collect();
return Err(statuses);
}
};
let (script, errors) = {
let deps = deps.iter().copied().chain(&modules);
verify_script_dependencies(script, deps)
};
if !errors.is_empty() {
return Err(to_statuses(errors));
}
Ok(VerifiedProgram {
script,
modules,
deps,
})
}
pub fn script(&self) -> &VerifiedScript {
&self.script
}
pub fn modules(&self) -> &[VerifiedModule] {
&self.modules
}
pub fn deps(&self) -> &[&'a VerifiedModule] {
&self.deps
}
pub fn into_inner(self) -> CompiledProgram {
CompiledProgram {
modules: self
.modules
.into_iter()
.map(|module| module.into_inner())
.collect(),
script: self.script.into_inner(),
}
}
}
impl<'a> fmt::Display for VerifiedProgram<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VerifiedProgram: {{\nModules: [\n")?;
for m in &self.modules {
writeln!(f, "{},", m)?;
}
write!(f, "],\nScript: {},\nDependencies: ...}}", self.script)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct VerifiedModule(CompiledModule);
impl VerifiedModule {
pub fn new(module: CompiledModule) -> Result<Self, (CompiledModule, Vec<VerificationError>)> {
let mut errors = DuplicationChecker::new(&module).verify();
if errors.is_empty() {
errors.append(&mut SignatureChecker::new(&module).verify());
errors.append(&mut ResourceTransitiveChecker::new(&module).verify());
}
if errors.is_empty() {
errors.append(&mut RecursiveStructDefChecker::new(&module).verify());
}
if errors.is_empty() {
errors.append(&mut CodeUnitVerifier::verify(&module));
}
if errors.is_empty() {
Ok(VerifiedModule(module))
} else {
Err((module, errors))
}
}
#[allow(non_snake_case)]
#[doc(hidden)]
pub fn bypass_verifier_DANGEROUS_FOR_TESTING_ONLY(module: CompiledModule) -> VerifiedModule {
VerifiedModule(module)
}
pub fn serialize(&self, binary: &mut Vec<u8>) -> Result<(), Error> {
self.as_inner().serialize(binary)
}
pub fn deserialize(binary: &[u8]) -> BinaryLoaderResult<Self> {
let deserialized = CompiledModule::deserialize(binary)?;
Ok(VerifiedModule(deserialized))
}
pub fn as_inner(&self) -> &CompiledModule {
&self.0
}
pub fn into_inner(self) -> CompiledModule {
self.0
}
}
impl ModuleAccess for VerifiedModule {
fn as_module(&self) -> &CompiledModule {
self.as_inner()
}
}
impl fmt::Display for VerifiedModule {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VerifiedModule: {}", self.0)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct VerifiedScript(CompiledScript);
impl VerifiedScript {
pub fn new(script: CompiledScript) -> Result<Self, (CompiledScript, Vec<VerificationError>)> {
let fake_module = script.into_module();
let (fake_module, mut errors) = match VerifiedModule::new(fake_module) {
Ok(module) => (module.into_inner(), vec![]),
Err((module, errors)) => (module, errors),
};
let script = fake_module.into_script();
errors.append(
&mut verify_main_signature(&script)
.into_iter()
.map(move |err| VerificationError {
kind: IndexKind::FunctionDefinition,
idx: 0,
err,
})
.collect(),
);
if errors.is_empty() {
Ok(VerifiedScript(script))
} else {
Err((script, errors))
}
}
pub fn into_module(self) -> VerifiedModule {
VerifiedModule(self.into_inner().into_module())
}
pub fn serialize(&self, binary: &mut Vec<u8>) -> Result<(), Error> {
self.as_inner().serialize(binary)
}
pub fn deserialize(binary: &[u8]) -> BinaryLoaderResult<Self> {
let deserialized = CompiledScript::deserialize(binary)?;
Ok(VerifiedScript(deserialized))
}
pub fn as_inner(&self) -> &CompiledScript {
&self.0
}
pub fn into_inner(self) -> CompiledScript {
self.0
}
}
impl ScriptAccess for VerifiedScript {
fn as_script(&self) -> &CompiledScript {
self.as_inner()
}
}
impl fmt::Display for VerifiedScript {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VerifiedScript: {}", self.0)
}
}
pub fn verify_main_signature(script: &CompiledScript) -> Vec<VMStaticViolation> {
let function_handle = &script.function_handle_at(script.main().function);
let function_signature = &script.function_signature_at(function_handle.signature);
if !function_signature.return_types.is_empty() {
return vec![VMStaticViolation::InvalidMainFunctionSignature];
}
for arg_type in &function_signature.arg_types {
if !arg_type.is_primitive() {
return vec![VMStaticViolation::InvalidMainFunctionSignature];
}
}
vec![]
}
pub fn verify_module_dependencies<'a>(
module: VerifiedModule,
dependencies: impl IntoIterator<Item = &'a VerifiedModule>,
) -> (VerifiedModule, Vec<VerificationError>) {
let module_id = module.self_id();
let mut dependency_map = BTreeMap::new();
for dependency in dependencies {
let dependency_id = dependency.self_id();
if module_id != dependency_id {
dependency_map.insert(dependency_id, dependency);
}
}
let mut errors = vec![];
let module_view = ModuleView::new(&module);
errors.append(&mut verify_struct_kind(&module_view, &dependency_map));
errors.append(&mut verify_function_visibility_and_type(
&module_view,
&dependency_map,
));
errors.append(&mut verify_all_dependencies_provided(
&module_view,
&dependency_map,
));
(module, errors)
}
pub fn verify_script_dependencies<'a>(
script: VerifiedScript,
dependencies: impl IntoIterator<Item = &'a VerifiedModule>,
) -> (VerifiedScript, Vec<VerificationError>) {
let fake_module = script.into_module();
let (fake_module, errors) = verify_module_dependencies(fake_module, dependencies);
let script = VerifiedScript(fake_module.into_inner().into_script());
(script, errors)
}
fn verify_all_dependencies_provided(
module_view: &ModuleView<VerifiedModule>,
dependency_map: &BTreeMap<ModuleId, &VerifiedModule>,
) -> Vec<VerificationError> {
let mut errors = vec![];
for (idx, module_handle_view) in module_view.module_handles().enumerate() {
let module_id = module_handle_view.module_id();
if idx != CompiledModule::IMPLEMENTED_MODULE_INDEX as usize
&& !dependency_map.contains_key(&module_id)
{
errors.push(VerificationError {
kind: IndexKind::ModuleHandle,
idx,
err: VMStaticViolation::MissingDependency,
});
}
}
errors
}
fn verify_struct_kind(
module_view: &ModuleView<VerifiedModule>,
dependency_map: &BTreeMap<ModuleId, &VerifiedModule>,
) -> Vec<VerificationError> {
let mut errors = vec![];
for (idx, struct_handle_view) in module_view.struct_handles().enumerate() {
let owner_module_id = struct_handle_view.module_id();
if !dependency_map.contains_key(&owner_module_id) {
continue;
}
let struct_name = struct_handle_view.name();
let owner_module = &dependency_map[&owner_module_id];
let owner_module_view = ModuleView::new(*owner_module);
if let Some(struct_definition_view) = owner_module_view.struct_definition(struct_name) {
if struct_handle_view.is_resource() != struct_definition_view.is_resource() {
errors.push(VerificationError {
kind: IndexKind::StructHandle,
idx,
err: VMStaticViolation::TypeMismatch,
});
}
} else {
errors.push(VerificationError {
kind: IndexKind::StructHandle,
idx,
err: VMStaticViolation::LookupFailed,
});
}
}
errors
}
fn verify_function_visibility_and_type(
module_view: &ModuleView<VerifiedModule>,
dependency_map: &BTreeMap<ModuleId, &VerifiedModule>,
) -> Vec<VerificationError> {
let resolver = Resolver::new(module_view.as_inner());
let mut errors = vec![];
for (idx, function_handle_view) in module_view.function_handles().enumerate() {
let owner_module_id = function_handle_view.module_id();
if !dependency_map.contains_key(&owner_module_id) {
continue;
}
let function_name = function_handle_view.name();
let owner_module = dependency_map[&owner_module_id];
let owner_module_view = ModuleView::new(owner_module);
if let Some(function_definition_view) = owner_module_view.function_definition(function_name)
{
if function_definition_view.is_public() {
let function_definition_signature = function_definition_view.signature().as_inner();
match resolver
.import_function_signature(owner_module, &function_definition_signature)
{
Ok(imported_function_signature) => {
let function_handle_signature = function_handle_view.signature().as_inner();
if imported_function_signature != *function_handle_signature {
errors.push(VerificationError {
kind: IndexKind::FunctionHandle,
idx,
err: VMStaticViolation::TypeMismatch,
});
}
}
Err(err) => {
errors.push(VerificationError {
kind: IndexKind::FunctionHandle,
idx,
err,
});
}
}
} else {
errors.push(VerificationError {
kind: IndexKind::FunctionHandle,
idx,
err: VMStaticViolation::VisibilityMismatch,
});
}
} else {
errors.push(VerificationError {
kind: IndexKind::FunctionHandle,
idx,
err: VMStaticViolation::LookupFailed,
});
}
}
errors
}