macho-core 0.5.6

Byte-safe structural Mach-O parsing and validation
Documentation
use macho_core::format::{ParseMode, ParseOptions};
use macho_core::{ContextFrame, ParseErrorKind};

#[test]
fn forensic_mode_retains_safe_validation_diagnostics() {
    let warning = macho_test_support::warning_bearing_image();
    let strict = macho_core::parse(&warning).expect("warnings do not reject strict parsing");
    assert!(strict.first_macho().is_some());
    let outcome = macho_core::parse_with_options(
        &warning,
        &ParseOptions {
            mode: ParseMode::Forensic,
            ..ParseOptions::default()
        },
    )
    .expect("forensic warning image");
    assert!(
        outcome
            .diagnostics
            .iter()
            .any(|diagnostic| { diagnostic.code.0 == "parse.validation.protection_mismatch" })
    );

    let recoverable_error = macho_test_support::validation_error_image();
    let strict_error = macho_core::parse(&recoverable_error).expect_err("strict validation fails");
    assert_eq!(strict_error.kind, ParseErrorKind::Validation);
    let forensic = macho_core::parse_with_options(
        &recoverable_error,
        &ParseOptions {
            mode: ParseMode::Forensic,
            ..ParseOptions::default()
        },
    )
    .expect("forensic mode retains representable model");
    assert!(
        forensic
            .diagnostics
            .iter()
            .any(|diagnostic| { diagnostic.code.0 == "parse.validation.segment_bounds" })
    );
}

#[test]
fn hard_bounds_fail_in_both_modes_with_command_context() {
    let bytes = macho_test_support::truncated_load_command_image();
    for mode in [ParseMode::Strict, ParseMode::Forensic] {
        let error = match macho_core::parse_with_options(
            &bytes,
            &ParseOptions {
                mode,
                ..ParseOptions::default()
            },
        ) {
            Ok(_) => panic!("truncated command is always a hard failure"),
            Err(error) => error,
        };
        assert_eq!(error.kind, ParseErrorKind::InvalidLoadCommand);
        assert_eq!(error.code(), "parse.load_command.invalid");
        assert_eq!(error.location.expect("command span").offset, 32);
        assert!(matches!(
            error.context.as_slice(),
            [ContextFrame::LoadCommand { index: 0 }]
        ));
    }
}

#[test]
fn nested_fat_error_preserves_slice_command_and_span() {
    let thin = macho_test_support::truncated_load_command_image();
    let fat = macho_test_support::fat32(&[(macho_test_support::CPU_TYPE_X86_64, 3, thin)]);
    let error = macho_core::parse(&fat).expect_err("nested command is malformed");
    assert_eq!(error.code(), "parse.load_command.invalid");
    assert_eq!(error.location.expect("nested command span").offset, 32);
    assert!(matches!(
        error.context.as_slice(),
        [
            ContextFrame::LoadCommand { index: 0 },
            ContextFrame::FatArchitecture { index: 0 }
        ]
    ));
}

#[test]
fn unknown_load_command_is_bounded_and_preserved() {
    let bytes = macho_test_support::unknown_load_command_image();
    let container = macho_core::parse(&bytes).expect("unknown command is structurally valid");
    let macho = container.first_macho().expect("one image");
    assert!(matches!(
        macho.load_commands()[0].kind(),
        macho_core::model::load_command::LoadCommand::Unknown(command)
            if command.cmd == 0x1234_5678
    ));
}