Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,6 @@ We're continuously working to improve `dotscope` and add new capabilities. Here
### Enhanced Parsing and Security

- String/Blob caching infrastructure
- PortablePDB support
- Non-embedded resource support

### Performance and Scalability
Expand Down
28 changes: 27 additions & 1 deletion src/disassembler/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ use crate::disassembler::{FlowType, Instruction};
/// assert_eq!(block.instructions.len(), 0);
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// [`BasicBlock`] is [`std::marker::Send`] and [`std::marker::Sync`] as all fields are thread-safe types.
/// Multiple threads can safely read from the same block concurrently, but mutation requires
/// external synchronization.
#[derive(Debug, Clone)]
pub struct BasicBlock {
/// Unique identifier for this block within the method
Expand Down Expand Up @@ -105,7 +111,7 @@ impl BasicBlock {
///
/// # Returns
///
/// A new [`crate::disassembler::block::BasicBlock`] instance ready for instruction insertion.
/// A new [`BasicBlock`] instance ready for instruction insertion.
///
/// # Examples
///
Expand All @@ -120,6 +126,10 @@ impl BasicBlock {
/// assert_eq!(entry_block.size, 0);
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// This method is thread-safe and can be called concurrently from multiple threads.
#[must_use]
pub fn new(id: usize, rva: u64, offset: usize) -> Self {
Self {
Expand Down Expand Up @@ -159,6 +169,10 @@ impl BasicBlock {
/// // assert!(block.instruction_first().is_some());
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// This method is thread-safe and can be called concurrently from multiple threads.
#[must_use]
pub fn instruction_first(&self) -> Option<&Instruction> {
self.instructions.first()
Expand Down Expand Up @@ -192,6 +206,10 @@ impl BasicBlock {
/// // }
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// This method is thread-safe and can be called concurrently from multiple threads.
#[must_use]
pub fn instruction_last(&self) -> Option<&Instruction> {
self.instructions.last()
Expand Down Expand Up @@ -223,6 +241,10 @@ impl BasicBlock {
/// assert!(!block.is_entry());
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// This method is thread-safe and can be called concurrently from multiple threads.
#[must_use]
pub fn is_entry(&self) -> bool {
self.predecessors.is_empty()
Expand Down Expand Up @@ -257,6 +279,10 @@ impl BasicBlock {
/// // assert!(block.is_exit());
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// This method is thread-safe and can be called concurrently from multiple threads.
#[must_use]
pub fn is_exit(&self) -> bool {
if let Some(last_instr) = self.instruction_last() {
Expand Down
42 changes: 36 additions & 6 deletions src/disassembler/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,16 @@ use crate::{
Result,
};

/// A stateful decoder instance, that exposes the more complex disassembly algorithm
/// in a simple manner to be used by the framework and exposed methods
/// A stateful decoder instance that exposes complex disassembly algorithms.
///
/// The [`Decoder`] maintains context during CIL bytecode disassembly, tracking visited
/// addresses and building control flow relationships between basic blocks. It provides
/// the core implementation for all higher-level disassembly functions.
///
/// # Thread Safety
///
/// [`Decoder`] is not [`std::marker::Send`] or [`std::marker::Sync`] due to mutable references
/// to parser state. Each thread should use its own decoder instance.
struct Decoder<'a> {
/// Collection of decoded basic blocks
blocks: Vec<BasicBlock>,
Expand All @@ -87,7 +95,7 @@ struct Decoder<'a> {
}

impl<'a> Decoder<'a> {
/// Create a new stateful Decoder
/// Create a new stateful decoder for CIL bytecode disassembly.
///
/// Initializes a decoder instance for processing CIL bytecode into basic blocks.
/// The decoder maintains state during disassembly, tracking visited addresses
Expand All @@ -109,6 +117,10 @@ impl<'a> Decoder<'a> {
/// # Errors
///
/// Returns [`crate::Error::OutOfBounds`] if the offset exceeds the parser's data length.
///
/// # Thread Safety
///
/// This method is thread-safe and can be called concurrently from multiple threads.
pub fn new(
parser: &'a mut Parser<'a>,
offset: usize,
Expand Down Expand Up @@ -139,7 +151,7 @@ impl<'a> Decoder<'a> {
///
/// # Returns
///
/// A slice containing all decoded [`crate::disassembler::block::BasicBlock`] instances.
/// A slice containing all decoded [`crate::disassembler::BasicBlock`] instances.
///
/// # Examples
///
Expand All @@ -149,6 +161,10 @@ impl<'a> Decoder<'a> {
/// let blocks = decoder.blocks();
/// println!("Decoded {} basic blocks", blocks.len());
/// ```
///
/// # Thread Safety
///
/// This method is thread-safe and can be called concurrently from multiple threads.
pub fn blocks(&self) -> &[BasicBlock] {
&self.blocks
}
Expand Down Expand Up @@ -185,6 +201,10 @@ impl<'a> Decoder<'a> {
///
/// Note: [`crate::disassembler::decode_blocks`] function internally uses this method to return ownership
/// of the blocks to the caller.
///
/// # Thread Safety
///
/// This method is thread-safe and can be called concurrently from multiple threads.
pub fn into_blocks(self) -> Vec<BasicBlock> {
self.blocks
}
Expand All @@ -207,10 +227,15 @@ impl<'a> Decoder<'a> {
Ok(())
}

/// Process a single block, adding its instructions and successor blocks
/// Process a single block, adding its instructions and successor blocks.
///
/// # Arguments
///
/// ## Arguments
/// * `block_id` - The id of the block to decode
///
/// # Errors
///
/// Returns [`crate::Error::OutOfBounds`] if the block offset exceeds parser bounds.
fn decode_block(&mut self, block_id: usize) -> Result<()> {
if self.blocks[block_id].offset > self.parser.len() {
return Err(OutOfBounds);
Expand Down Expand Up @@ -620,6 +645,11 @@ pub fn decode_stream(parser: &mut Parser, rva: u64) -> Result<Vec<Instruction>>
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// This function is thread-safe and can be called concurrently from multiple threads
/// with different parser instances.
///
/// # Implementation Notes
///
/// - Handles both 0xFE-prefixed extended opcodes and standard single-byte opcodes
Expand Down
47 changes: 47 additions & 0 deletions src/disassembler/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ use std::fmt::{self, UpperHex};
/// let token_operand = OperandType::Token; // ldtoken takes a metadata token
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// [`OperandType`] is [`std::marker::Send`] and [`std::marker::Sync`] as it only contains primitive data.
/// All variants are safe to share across threads without synchronization.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OperandType {
/// No operand present
Expand Down Expand Up @@ -117,6 +122,11 @@ pub enum OperandType {
/// assert_eq!(as_u64, 42);
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// [`Immediate`] is [`std::marker::Send`] and [`std::marker::Sync`] as it only contains primitive data.
/// All numeric and floating-point values are safe to share across threads.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Immediate {
/// Signed 8-bit immediate value
Expand Down Expand Up @@ -198,6 +208,11 @@ impl From<Immediate> for u64 {
/// let metadata_ref = Operand::Token(Token::new(0x06000001));
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// [`Operand`] is [`std::marker::Send`] and [`std::marker::Sync`] as all variants contain thread-safe types.
/// This includes primitives, [`crate::disassembler::instruction::Immediate`], [`crate::metadata::token::Token`], and [`std::vec::Vec`].
#[derive(Debug, Clone)]
pub enum Operand {
/// No operand present
Expand Down Expand Up @@ -233,6 +248,11 @@ pub enum Operand {
/// let ret = FlowType::Return; // ret instruction
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// [`FlowType`] is [`std::marker::Send`] and [`std::marker::Sync`] as it only contains unit variants.
/// All variants are safe to share across threads without synchronization.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FlowType {
/// Normal execution continues to next instruction
Expand Down Expand Up @@ -280,6 +300,11 @@ pub enum FlowType {
/// };
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// [`StackBehavior`] is [`std::marker::Send`] and [`std::marker::Sync`] as it only contains primitive integer fields.
/// All instances can be safely shared across threads without synchronization.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StackBehavior {
/// Number of items popped from stack
Expand All @@ -306,6 +331,11 @@ pub struct StackBehavior {
/// let load_store = InstructionCategory::LoadStore; // ldloc, stfld
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// [`InstructionCategory`] is [`std::marker::Send`] and [`std::marker::Sync`] as it only contains unit variants.
/// All variants are safe to share across threads without synchronization.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InstructionCategory {
/// Arithmetic operations (add, sub, mul, div, rem, neg)
Expand Down Expand Up @@ -356,6 +386,11 @@ pub enum InstructionCategory {
/// println!("Stack effect: {:?}", instruction.stack_behavior);
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// [`Instruction`] is [`std::marker::Send`] and [`std::marker::Sync`] as all fields contain thread-safe types.
/// This includes primitives, static string references, and owned collections that can be safely shared across threads.
#[derive(Clone)]
pub struct Instruction {
// Core fields
Expand Down Expand Up @@ -418,6 +453,10 @@ impl Instruction {
/// assert!(instruction.is_branch());
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// This method is thread-safe and can be called concurrently from multiple threads.
#[must_use]
pub fn is_branch(&self) -> bool {
matches!(
Expand Down Expand Up @@ -454,6 +493,10 @@ impl Instruction {
/// assert!(ret_instruction.is_terminal());
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// This method is thread-safe and can be called concurrently from multiple threads.
#[must_use]
pub fn is_terminal(&self) -> bool {
matches!(
Expand Down Expand Up @@ -497,6 +540,10 @@ impl Instruction {
/// assert_eq!(targets, vec![0x2000]);
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// This method is thread-safe and can be called concurrently from multiple threads.
#[must_use]
pub fn get_targets(&self) -> Vec<u64> {
match self.flow_type {
Expand Down
6 changes: 6 additions & 0 deletions src/disassembler/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ use crate::disassembler::{FlowType, InstructionCategory, OperandType};
/// };
/// # Ok::<(), dotscope::Error>(())
/// ```
///
/// # Thread Safety
///
/// [`CilInstruction`] is [`std::marker::Send`] and [`std::marker::Sync`] as all fields contain thread-safe types.
/// This includes primitives, static string references, and [`crate::disassembler::instruction::OperandType`],
/// [`crate::disassembler::instruction::InstructionCategory`], and [`crate::disassembler::instruction::FlowType`] enums.
pub struct CilInstruction<'a> {
/// The [`crate::disassembler::OperandType`] that this instruction expects
pub op_type: OperandType,
Expand Down
8 changes: 8 additions & 0 deletions src/disassembler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@
//! # Ok::<(), dotscope::Error>(())
//! ```
//!
//! # Thread Safety
//!
//! All public types in this module are designed to be thread-safe where appropriate.
//! [`crate::disassembler::Instruction`], [`crate::disassembler::BasicBlock`], and related types
//! implement [`std::marker::Send`] and [`std::marker::Sync`] as they contain only
//! thread-safe data. The decoder functions can be called concurrently from different threads
//! with separate parser instances.
//!
//! # Integration
//!
//! This module integrates with:
Expand Down
13 changes: 13 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@
//! }
//! ```
//!
//! # Thread Safety
//!
//! All error types in this module are thread-safe. The [`crate::Error`] enum implements
//! [`std::marker::Send`] and [`std::marker::Sync`], allowing errors to be safely passed
//! between threads and shared across thread boundaries. This enables proper error
//! propagation in concurrent parsing and analysis operations.
//!
//! # Integration
//!
//! This module integrates with:
Expand Down Expand Up @@ -181,6 +188,12 @@ macro_rules! malformed_error {
/// - [`crate::Error::RecursionLimit`] - Maximum recursion depth exceeded
/// - [`crate::Error::LockError`] - Thread synchronization failure
/// - [`crate::Error::GraphError`] - Dependency graph analysis error
///
/// # Thread Safety
///
/// This error enum is [`std::marker::Send`] and [`std::marker::Sync`] as all variants contain thread-safe types.
/// This includes owned strings, primitive values, and errors from external crates that are themselves
/// thread-safe. Errors can be safely passed between threads and shared across thread boundaries.
#[derive(Error, Debug)]
pub enum Error {
// File parsing Errors
Expand Down
Loading
Loading