Documents
Memory Safety And Unsafe Code Policy
Memory Safety And Unsafe Code Policy
Type
Topic
Status
Published
Created
Feb 28, 2026
Updated
Feb 28, 2026
Created by
Dosu Bot
Updated by
Dosu Bot

Memory Safety And Unsafe Code Policy#

The libmagic-rs project enforces an exceptionally strict memory safety policy that goes beyond Rust's default safety guarantees. The project uses unsafe_code = "forbid" as a workspace-level lint configuration, making it a compile-time error to introduce any unsafe code blocks in the project's own codebase. This policy is described as "a hardening mechanism, not a suggestion" in the project's developer documentation.

The policy encompasses multiple layers of enforcement: workspace lints in Cargo.toml, CI pipeline checks with clippy, mandatory code review requirements, and dependency vetting through cargo-deny. Beyond prohibiting unsafe code, the policy mandates specific safe coding patterns including bounds-checked buffer access, UTF-8-safe string operations, structured error handling with no panics, and RAII resource management patterns.

This policy distinguishes libmagic-rs from typical Rust projects by treating memory safety as an absolute requirement rather than a best-effort goal. While Rust's standard safety guarantees prevent many common memory errors, this project's additional restrictions create a defense-in-depth approach suitable for security-critical applications that handle potentially malicious input during file type detection.

Policy Enforcement Mechanisms#

Workspace Lint Configuration#

The foundation of the policy is the workspace-level lint configuration in Cargo.toml:

[workspace.lints.rust]
# Security: Forbid unsafe code globally
unsafe_code = "forbid"
# Zero warnings policy
warnings = "deny"

The forbid level is stronger than deny - it cannot be overridden at lower scopes (crate or module level), ensuring no unsafe code can be introduced anywhere in the project's codebase. The warnings = "deny" setting enforces a zero-warnings policy, treating all compiler warnings as errors.

CI Pipeline Enforcement#

The project's CI pipeline includes a quality job that runs just lint-rust, which executes clippy with the -D warnings flag, treating all clippy warnings as errors. This ensures that any violation of the safety policy causes the build to fail before code can be merged.

Multiple security-focused workflows provide additional enforcement:

Dependency Vetting#

The project uses cargo-deny configuration to ban unsafe C-based libraries in favor of pure-Rust alternatives:

[[bans.deny]]
name = "openssl"
wrappers = ["openssl-sys"]
use-instead = "rustls"

Only vetted dependencies with minimal unsafe code are permitted:

  • memmap2 - Memory mapping (audited, uses unsafe internally)
  • byteorder - Endianness handling (no unsafe code)

Code Review Requirements#

The project's code review guidelines mandate that reviewers check for:

PRs must not introduce unsafe code, unwrap()/expect() in library code, or panics.

Mandatory Safe Coding Patterns#

Bounds-Checked Buffer Access#

The project requires bounds checking for all buffer access. The codebase implements this through two approaches:

  1. Explicit Validation Pattern - A custom SafeBufferAccess trait with validation before access
  2. Safe Get Pattern - Using .get() with Result conversion:
pub fn safe_read_byte(buffer: &[u8], offset: BufferOffset) -> Result<u8, IoError> {
    buffer.get(offset).copied().ok_or(IoError::BufferOverrun {
        offset,
        length: 1,
        buffer_size: buffer.len(),
    })
}

Example of bounds checking in type reading:

let bytes = buffer
    .get(offset..offset + 2)
    .ok_or(TypeReadError::BufferOverrun {
        offset,
        buffer_len: buffer.len(),
    })?;

UTF-8 Safe String Operations#

The project mandates using strip_prefix()/strip_suffix() instead of direct string slicing to avoid UTF-8 panics. Direct slicing like &str[n..] can panic if the index falls in the middle of a multi-byte UTF-8 character.

Example from the message concatenation logic:

if let Some(rest) = m.message.strip_prefix('\u{0008}') {
    // Backspace suppresses the space and the character itself
    result.push_str(rest);
}

This pattern safely handles Unicode characters and returns None if the prefix doesn't match, preventing panics from invalid UTF-8 operations.

Structured Error Handling#

The project requires Result<T, E> patterns consistently and prohibits panics in library code. The comprehensive error type hierarchy uses thiserror for structured errors:

  • LibmagicError - Top-level error enum
  • ParseError - Magic file parsing errors
  • EvaluationError - Rule evaluation errors
  • TypeReadError - Type reading errors
  • IoError - I/O operation errors

