use crate::{
dispatch_table_type,
metadata::{
tables::{
CodedIndex, ConstantRaw, CustomAttributeRaw, DeclSecurityRaw, FieldMarshalRaw,
GenericParamConstraintRaw, GenericParamRaw, InterfaceImplRaw, MemberRefRaw,
MetadataTable, MethodImplRaw, MethodSpecRaw, NestedClassRaw, TableId, TypeDefRaw,
},
token::Token,
validation::{
context::{RawValidationContext, ValidationContext},
shared::{ReferenceValidator, TokenValidator},
traits::RawValidator,
},
},
Error, Result,
};
use strum::IntoEnumIterator;
pub struct RawTokenValidator;
impl RawTokenValidator {
#[must_use]
pub fn new() -> Self {
Self
}
fn validate_cross_table_references(context: &RawValidationContext) -> Result<()> {
let assembly_view = context.assembly_view();
let token_validator = TokenValidator::new(context.reference_scanner());
let reference_validator = ReferenceValidator::new(context.reference_scanner());
let mut referenced_tokens = Vec::new();
if let Some(tables) = assembly_view.tables() {
if let Some(table) = tables.table::<TypeDefRaw>() {
for typedef in table {
if typedef.extends.row != 0 {
referenced_tokens.push(typedef.extends.token);
}
}
}
if let Some(table) = tables.table::<InterfaceImplRaw>() {
for interface_impl in table {
token_validator.validate_table_row(TableId::TypeDef, interface_impl.class)?;
referenced_tokens.push(interface_impl.interface.token);
}
}
if let Some(table) = tables.table::<MemberRefRaw>() {
for memberref in table {
referenced_tokens.push(memberref.class.token);
}
}
if let Some(table) = tables.table::<CustomAttributeRaw>() {
for attr in table {
referenced_tokens.push(attr.parent.token);
referenced_tokens.push(attr.constructor.token);
}
}
if let Some(table) = tables.table::<NestedClassRaw>() {
for nested in table {
token_validator.validate_table_row(TableId::TypeDef, nested.nested_class)?;
token_validator.validate_table_row(TableId::TypeDef, nested.enclosing_class)?;
}
}
if let Some(table) = tables.table::<GenericParamRaw>() {
for genparam in table {
referenced_tokens.push(genparam.owner.token);
}
}
if let Some(table) = tables.table::<MethodSpecRaw>() {
for methodspec in table {
referenced_tokens.push(methodspec.method.token);
}
}
if let Some(table) = tables.table::<GenericParamConstraintRaw>() {
for constraint in table {
token_validator.validate_table_row(TableId::GenericParam, constraint.owner)?;
referenced_tokens.push(constraint.constraint.token);
}
}
if let Some(table) = tables.table::<MethodImplRaw>() {
for method_impl in table {
token_validator.validate_table_row(TableId::TypeDef, method_impl.class)?;
referenced_tokens.push(method_impl.method_body.token);
referenced_tokens.push(method_impl.method_declaration.token);
}
}
if let Some(table) = tables.table::<ConstantRaw>() {
for constant in table {
referenced_tokens.push(constant.parent.token);
}
}
if let Some(table) = tables.table::<FieldMarshalRaw>() {
for marshal in table {
referenced_tokens.push(marshal.parent.token);
}
}
if let Some(table) = tables.table::<DeclSecurityRaw>() {
for security in table {
referenced_tokens.push(security.parent.token);
}
}
}
reference_validator.validate_token_references(referenced_tokens)?;
Ok(())
}
fn validate_token_references(context: &RawValidationContext) -> Result<()> {
let assembly_view = context.assembly_view();
let token_validator = TokenValidator::new(context.reference_scanner());
let reference_validator = ReferenceValidator::new(context.reference_scanner());
if let Some(tables) = assembly_view.tables() {
if let Some(table) = tables.table::<InterfaceImplRaw>() {
Self::validate_interfaceimpl_tokens(table, &token_validator, &reference_validator)?;
}
if let Some(table) = tables.table::<MemberRefRaw>() {
Self::validate_memberref_tokens(table, &token_validator, &reference_validator)?;
}
if let Some(table) = tables.table::<CustomAttributeRaw>() {
Self::validate_customattribute_tokens(
table,
&token_validator,
&reference_validator,
)?;
}
if let Some(table) = tables.table::<NestedClassRaw>() {
Self::validate_nestedclass_tokens(table, &token_validator, &reference_validator)?;
}
if let Some(table) = tables.table::<GenericParamRaw>() {
Self::validate_genericparam_tokens(table, &token_validator, &reference_validator)?;
}
if let Some(table) = tables.table::<MethodSpecRaw>() {
Self::validate_methodspec_tokens(table, &token_validator, &reference_validator)?;
}
if let Some(table) = tables.table::<GenericParamConstraintRaw>() {
Self::validate_genericparamconstraint_tokens(
table,
&token_validator,
&reference_validator,
)?;
}
if let Some(table) = tables.table::<MethodImplRaw>() {
Self::validate_methodimpl_tokens(table, &token_validator, &reference_validator)?;
}
if let Some(table) = tables.table::<ConstantRaw>() {
Self::validate_constant_tokens(table, &token_validator, &reference_validator)?;
}
if let Some(table) = tables.table::<FieldMarshalRaw>() {
Self::validate_fieldmarshal_tokens(table, &token_validator, &reference_validator)?;
}
if let Some(table) = tables.table::<DeclSecurityRaw>() {
Self::validate_declsecurity_tokens(table, &token_validator, &reference_validator)?;
}
}
Ok(())
}
fn validate_rid_bounds(context: &RawValidationContext) -> Result<()> {
let assembly_view = context.assembly_view();
let token_validator = TokenValidator::new(context.reference_scanner());
if let Some(tables) = assembly_view.tables() {
for table_id in TableId::iter() {
let table_type = table_id as u32;
dispatch_table_type!(table_id, |RawType| {
if let Some(table) = tables.table::<RawType>() {
let row_count = table.row_count;
if row_count > 0x00FF_FFFF {
let token_value = (table_type << 24) | row_count;
return Err(Error::InvalidToken {
token: Token::new(token_value),
message: format!(
"{table_id:?} table RID {row_count} exceeds maximum allowed value (0x00FFFFFF)"
),
});
}
for rid in 1..=row_count {
token_validator.validate_table_row(table_id, rid)?;
}
}
Ok(()) as Result<()>
})?;
}
}
Ok(())
}
fn validate_coded_indexes(context: &RawValidationContext) -> Result<()> {
let assembly_view = context.assembly_view();
let token_validator = TokenValidator::new(context.reference_scanner());
let reference_validator = ReferenceValidator::new(context.reference_scanner());
if let Some(tables) = assembly_view.tables() {
if let Some(table) = tables.table::<TypeDefRaw>() {
for typedef in table {
Self::validate_coded_index_field(
&typedef.extends,
&token_validator,
&reference_validator,
)?;
}
}
if let Some(table) = tables.table::<InterfaceImplRaw>() {
for interface_impl in table {
Self::validate_coded_index_field(
&interface_impl.interface,
&token_validator,
&reference_validator,
)?;
}
}
if let Some(table) = tables.table::<MemberRefRaw>() {
for memberref in table {
Self::validate_coded_index_field(
&memberref.class,
&token_validator,
&reference_validator,
)?;
}
}
if let Some(table) = tables.table::<CustomAttributeRaw>() {
for attr in table {
Self::validate_coded_index_field(
&attr.parent,
&token_validator,
&reference_validator,
)?;
Self::validate_coded_index_field(
&attr.constructor,
&token_validator,
&reference_validator,
)?;
}
}
if let Some(table) = tables.table::<GenericParamRaw>() {
for genparam in table {
Self::validate_coded_index_field(
&genparam.owner,
&token_validator,
&reference_validator,
)?;
}
}
if let Some(table) = tables.table::<MethodSpecRaw>() {
for methodspec in table {
Self::validate_coded_index_field(
&methodspec.method,
&token_validator,
&reference_validator,
)?;
}
}
if let Some(table) = tables.table::<GenericParamConstraintRaw>() {
for constraint in table {
Self::validate_coded_index_field(
&constraint.constraint,
&token_validator,
&reference_validator,
)?;
}
}
if let Some(table) = tables.table::<ConstantRaw>() {
for constant in table {
Self::validate_coded_index_field(
&constant.parent,
&token_validator,
&reference_validator,
)?;
}
}
if let Some(table) = tables.table::<FieldMarshalRaw>() {
for marshal in table {
Self::validate_coded_index_field(
&marshal.parent,
&token_validator,
&reference_validator,
)?;
}
}
if let Some(table) = tables.table::<DeclSecurityRaw>() {
for security in table {
Self::validate_coded_index_field(
&security.parent,
&token_validator,
&reference_validator,
)?;
}
}
}
Ok(())
}
fn validate_interfaceimpl_tokens(
table: &MetadataTable<InterfaceImplRaw>,
token_validator: &TokenValidator,
reference_validator: &ReferenceValidator,
) -> Result<()> {
for interface_impl in table {
token_validator.validate_table_row(TableId::TypeDef, interface_impl.class)?;
let interface_token = interface_impl.interface.token;
let allowed_tables = interface_impl.interface.ci_type.tables();
token_validator.validate_typed_token(interface_token, allowed_tables)?;
reference_validator.validate_token_integrity(interface_token)?;
}
Ok(())
}
fn validate_memberref_tokens(
table: &MetadataTable<MemberRefRaw>,
token_validator: &TokenValidator,
reference_validator: &ReferenceValidator,
) -> Result<()> {
for memberref in table {
let class_token = memberref.class.token;
let allowed_tables = memberref.class.ci_type.tables();
token_validator.validate_typed_token(class_token, allowed_tables)?;
reference_validator.validate_token_integrity(class_token)?;
}
Ok(())
}
fn validate_customattribute_tokens(
table: &MetadataTable<CustomAttributeRaw>,
token_validator: &TokenValidator,
reference_validator: &ReferenceValidator,
) -> Result<()> {
for attr in table {
let parent_token = attr.parent.token;
token_validator.validate_token_bounds(parent_token)?;
reference_validator.validate_token_integrity(parent_token)?;
let constructor_token = attr.constructor.token;
let allowed_tables = attr.constructor.ci_type.tables();
token_validator.validate_typed_token(constructor_token, allowed_tables)?;
reference_validator.validate_token_integrity(constructor_token)?;
}
Ok(())
}
fn validate_nestedclass_tokens(
table: &MetadataTable<NestedClassRaw>,
token_validator: &TokenValidator,
reference_validator: &ReferenceValidator,
) -> Result<()> {
for nested in table {
token_validator.validate_table_row(TableId::TypeDef, nested.nested_class)?;
token_validator.validate_table_row(TableId::TypeDef, nested.enclosing_class)?;
let nested_class_token =
Token::new(u32::from(TableId::TypeDef.token_type()) << 24 | nested.nested_class);
let enclosing_class_token =
Token::new(u32::from(TableId::TypeDef.token_type()) << 24 | nested.enclosing_class);
reference_validator
.validate_nested_class_relationship(enclosing_class_token, nested_class_token)?;
}
Ok(())
}
fn validate_genericparam_tokens(
table: &MetadataTable<GenericParamRaw>,
token_validator: &TokenValidator,
reference_validator: &ReferenceValidator,
) -> Result<()> {
for genparam in table {
let owner_token = genparam.owner.token;
let allowed_tables = genparam.owner.ci_type.tables();
token_validator.validate_typed_token(owner_token, allowed_tables)?;
reference_validator.validate_token_integrity(owner_token)?;
}
Ok(())
}
fn validate_methodspec_tokens(
table: &MetadataTable<MethodSpecRaw>,
token_validator: &TokenValidator,
reference_validator: &ReferenceValidator,
) -> Result<()> {
for methodspec in table {
let method_token = methodspec.method.token;
let allowed_tables = methodspec.method.ci_type.tables();
token_validator.validate_typed_token(method_token, allowed_tables)?;
reference_validator.validate_token_integrity(method_token)?;
}
Ok(())
}
fn validate_genericparamconstraint_tokens(
table: &MetadataTable<GenericParamConstraintRaw>,
token_validator: &TokenValidator,
reference_validator: &ReferenceValidator,
) -> Result<()> {
for constraint in table {
token_validator.validate_table_row(TableId::GenericParam, constraint.owner)?;
let constraint_token = constraint.constraint.token;
let allowed_tables = constraint.constraint.ci_type.tables();
token_validator.validate_typed_token(constraint_token, allowed_tables)?;
reference_validator.validate_token_integrity(constraint_token)?;
}
Ok(())
}
fn validate_methodimpl_tokens(
table: &MetadataTable<MethodImplRaw>,
token_validator: &TokenValidator,
reference_validator: &ReferenceValidator,
) -> Result<()> {
for method_impl in table {
token_validator.validate_table_row(TableId::TypeDef, method_impl.class)?;
let body_token = method_impl.method_body.token;
let allowed_tables = method_impl.method_body.ci_type.tables();
token_validator.validate_typed_token(body_token, allowed_tables)?;
reference_validator.validate_token_integrity(body_token)?;
let declaration_token = method_impl.method_declaration.token;
let allowed_tables = method_impl.method_declaration.ci_type.tables();
token_validator.validate_typed_token(declaration_token, allowed_tables)?;
reference_validator.validate_token_integrity(declaration_token)?;
}
Ok(())
}
fn validate_constant_tokens(
table: &MetadataTable<ConstantRaw>,
token_validator: &TokenValidator,
reference_validator: &ReferenceValidator,
) -> Result<()> {
for constant in table {
let parent_token = constant.parent.token;
token_validator.validate_token_bounds(parent_token)?;
reference_validator.validate_token_integrity(parent_token)?;
}
Ok(())
}
fn validate_fieldmarshal_tokens(
table: &MetadataTable<FieldMarshalRaw>,
token_validator: &TokenValidator,
reference_validator: &ReferenceValidator,
) -> Result<()> {
for marshal in table {
let parent_token = marshal.parent.token;
let allowed_tables = marshal.parent.ci_type.tables();
token_validator.validate_typed_token(parent_token, allowed_tables)?;
reference_validator.validate_token_integrity(parent_token)?;
}
Ok(())
}
fn validate_declsecurity_tokens(
table: &MetadataTable<DeclSecurityRaw>,
token_validator: &TokenValidator,
reference_validator: &ReferenceValidator,
) -> Result<()> {
for security in table {
let parent_token = security.parent.token;
let allowed_tables = security.parent.ci_type.tables();
token_validator.validate_typed_token(parent_token, allowed_tables)?;
reference_validator.validate_token_integrity(parent_token)?;
}
Ok(())
}
fn validate_coded_index_field(
coded_index: &CodedIndex,
token_validator: &TokenValidator,
reference_validator: &ReferenceValidator,
) -> Result<()> {
if coded_index.row != 0 {
let token = coded_index.token;
let allowed_tables = coded_index.ci_type.tables();
token_validator.validate_typed_token(token, allowed_tables)?;
reference_validator.validate_token_integrity(token)?;
}
Ok(())
}
}
impl RawValidator for RawTokenValidator {
fn validate_raw(&self, context: &RawValidationContext) -> Result<()> {
Self::validate_token_references(context)?;
Self::validate_rid_bounds(context)?;
Self::validate_coded_indexes(context)?;
if context.config().enable_cross_table_validation {
Self::validate_cross_table_references(context)?;
}
Ok(())
}
fn name(&self) -> &'static str {
"RawTokenValidator"
}
fn priority(&self) -> u32 {
200
}
fn should_run(&self, context: &RawValidationContext) -> bool {
context.config().enable_structural_validation
}
}
impl Default for RawTokenValidator {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
metadata::validation::ValidationConfig,
test::{
factories::validation::raw_structure_token::*, get_testfile_wb, validator_test,
TestAssembly,
},
Error,
};
#[test]
fn test_raw_token_validator_comprehensive() -> Result<()> {
let validator = RawTokenValidator::new();
validator_test(
raw_token_validator_file_factory,
"RawTokenValidator",
"InvalidRid",
ValidationConfig {
enable_structural_validation: true,
enable_cross_table_validation: true,
..Default::default()
},
|context| validator.validate_raw(context),
)
}
#[test]
fn test_raw_token_validator_configuration() -> Result<()> {
let validator = RawTokenValidator::new();
fn clean_only_factory() -> Result<Vec<TestAssembly>> {
let Some(clean_testfile) = get_testfile_wb() else {
return Err(Error::Other("WindowsBase.dll not available".to_string()));
};
Ok(vec![TestAssembly::new(&clean_testfile, true)])
}
let result_disabled = validator_test(
clean_only_factory,
"RawTokenValidator",
"InvalidRid",
ValidationConfig {
enable_structural_validation: false,
..Default::default()
},
|context| {
if validator.should_run(context) {
validator.validate_raw(context)
} else {
Ok(())
}
},
);
assert!(
result_disabled.is_ok(),
"Configuration test failed: validator should not run when disabled"
);
let result_enabled = validator_test(
clean_only_factory,
"RawTokenValidator",
"InvalidRid",
ValidationConfig {
enable_structural_validation: true,
..Default::default()
},
|context| validator.validate_raw(context),
);
assert!(
result_enabled.is_ok(),
"Configuration test failed: validator should run when enabled"
);
Ok(())
}
#[test]
fn test_raw_token_validator_metadata() {
let validator = RawTokenValidator::new();
assert_eq!(validator.name(), "RawTokenValidator");
assert_eq!(validator.priority(), 200);
let _config_enabled = ValidationConfig {
enable_structural_validation: true,
..Default::default()
};
let _config_disabled = ValidationConfig {
enable_structural_validation: false,
..Default::default()
};
}
}