Skip to content

Separate addr2line code from asan module #2980

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 13, 2025
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 7 additions & 190 deletions libafl_qemu/src/modules/usermode/asan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,19 @@ use libc::{
};
use meminterval::{Interval, IntervalTree};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use object::{Object, ObjectSection};
use object::Object;
use rangemap::RangeMap;

use crate::{
emu::EmulatorModules,
modules::{
calls::FullBacktraceCollector,
snapshot::SnapshotModule,
utils::filters::{HasAddressFilter, StdAddressFilter},
utils::{
addr2line_legacy,
filters::{HasAddressFilter, StdAddressFilter},
load_file_section,
},
AddressFilter, EmulatorModule, EmulatorModuleTuple,
},
qemu::{Hook, MemAccessInfo, QemuHooks, SyscallHookResult},
Expand Down Expand Up @@ -1341,194 +1345,6 @@ where
}
}

fn load_file_section<'input, 'arena, Endian: addr2line::gimli::Endianity>(
id: addr2line::gimli::SectionId,
file: &object::File<'input>,
endian: Endian,
arena_data: &'arena typed_arena::Arena<Cow<'input, [u8]>>,
) -> Result<addr2line::gimli::EndianSlice<'arena, Endian>, object::Error> {
// TODO: Unify with dwarfdump.rs in gimli.
let name = id.name();
match file.section_by_name(name) {
Some(section) => match section.uncompressed_data()? {
Cow::Borrowed(b) => Ok(addr2line::gimli::EndianSlice::new(b, endian)),
Cow::Owned(b) => Ok(addr2line::gimli::EndianSlice::new(
arena_data.alloc(b.into()),
endian,
)),
},
None => Ok(addr2line::gimli::EndianSlice::new(&[][..], endian)),
}
}

