use std::{collections::HashSet, hash::Hash};
use vm::{
access::ModuleAccess,
errors::{VMStaticViolation, VerificationError},
file_format::{
CompiledModule, FieldDefinitionIndex, FunctionHandleIndex, ModuleHandleIndex,
StructHandleIndex, TableIndex,
},
IndexKind,
};
pub struct DuplicationChecker<'a> {
module: &'a CompiledModule,
}
impl<'a> DuplicationChecker<'a> {
pub fn new(module: &'a CompiledModule) -> Self {
Self { module }
}
pub fn verify(self) -> Vec<VerificationError> {
let mut errors = vec![];
if let Some(idx) = Self::first_duplicate_element(self.module.string_pool()) {
errors.push(VerificationError {
kind: IndexKind::StringPool,
idx,
err: VMStaticViolation::DuplicateElement,
})
}
if let Some(idx) = Self::first_duplicate_element(self.module.byte_array_pool()) {
errors.push(VerificationError {
kind: IndexKind::ByteArrayPool,
idx,
err: VMStaticViolation::DuplicateElement,
})
}
if let Some(idx) = Self::first_duplicate_element(self.module.address_pool()) {
errors.push(VerificationError {
kind: IndexKind::AddressPool,
idx,
err: VMStaticViolation::DuplicateElement,
})
}
if let Some(idx) = Self::first_duplicate_element(self.module.type_signatures()) {
errors.push(VerificationError {
kind: IndexKind::TypeSignature,
idx,
err: VMStaticViolation::DuplicateElement,
})
}
if let Some(idx) = Self::first_duplicate_element(self.module.function_signatures()) {
errors.push(VerificationError {
kind: IndexKind::FunctionSignature,
idx,
err: VMStaticViolation::DuplicateElement,
})
}
if let Some(idx) = Self::first_duplicate_element(self.module.locals_signatures()) {
errors.push(VerificationError {
kind: IndexKind::LocalsSignature,
idx,
err: VMStaticViolation::DuplicateElement,
})
}
if let Some(idx) = Self::first_duplicate_element(self.module.module_handles()) {
errors.push(VerificationError {
kind: IndexKind::ModuleHandle,
idx,
err: VMStaticViolation::DuplicateElement,
})
}
if let Some(idx) = Self::first_duplicate_element(
self.module
.struct_handles()
.iter()
.map(|x| (x.module, x.name)),
) {
errors.push(VerificationError {
kind: IndexKind::StructHandle,
idx,
err: VMStaticViolation::DuplicateElement,
})
}
if let Some(idx) = Self::first_duplicate_element(
self.module
.function_handles()
.iter()
.map(|x| (x.module, x.name)),
) {
errors.push(VerificationError {
kind: IndexKind::FunctionHandle,
idx,
err: VMStaticViolation::DuplicateElement,
})
}
if let Some(idx) =
Self::first_duplicate_element(self.module.struct_defs().iter().map(|x| x.struct_handle))
{
errors.push(VerificationError {
kind: IndexKind::StructDefinition,
idx,
err: VMStaticViolation::DuplicateElement,
})
}
if let Some(idx) =
Self::first_duplicate_element(self.module.function_defs().iter().map(|x| x.function))
{
errors.push(VerificationError {
kind: IndexKind::FunctionDefinition,
idx,
err: VMStaticViolation::DuplicateElement,
})
}
if let Some(idx) = Self::first_duplicate_element(
self.module.field_defs().iter().map(|x| (x.struct_, x.name)),
) {
errors.push(VerificationError {
kind: IndexKind::FieldDefinition,
idx,
err: VMStaticViolation::DuplicateElement,
})
}
let mut start_field_index: usize = 0;
let mut idx_opt = None;
for (idx, struct_def) in self.module.struct_defs().iter().enumerate() {
if FieldDefinitionIndex::new(start_field_index as u16) != struct_def.fields {
idx_opt = Some(idx);
break;
}
let next_start_field_index = start_field_index + struct_def.field_count as usize;
let all_fields_match = (start_field_index..next_start_field_index).all(|i| {
struct_def.struct_handle
== self
.module
.field_def_at(FieldDefinitionIndex::new(i as TableIndex))
.struct_
});
if !all_fields_match {
idx_opt = Some(idx);
break;
}
start_field_index = next_start_field_index;
}
if let Some(idx) = idx_opt {
errors.push(VerificationError {
kind: IndexKind::StructDefinition,
idx,
err: VMStaticViolation::InconsistentFields,
});
} else if start_field_index != self.module.field_defs().len() {
errors.push(VerificationError {
kind: IndexKind::FieldDefinition,
idx: start_field_index,
err: VMStaticViolation::UnusedFields,
});
}
if let Some(idx) = self.module.struct_defs().iter().position(|x| {
self.module.struct_handle_at(x.struct_handle).module
!= ModuleHandleIndex::new(CompiledModule::IMPLEMENTED_MODULE_INDEX)
}) {
errors.push(VerificationError {
kind: IndexKind::StructDefinition,
idx,
err: VMStaticViolation::InvalidModuleHandle,
})
}
if let Some(idx) = self.module.function_defs().iter().position(|x| {
self.module.function_handle_at(x.function).module
!= ModuleHandleIndex::new(CompiledModule::IMPLEMENTED_MODULE_INDEX)
}) {
errors.push(VerificationError {
kind: IndexKind::FunctionDefinition,
idx,
err: VMStaticViolation::InvalidModuleHandle,
})
}
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
== ModuleHandleIndex::new(CompiledModule::IMPLEMENTED_MODULE_INDEX)
&& !implemented_struct_handles.contains(&y)
}) {
errors.push(VerificationError {
kind: IndexKind::StructHandle,
idx,
err: VMStaticViolation::UnimplementedHandle,
})
}
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
== ModuleHandleIndex::new(CompiledModule::IMPLEMENTED_MODULE_INDEX)
&& !implemented_function_handles.contains(&y)
}) {
errors.push(VerificationError {
kind: IndexKind::FunctionHandle,
idx,
err: VMStaticViolation::UnimplementedHandle,
})
}
errors
}
fn first_duplicate_element<T>(iter: T) -> Option<usize>
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);
}
}
None
}
}