Example of proper error handling with ? operator:

pub fn read_byte(buffer: &[u8], offset: usize) -> Result<Value, TypeReadError> {
    buffer
        .get(offset)
        .map(|&byte| Value::Uint(u64::from(byte)))
        .ok_or(TypeReadError::BufferOverrun {
            offset,
            buffer_len: buffer.len(),
        })
}

RAII Resource Management#

The project requires safe resource management with RAII patterns, ensuring resources are automatically cleaned up when they go out of scope. This prevents resource leaks without requiring explicit cleanup code.

Input Validation#

The policy includes comprehensive input validation requirements:

Vetted Dependencies#

While the project forbids unsafe code in its own codebase, it permits vetted dependencies with minimal unsafe code.

memmap2 - Memory Mapping#

memmap2 is used for efficient memory-mapped file I/O with an explicit safety annotation:

fn create_memory_mapping(file: &File, path_buf: &Path) -> Result<Mmap, IoError> {
    // SAFETY: We use safe memory mapping through memmap2, which handles
    // the unsafe operations internally with proper error checking.
    // The memmap2 crate is a vetted dependency that provides safe abstractions
    // over unsafe memory mapping operations.
    #[allow(unsafe_code)]
    unsafe {
        MmapOptions::new().map(file).map_err(|source| { ... })
    }
}

This is the only location in the codebase where unsafe code is permitted via #[allow(unsafe_code)], with explicit documentation of why it's safe.

byteorder - Endianness Handling#

byteorder provides endianness-aware integer reading and is documented as containing no unsafe code. It's used extensively for reading multi-byte integers:

let value = match endian {
    Endianness::Little => LittleEndian::read_u16(bytes),
    Endianness::Big => BigEndian::read_u16(bytes),
    Endianness::Native => NativeEndian::read_u16(bytes),
};

Distinction from Generic Rust Safety#

This policy goes beyond Rust's standard safety guarantees in several ways:

  1. Forbid vs Deny: Standard Rust uses #![deny(unsafe_code)] which can be overridden at lower scopes. This project uses forbid which cannot be overridden.

  2. No Panics in Library Code: While Rust allows panics for unrecoverable errors, this project prohibits panics in library code, requiring all errors to be represented as Result types.

  3. No unwrap()/expect() in Library Code: While these methods are common in Rust code, this project bans them from library code, requiring explicit error handling.

  4. Mandatory Bounds Checking: While Rust provides bounds checking on array access, this project requires using .get() methods which return Option types rather than panicking.

  5. UTF-8 Safe String Operations: Standard Rust allows direct string slicing which can panic. This project mandates strip_prefix()/strip_suffix() which handle UTF-8 boundaries safely.

  6. Dependency Vetting: The project explicitly bans unsafe C-based libraries like openssl in favor of pure-Rust alternatives.

  7. Zero Warnings Policy: The warnings = "deny" setting treats all compiler warnings as errors, ensuring code quality.

Relevant Code Files#

File PathPurposeRelated Aspects
Cargo.tomlWorkspace lint configurationunsafe_code = "forbid" enforcement, zero warnings policy
AGENTS.mdDeveloper documentationMemory safety principles, coding guidelines, code review requirements
deny.tomlDependency policy configurationBan unsafe C-based libraries, version control
.github/workflows/ci.ymlCI pipelineQuality checks with clippy
.github/workflows/security.ymlSecurity workflowcargo deny checks
justfileBuild automationlint-rust command with -D warnings
src/io/mod.rsI/O moduleSafeBufferAccess trait, bounds checking, memmap2 usage
src/evaluator/types.rsType readingBounds-checked buffer reads, byteorder usage, error handling
src/error.rsError typesStructured error hierarchy with thiserror
src/lib.rsLibrary rootUTF-8 safe string operations example
  • Rust Safety Guarantees - Rust's built-in memory safety through ownership and borrowing
  • Cargo Lints - Rust's lint system and configuration
  • Fuzzing - The project includes fuzzing integration for robustness testing
  • Supply Chain Security - cargo-deny for dependency vetting
  • OSSF Best Practices - The project maintains certification compliance
  • Security Assurance - When new attack surface is introduced, security documentation must be updated