8000 Isolated Processes 4: Redirect maps_reader and module_reader by marti4d · Pull Request #181 · rust-minidump/minidump-writer · GitHub
[go: up one dir, main page]

Skip to content
8000
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
15 changes: 6 additions & 9 deletions src/bin/test.rs
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ mod linux {
minidump_writer::{
LINUX_GATE_LIBRARY_NAME,
minidump_writer::{MinidumpWriter, MinidumpWriterConfig},
module_reader,
},
nix::{
sys::mman::{MapFlags, ProtFlags, mmap_anonymous},
Expand Down Expand Up @@ -191,7 +190,7 @@ mod linux {
}
}
let idx = found_exe.unwrap();
let module_reader::BuildId(id) = dumper.from_process_memory_for_index(idx)?;
let id = dumper.build_id_from_process_memory_for_index(idx)?;

drop(dumper);

Expand Down Expand Up @@ -228,19 +227,17 @@ mod linux {

fn test_linux_gate_mapping_id() -> Result<()> {
let ppid = getppid().as_raw();
let dumper = fail_on_soft_error!(
let mut dumper = fail_on_soft_error!(
soft_errors,
MinidumpWriterConfig::new(ppid, ppid).build_for_testing(&mut soft_errors)?
);
let mut found_linux_gate = false;
for mapping in dumper.mappings.clone() {
if mapping.name == Some(LINUX_GATE_LIBRARY_NAME.into()) {
for idx in 0..dumper.mappings.len() {
if dumper.mappings[idx].name == Some(LINUX_GATE_LIBRARY_NAME.into()) {
found_linux_gate = true;

let module_reader::BuildId(id) = MinidumpWriter::from_process_memory_for_mapping(
&dumper.process_inspector,
&mapping,
)?;
let id = dumper.build_id_from_process_memory_for_index(idx)?;

test!(!id.is_empty(), "id-vec is empty");
test!(id.iter().any(|&x| x > 0), "all id elements are 0");
drop(dumper);
Expand Down
76 changes: 24 additions & 52 deletions src/linux/maps_reader.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
use {
super::{auxv::AuxvType, module_reader::ModuleReaderError, serializers::*},
super::{
auxv::AuxvType, module_reader::ModuleReaderError, process_inspection::ProcessInspector,
serializers::*,
},
crate::serializers::*,
byteorder::{NativeEndian, ReadBytesExt},
goblin::elf,
memmap2::{Mmap, MmapOptions},
procfs_core::{
FromRead,
process::{MMPermissions, MMapPath, MemoryMaps},
},
std::{
ffi::{OsStr, OsString},
fs::File,
mem::size_of,
os::unix::ffi::{OsStrExt, OsStringExt},
path::PathBuf,
path::{Path, PathBuf},
},
};

Expand Down Expand Up @@ -67,6 +67,10 @@ pub enum MapsReaderError {
#[serde(serialize_with = "serialize_goblin_error")]
goblin::error::Error,
),
#[error("error reading soname from file")]
ReadSoNameFromFileFailed(#[source] ModuleReaderError),
#[error("failed to memory map file")]
MemoryMapFileFailed(#[source] ModuleReaderError),
#[error("No soname found (filename: {})", .0.to_string_lossy())]
NoSoName(OsString, #[source] ModuleReaderError),

Expand Down Expand Up @@ -129,9 +133,13 @@ fn sanitize_path(pathname: OsString) -> OsString {

impl MappingInfo {
/// Get the mappings for the given process.
pub fn for_pid(pid: i32, linux_gate_loc: Option<AuxvType>) -> Result<Vec<Self>> {
pub fn for_pid(
process_inspector: &ProcessInspector,
pid: i32,
linux_gate_loc: Option<AuxvType>,
) -> Result<Vec<Self>> {
let maps_path = format!("/proc/{}/maps", pid);
let maps_file = File::open(&maps_path).map_err(|e| {
let maps_file = process_inspector.read_file(&maps_path).map_err(|e| {
if e.kind() == std::io::ErrorKind::NotFound {
MapsReaderError::MappingFileMissing { pid }
} else {
Expand Down Expand Up @@ -257,28 +265,6 @@ impl MappingInfo {
Ok(infos)
}

pub fn get_mmap(name: &Option<OsString>, offset: usize) -> Result<Mmap> {
if !MappingInfo::is_mapped_file_safe_to_open(name) {
return Err(MapsReaderError::NotSafeToOpenMapping(
name.clone().unwrap_or_default(),
));
}

// Not doing this as root_prefix is always "" at the moment
// if (!dumper.GetMappingAbsolutePath(mapping, filename))
let filename = name.clone().unwrap_or_default();
let mapped_file = unsafe {
MmapOptions::new()
.offset(offset.try_into()?) // try_into() to work for both 32 and 64 bit
.map(&File::open(filename)?)?
};

if mapped_file.is_empty() || mapped_file.len() < elf::header::SELFMAG {
return Err(MapsReaderError::MmapSanityCheckFailed);
}
Ok(mapped_file)
}

pub fn stack_has_pointer_to_mapping(&self, stack_copy: &[u8], sp_offset: usize) -> bool {
// Loop over all stack words that would have been on the stack in
// the target process (i.e. are word aligned, and at addresses >=
Expand Down Expand Up @@ -312,29 +298,12 @@ impl MappingInfo {
false
}

pub fn is_mapped_file_safe_to_open(name: &Option<OsString>) -> bool {
// It is unsafe to attempt to open a mapped file that lives under /dev,
// because the semantics of the open may be driver-specific so we'd risk
// hanging the crash dumper. And a file in /dev/ almost certainly has no
// ELF file identifier anyways.
if let Some(name) = name
&& name.as_bytes().starts_with(b"/dev/")
{
return false;
}
true
}

/// Find the shared object name (SONAME) by examining the ELF information
/// for the mapping.
fn so_name(&self) -> Result<String> {
use super::module_reader::{ReadFromModule, SoName};

let mapped_file = MappingInfo::get_mmap(&self.name, self.offset)?;
Ok(SoName::read_from_module((&*mapped_file).into())
.map_err(|e| MapsReaderError::NoSoName(self.name.clone().unwrap_or_default(), e))?
.0
.to_string())
fn so_name(&self, process_inspector: &ProcessInspector) -> Result<String> {
let path = Path::new(self.name.as_deref().unwrap_or_default());
super::module_reader::read_soname_from_file(process_inspector, path, self.offset)
.map_err(MapsReaderError::ReadSoNameFromFileFailed)
}

#[inline]
Expand All @@ -344,6 +313,7 @@ impl MappingInfo {

pub fn get_mapping_effective_path_name_and_version(
&self,
process_inspector: &ProcessInspector,
soname: Option<String>,
) -> Result<(PathBuf, String, Option<SoVersion>)> {
let mut file_path = PathBuf::from(self.name.clone().unwrap_or_default());
Expand All @@ -354,7 +324,7 @@ impl MappingInfo {
// filesystem name of the module.

// Just use the filesystem name if no SONAME is present.
let Some(file_name) = soname.or_else(|| self.so_name().ok()) else {
let Some(file_name) = soname.or_else(|| self.so_name(process_inspector).ok()) else {
// file_path := /path/to/libname.so
// file_name := libname.so
let file_name = file_path
Expand Down Expand Up @@ -760,8 +730,10 @@ a4840000-a4873000 rw-p 09021000 08:12 393449 /data/app/org.mozilla.firefox-1
);
assert_eq!(mappings.len(), 1);

let process_inspector = ProcessInspector::local(0);

let (file_path, file_name, _version) = mappings[0]
.get_mapping_effective_path_name_and_version(None)
.get_mapping_effective_path_name_and_version(&process_inspector, None)
.expect("Couldn't get effective name for mapping");
assert_eq!(file_name, "libmozgtk.so");
assert_eq!(
Expand Down
42 changes: 23 additions & 19 deletions src/linux/minidump_writer/mappings.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
use super::{
super::{
maps_reader::MappingInfo,
module_reader::{BuildId, ReadFromModule, SoName},
},
*,
};
use super::{super::maps_reader::MappingInfo, *};

#[derive(Debug, Error, serde::Serialize)]
pub enum SectionMappingsError {
Expand Down Expand Up @@ -37,8 +31,8 @@ impl MinidumpWriter {
continue;
}
log::debug!("retrieving build id for {:?}", &self.mappings[map_idx]);
let BuildId(identifier) = self
.from_process_memory_for_index(map_idx)
let identifier = self
.build_id_from_process_memory_for_index(map_idx)
.or_else(|e| {
// If the mapping has an associated name that is a file, try to read the build id
// from the file. If there is no note segment with the build id in
Expand All @@ -48,7 +42,7 @@ impl MinidumpWriter {
let path = std::path::Path::new(&path);
if path.exists() {
log::debug!("failed to get build id from process memory ({e}), attempting to retrieve from {}", path.display());
return BuildId::read_from_file(path)
return module_reader::read_build_id_from_file(&self.process_inspector, path)
.map_err(errors::WriterError::ModuleReaderError);
}
log::debug!(
Expand All @@ -60,7 +54,7 @@ impl MinidumpWriter {
})
.unwrap_or_else(|e| {
log::warn!("failed to get build id for mapping: {e}");
BuildId(Vec::new())
Vec::new()
});

// If the identifier is all 0, its an uninteresting mapping (bmc#1676109)
Expand All @@ -70,19 +64,28 @@ impl MinidumpWriter {

// SONAME should always be accessible through program headers alone, so we don't really
// need to fall back to trying to read from the mapping file.
let soname = self
.from_process_memory_for_index(map_idx)
.ok()
.map(|SoName(n)| n);

let module = fill_raw_module(buffer, &self.mappings[map_idx], &identifier, soname)?;
let soname = self.soname_from_process_memory_for_index(map_idx).ok();

let module = fill_raw_module(
&self.process_inspector,
buffer,
&self.mappings[map_idx],
&identifier,
soname,
)?;
modules.push(module);
}

// Next write all the mappings provided by the caller
for user in &self.user_mapping_list {
// GUID was provided by caller.
let module = fill_raw_module(buffer, &user.mapping, &user.identifier, None)?;
let module = fill_raw_module(
&self.process_inspector,
buffer,
&user.mapping,
&user.identifier,
None,
)?;
modules.push(module);
}

Expand All @@ -102,6 +105,7 @@ impl MinidumpWriter {
}
}
fn fill_raw_module(
process_inspector: &ProcessInspector,
buffer: &mut DumpBuf,
mapping: &MappingInfo,
identifier: &[u8],
Expand All @@ -127,7 +131,7 @@ fn fill_raw_module(
};

let (file_path, _, so_version) = mapping
.get_mapping_effective_path_name_and_version(soname)
.get_mapping_effective_path_name_and_version(process_inspector, soname)
.map_err(|e| SectionMappingsError::GetEffectivePathError(mapping.clone(), e))?;
let name_header = write_string_to_location(buffer, file_path.to_string_lossy().as_ref())?;

Expand Down
39 changes: 24 additions & 15 deletions src/linux/minidump_writer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -733,8 +733,12 @@ impl MinidumpWriter {
// case its entry when creating the list of mappings.
// See http://www.trilithium.com/johan/2005/08/linux-gate/ for more
// information.
self.mappings = MappingInfo::for_pid(self.process_id, self.auxv.get_linux_gate_address())
.map_err(InitError::AggregateMappingsFailed)?;
self.mappings = MappingInfo::for_pid(
&self.process_inspector,
self.process_id,
self.auxv.get_linux_gate_address(),
)
.map_err(InitError::AggregateMappingsFailed)?;

// Although the initial executable is usually the first mapping, it's not
// guaranteed (see http://crosbug.com/25355); therefore, try to use the
Expand Down Expand Up @@ -945,23 +949,28 @@ impl MinidumpWriter {
})
}

pub fn from_process_memory_for_index<T: module_reader::ReadFromModule>(
pub fn build_id_from_process_memory_for_index(
&mut self,
idx: usize,
) -> Result<T, WriterError> {
assert!(idx < self.mappings.len());

Self::from_process_memory_for_mapping(&self.process_inspector, &self.mappings[idx])
) -> Result<Vec<u8>, WriterError> {
let reader = self.process_inspector.process_reader();
module_reader::read_build_id_from_module(module_reader::ProcessModuleMemoryReader::new(
reader,
self.mappings[idx].start_address,
))
.map_err(WriterError::ModuleReaderError)
}

pub fn from_process_memory_for_mapping<T: module_reader::ReadFromModule>(
process_inspector: &ProcessInspector,
mapping: &MappingInfo,
) -> Result<T, WriterError> {
let reader = process_inspector.process_reader();
Ok(T::read_from_module(
module_reader::ModuleMemory::from_process(reader, mapping.start_address),
)?)
pub fn soname_from_process_memory_for_index(
&mut self,
idx: usize,
) -> Result<String, WriterError> {
let reader = self.process_inspector.process_reader();
module_reader::read_soname_from_module(module_reader::ProcessModuleMemoryReader::new(
reader,
self.mappings[idx].start_address,
))
.map_err(WriterError::ModuleReaderError)
}

/// Copies a block of bytes from the target process, returning the heap
Expand Down
Loading
Loading
0