/// Taken from `addr2line` [v0.22](https://github.com/gimli-rs/addr2line/blob/5c3c83f74f992220b2d9a17b3ac498a89214bf92/src/builtin_split_dwarf_loader.rs)
/// has been removed in version v0.23 for some reason.
/// TODO: find another cleaner solution.
mod addr2line_legacy {
use std::{borrow::Cow, env, ffi::OsString, fs::File, path::PathBuf, sync::Arc};

use addr2line::{gimli, LookupContinuation, LookupResult};
use object::Object;

#[cfg(unix)]
fn convert_path<R: gimli::Reader<Endian = gimli::RunTimeEndian>>(
r: &R,
) -> Result<PathBuf, gimli::Error> {
use std::{ffi::OsStr, os::unix::ffi::OsStrExt};
let bytes = r.to_slice()?;
let s = OsStr::from_bytes(&bytes);
Ok(PathBuf::from(s))
}

#[cfg(not(unix))]
fn convert_path<R: gimli::Reader<Endian = gimli::RunTimeEndian>>(
r: &R,
) -> Result<PathBuf, gimli::Error> {
let bytes = r.to_slice()?;
let s = str::from_utf8(&bytes).map_err(|_| gimli::Error::BadUtf8)?;
Ok(PathBuf::from(s))
}

fn load_section<'data, O, R, F>(
id: gimli::SectionId,
file: &O,
endian: R::Endian,
loader: &mut F,
) -> R
where
O: Object<'data>,
R: gimli::Reader<Endian = gimli::RunTimeEndian>,
F: FnMut(Cow<'data, [u8]>, R::Endian) -> R,
{
use object::ObjectSection;

let data = id
.dwo_name()
.and_then(|dwo_name| {
file.section_by_name(dwo_name)
.and_then(|section| section.uncompressed_data().ok())
})
.unwrap_or(Cow::Borrowed(&[]));
loader(data, endian)
}

/// A simple builtin split DWARF loader.
pub struct SplitDwarfLoader<R, F>
where
R: gimli::Reader<Endian = gimli::RunTimeEndian>,
F: FnMut(Cow<'_, [u8]>, R::Endian) -> R,
{
loader: F,
dwarf_package: Option<gimli::DwarfPackage<R>>,
}

impl<R, F> SplitDwarfLoader<R, F>
where
R: gimli::Reader<Endian = gimli::RunTimeEndian>,
F: FnMut(Cow<'_, [u8]>, R::Endian) -> R,
{
fn load_dwarf_package(
loader: &mut F,
path: Option<PathBuf>,
) -> Option<gimli::DwarfPackage<R>> {
let mut path = path.map_or_else(env::current_exe, Ok).ok()?;
let dwp_extension = path.extension().map_or_else(
|| OsString::from("dwp"),
|previous_extension| {
let mut previous_extension = previous_extension.to_os_string();
previous_extension.push(".dwp");
previous_extension
},
);
path.set_extension(dwp_extension);
let file = File::open(&path).ok()?;
let map = unsafe { memmap2::Mmap::map(&file).ok()? };
let dwp = object::File::parse(&*map).ok()?;

let endian = if dwp.is_little_endian() {
gimli::RunTimeEndian::Little
} else {
gimli::RunTimeEndian::Big
};

let empty = loader(Cow::Borrowed(&[]), endian);
gimli::DwarfPackage::load::<_, gimli::Error>(
|section_id| Ok(load_section(section_id, &dwp, endian, loader)),
empty,
)
.ok()
}

/// Create a new split DWARF loader.
pub fn new(mut loader: F, path: Option<PathBuf>) -> SplitDwarfLoader<R, F> {
let dwarf_package = SplitDwarfLoader::load_dwarf_package(&mut loader, path);
SplitDwarfLoader {
loader,
dwarf_package,
}
}

/// Run the provided `LookupResult` to completion, loading any necessary
/// split DWARF along the way.
pub fn run<L>(&mut self, mut l: LookupResult<L>) -> L::Output
where
L: LookupContinuation<Buf = R>,
{
loop {
let (load, continuation) = match l {
LookupResult::Output(output) => break output,
LookupResult::Load { load, continuation } => (load, continuation),
};

let mut r: Option<Arc<gimli::Dwarf<_>>> = None;
if let Some(dwp) = self.dwarf_package.as_ref() {
if let Ok(Some(cu)) = dwp.find_cu(load.dwo_id, &load.parent) {
r = Some(Arc::new(cu));
}
}

if r.is_none() {
let mut path = PathBuf::new();
if let Some(p) = load.comp_dir.as_ref() {
if let Ok(p) = convert_path(p) {
path.push(p);
}
}

if let Some(p) = load.path.as_ref() {
if let Ok(p) = convert_path(p) {
path.push(p);
}
}

if let Ok(file) = File::open(&path) {
if let Ok(map) = unsafe { memmap2::Mmap::map(&file) } {
if let Ok(file) = object::File::parse(&*map) {
let endian = if file.is_little_endian() {
gimli::RunTimeEndian::Little
} else {
gimli::RunTimeEndian::Big
};

r = gimli::Dwarf::load::<_, gimli::Error>(|id| {
Ok(load_section(id, &file, endian, &mut self.loader))
})
.ok()
.map(|mut dwo_dwarf| {
dwo_dwarf.make_dwo(&load.parent);
Arc::new(dwo_dwarf)
});
}
}
}
}

l = continuation.resume(r);
}
}
}
}

/// # Safety
/// Will access the global [`FullBacktraceCollector`].
/// Calling this function concurrently might be racey.
Expand Down Expand Up @@ -1651,6 +1467,7 @@ pub unsafe fn asan_report(rt: &AsanGiovese, qemu: Qemu, pc: GuestAddr, err: &Asa
info
};

// TODO, make a class Resolver for resolving the addresses??
eprintln!("=================================================================");
let backtrace = FullBacktraceCollector::backtrace()
.map(|r| {
Expand Down
Loading
Loading