use crate::{
metadata::{
cilassemblyview::CilAssemblyView,
tables::{
FieldRaw, MemberRefRaw, MethodDefRaw, PropertyRaw, StandAloneSigRaw, TypeSpecRaw,
},
validation::{
context::{RawValidationContext, ValidationContext},
traits::RawValidator,
},
},
Error, Result,
};
pub struct RawSignatureValidator;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum SignatureKind {
Method,
Field,
Property,
LocalVar,
TypeSpec,
MemberRef,
}
impl RawSignatureValidator {
#[must_use]
pub fn new() -> Self {
Self
}
fn validate_signature_blob_integrity(
assembly_view: &CilAssemblyView,
blob_index: u32,
expected_kind: SignatureKind,
) -> Result<()> {
if blob_index == 0 {
return Ok(());
}
let Some(blob_heap) = assembly_view.blobs() else {
return Err(Error::ValidationRawFailed {
validator: "RawSignatureValidator".to_string(),
message: "Signature validation requires blob heap access".to_string(),
});
};
let blob_data =
blob_heap
.get(blob_index as usize)
.map_err(|_| Error::ValidationRawFailed {
validator: "RawSignatureValidator".to_string(),
message: format!("Signature blob index {blob_index} exceeds blob heap bounds"),
})?;
if blob_data.is_empty() {
return Err(Error::ValidationRawFailed {
validator: "RawSignatureValidator".to_string(),
message: format!("Signature blob at index {blob_index} is empty"),
});
}
let calling_convention = blob_data[0];
Self::validate_calling_convention(calling_convention, expected_kind, blob_index)?;
if matches!(
expected_kind,
SignatureKind::Method | SignatureKind::LocalVar | SignatureKind::Property
) {
if blob_data.len() < 2 {
return Err(Error::ValidationRawFailed {
validator: "RawSignatureValidator".to_string(),
message: format!(
"Signature blob at index {blob_index} too short for parameter count"
),
});
}
Self::validate_compressed_integer(&blob_data[1..], blob_index)?;
}
Self::validate_blob_bounds(blob_data, blob_index)?;
Ok(())
}
fn validate_calling_convention(
calling_convention: u8,
expected_kind: SignatureKind,
blob_index: u32,
) -> Result<()> {
match expected_kind {
SignatureKind::Method | SignatureKind::MemberRef => {
let base_convention = calling_convention & 0x0F;
if base_convention > 0x05 {
return Err(Error::ValidationRawFailed {
validator: "RawSignatureValidator".to_string(),
message: format!("Invalid method calling convention 0x{calling_convention:02X} in signature blob {blob_index}"),
});
}
}
SignatureKind::Field => {
if calling_convention != 0x06 {
return Err(Error::ValidationRawFailed {
validator: "RawSignatureValidator".to_string(),
message: format!("Invalid field signature marker 0x{calling_convention:02X} in blob {blob_index}, expected 0x06"),
});
}
}
SignatureKind::Property => {
let base_convention = calling_convention & 0x0F;
if base_convention != 0x08 {
return Err(Error::ValidationRawFailed {
validator: "RawSignatureValidator".to_string(),
message: format!("Invalid property signature marker 0x{calling_convention:02X} in blob {blob_index}, expected 0x08"),
});
}
}
SignatureKind::LocalVar => {
if calling_convention != 0x07 {
return Err(Error::ValidationRawFailed {
validator: "RawSignatureValidator".to_string(),
message: format!("Invalid local variable signature marker 0x{calling_convention:02X} in blob {blob_index}, expected 0x07"),
});
}
}
SignatureKind::TypeSpec => {
if calling_convention == 0x00 {
return Err(Error::ValidationRawFailed {
validator: "RawSignatureValidator".to_string(),
message: format!("Invalid type specification signature marker 0x{calling_convention:02X} in blob {blob_index}"),
});
}
}
}
Ok(())
}
fn validate_compressed_integer(data: &[u8], blob_index: u32) -> Result<()> {
if data.is_empty() {
return Err(Error::ValidationRawFailed {
validator: "RawSignatureValidator".to_string(),
message: format!("Insufficient data for compressed integer in blob {blob_index}"),
});
}
let first_byte = data[0];
if (first_byte & 0x80) == 0 {
Ok(())
} else if (first_byte & 0xC0) == 0x80 {
if data.len() < 2 {
return Err(Error::ValidationRawFailed {
validator: "RawSignatureValidator".to_string(),
message: format!(
"Insufficient data for 2-byte compressed integer in blob {blob_index}"
),
});
}
Ok(())
} else if (first_byte & 0xE0) == 0xC0 {
if data.len() < 4 {
return Err(Error::ValidationRawFailed {
validator: "RawSignatureValidator".to_string(),
message: format!(
"Insufficient data for 4-byte compressed integer in blob {blob_index}"
),
});
}
Ok(())
} else {
Err(Error::ValidationRawFailed {
validator: "RawSignatureValidator".to_string(),
message: format!(
"Invalid compressed integer encoding 0x{first_byte:02X} in blob {blob_index}"
),
})
}
}
fn validate_blob_bounds(blob_data: &[u8], blob_index: u32) -> Result<()> {
if blob_data.len() > 65536 {
return Err(Error::ValidationRawFailed {
validator: "RawSignatureValidator".to_string(),
message: format!(
"Signature blob {} exceeds maximum reasonable size ({})",
blob_index,
blob_data.len()
),
});
}
Ok(())
}
}
impl RawValidator for RawSignatureValidator {
fn validate_raw(&self, context: &RawValidationContext) -> Result<()> {
if !self.should_run(context) {
return Ok(());
}
let assembly_view = context.assembly_view();
let Some(tables) = assembly_view.tables() else {
return Ok(());
};
if let Some(table) = tables.table::<MethodDefRaw>() {
for method in table {
if let Some(blob_heap) = assembly_view.blobs() {
if let Ok(blob_data) = blob_heap.get(method.signature as usize) {
if !blob_data.is_empty() {
let calling_convention = blob_data[0];
let signature_kind = match calling_convention {
0x06 => SignatureKind::Field,
_ => SignatureKind::Method,
};
Self::validate_signature_blob_integrity(
assembly_view,
method.signature,
signature_kind,
)?;
}
}
} else {
Self::validate_signature_blob_integrity(
assembly_view,
method.signature,
SignatureKind::Method,
)?;
}
}
}
if let Some(table) = tables.table::<FieldRaw>() {
for field in table {
Self::validate_signature_blob_integrity(
assembly_view,
field.signature,
SignatureKind::Field,
)?;
}
}
if let Some(table) = tables.table::<PropertyRaw>() {
for property in table {
Self::validate_signature_blob_integrity(
assembly_view,
property.signature,
SignatureKind::Property,
)?;
}
}
if let Some(table) = tables.table::<StandAloneSigRaw>() {
for standalone_sig in table {
if let Some(blob_heap) = assembly_view.blobs() {
if let Ok(blob_data) = blob_heap.get(standalone_sig.signature as usize) {
if !blob_data.is_empty() {
let calling_convention = blob_data[0];
let signature_kind = match calling_convention {
0x07 => SignatureKind::LocalVar,
0x06 => SignatureKind::Field,
_ => SignatureKind::Method,
};
Self::validate_signature_blob_integrity(
assembly_view,
standalone_sig.signature,
signature_kind,
)?;
}
}
}
}
}
if let Some(table) = tables.table::<TypeSpecRaw>() {
for type_spec in table {
Self::validate_signature_blob_integrity(
assembly_view,
type_spec.signature,
SignatureKind::TypeSpec,
)?;
}
}
if let Some(table) = tables.table::<MemberRefRaw>() {
for member_ref in table {
if let Some(blob_heap) = assembly_view.blobs() {
if let Ok(blob_data) = blob_heap.get(member_ref.signature as usize) {
if !blob_data.is_empty() {
let calling_convention = blob_data[0];
let signature_kind = match calling_convention {
0x06 => SignatureKind::Field,
_ => SignatureKind::Method,
};
Self::validate_signature_blob_integrity(
assembly_view,
member_ref.signature,
signature_kind,
)?;
}
}
} else {
Self::validate_signature_blob_integrity(
assembly_view,
member_ref.signature,
SignatureKind::MemberRef,
)?;
}
}
}
Ok(())
}
fn priority(&self) -> u32 {
175
}
fn name(&self) -> &'static str {
"RawSignatureValidator"
}
fn should_run(&self, context: &RawValidationContext) -> bool {
context.config().enable_token_validation
}
}
impl Default for RawSignatureValidator {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
metadata::validation::ValidationConfig,
test::{factories::validation::raw_structure_signature::*, validator_test},
Result,
};
#[test]
fn test_raw_signature_validator() -> Result<()> {
let validator = RawSignatureValidator::new();
let config = ValidationConfig {
enable_token_validation: true,
..Default::default()
};
validator_test(
raw_signature_validator_file_factory,
"RawSignatureValidator",
"ValidationRawFailed",
config,
|context| validator.validate_raw(context),
)
}
}