Skip to content

resolve passes symbols which can be invalidated #753

@Zoxc

Description

@Zoxc

resolve in src/symbolize/gimli.rs passes cb a Symbol built from name/location references into the current cached Mapping, and then continues iterating the same DWARF state after the callback returns. Because Cache::with_global is reentrant on the same thread, a callback can call clear_symbol_cache() or recursively call resolve() and clear/evict that Mapping while the outer call is still active, leaving both the in-callback &Symbol and the outer frames iterator with dangling references to freed debug data (use-after-free UB).

This can be reproduced by:

fn clear_symbol_cache_during_resolve_callback() {
    #[inline(never)]
    fn target() -> *mut c_void {
        let mut frames = Vec::new();
        backtrace::trace(|frame| {
            frames.push(frame.ip());
            frames.len() < 32
        });

        frames
            .into_iter()
            .find(|ip| {
                let mut found = false;
                backtrace::resolve(*ip, |_| found = true);
                found
            })
            .expect("expected to capture a resolvable frame")
    }

    let ip = target();
    let mut saw_symbol = false;
    backtrace::resolve(ip, |symbol| {
        saw_symbol = true;

        // This reproduces the gimli cache lifetime bug by evicting the mapping
        // that backs the borrowed name/location data passed to the callback,
        // then touching the borrowed symbol again.
        backtrace::clear_symbol_cache();

        let _ = symbol.filename().map(|path| path.as_os_str().len());
        let _ = symbol.name().map(|name| name.to_string());
    });

    assert!(saw_symbol, "expected to resolve at least one symbol");
}
cargo test --test smoke clear_symbol_cache_during_resolve_callback --no-run
valgrind --tool=memcheck target/debug/deps/smoke-b6b34c31904af23c clear_symbol_cache_during_resolve_callback
==26174== Invalid read of size 1
==26174==    at 0x41E172C: run_utf8_validation (validations.rs:0)
==26174==    by 0x41E172C: core::str::converts::from_utf8 (converts.rs:91)
==26174==    by 0x4135AC1: <backtrace::symbolize::SymbolName>::new (mod.rs:310)
==26174==    by 0x411BBCD: <backtrace::symbolize::gimli::Symbol>::name (gimli.rs:521)
==26174==    by 0x4135B95: <backtrace::symbolize::Symbol>::name (mod.rs:208)
==26174==    by 0x407EDBA: smoke::clear_symbol_cache_during_resolve_callback::{closure#0} (smoke.rs:272)
==26174==    by 0x41540DA: backtrace::symbolize::gimli::resolve::{closure#0} (gimli.rs:449)
==26174==    by 0x4154464: backtrace::symbolize::gimli::resolve::{closure#1} (gimli.rs:473)
==26174==    by 0x4153AEF: <backtrace::symbolize::gimli::Cache>::with_global::<backtrace::symbolize::gimli::resolve::{closure#1}> (gimli.rs:379)
==26174==    by 0x411CD31: backtrace::symbolize::gimli::resolve (gimli.rs:453)
==26174==    by 0x408203C: backtrace::symbolize::resolve_unsynchronized::<smoke::clear_symbol_cache_during_resolve_callback::{closure#0}> (mod.rs:162)
==26174==    by 0x40823A2: backtrace::symbolize::resolve::<smoke::clear_symbol_cache_during_resolve_callback::{closure#0}> (mod.rs:63)
==26174==    by 0x407BE3F: smoke::clear_symbol_cache_during_resolve_callback (smoke.rs:263)
==26174==  Address 0x5a35ee6 is not stack'd, malloc'd or (recently) free'd

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions