Skip to content
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

Setup infrastructure to allow Auxv information to be passed from crashed process. #127

Merged
merged 6 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
22 changes: 10 additions & 12 deletions src/bin/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ pub type Result<T> = std::result::Result<T, Error>;
mod linux {
use super::*;
use minidump_writer::{
minidump_writer::STOP_TIMEOUT,
module_reader,
ptrace_dumper::{PtraceDumper, AT_SYSINFO_EHDR},
minidump_writer::STOP_TIMEOUT, module_reader, ptrace_dumper::PtraceDumper,
LINUX_GATE_LIBRARY_NAME,
};
use nix::{
Expand All @@ -30,13 +28,13 @@ mod linux {

fn test_setup() -> Result<()> {
let ppid = getppid();
PtraceDumper::new(ppid.as_raw(), STOP_TIMEOUT)?;
PtraceDumper::new(ppid.as_raw(), STOP_TIMEOUT, Default::default())?;
Ok(())
}

fn test_thread_list() -> Result<()> {
let ppid = getppid();
let dumper = PtraceDumper::new(ppid.as_raw(), STOP_TIMEOUT)?;
let dumper = PtraceDumper::new(ppid.as_raw(), STOP_TIMEOUT, Default::default())?;
test!(!dumper.threads.is_empty(), "No threads")?;
test!(
dumper
Expand All @@ -52,7 +50,7 @@ mod linux {

fn test_copy_from_process(stack_var: usize, heap_var: usize) -> Result<()> {
let ppid = getppid().as_raw();
let mut dumper = PtraceDumper::new(ppid, STOP_TIMEOUT)?;
let mut dumper = PtraceDumper::new(ppid, STOP_TIMEOUT, Default::default())?;
dumper.suspend_threads()?;
let stack_res = PtraceDumper::copy_from_process(ppid, stack_var as *mut libc::c_void, 1)?;

Expand All @@ -74,7 +72,7 @@ mod linux {

fn test_find_mappings(addr1: usize, addr2: usize) -> Result<()> {
let ppid = getppid();
let dumper = PtraceDumper::new(ppid.as_raw(), STOP_TIMEOUT)?;
let dumper = PtraceDumper::new(ppid.as_raw(), STOP_TIMEOUT, Default::default())?;
dumper
.find_mapping(addr1)
.ok_or("No mapping for addr1 found")?;
Expand All @@ -91,7 +89,7 @@ mod linux {
let ppid = getppid().as_raw();
let exe_link = format!("/proc/{}/exe", ppid);
let exe_name = std::fs::read_link(exe_link)?.into_os_string();
let mut dumper = PtraceDumper::new(ppid, STOP_TIMEOUT)?;
let mut dumper = PtraceDumper::new(ppid, STOP_TIMEOUT, Default::default())?;
dumper.suspend_threads()?;
let mut found_exe = None;
for (idx, mapping) in dumper.mappings.iter().enumerate() {
Expand All @@ -110,7 +108,7 @@ mod linux {

fn test_merged_mappings(path: String, mapped_mem: usize, mem_size: usize) -> Result<()> {
// Now check that PtraceDumper interpreted the mappings properly.
let dumper = PtraceDumper::new(getppid().as_raw(), STOP_TIMEOUT)?;
let dumper = PtraceDumper::new(getppid().as_raw(), STOP_TIMEOUT, Default::default())?;
let mut mapping_count = 0;
for map in &dumper.mappings {
if map
Expand All @@ -132,7 +130,7 @@ mod linux {

fn test_linux_gate_mapping_id() -> Result<()> {
let ppid = getppid().as_raw();
let mut dumper = PtraceDumper::new(ppid, STOP_TIMEOUT)?;
let mut dumper = PtraceDumper::new(ppid, STOP_TIMEOUT, Default::default())?;
let mut found_linux_gate = false;
for mapping in dumper.mappings.clone() {
if mapping.name == Some(LINUX_GATE_LIBRARY_NAME.into()) {
Expand All @@ -152,8 +150,8 @@ mod linux {

fn test_mappings_include_linux_gate() -> Result<()> {
let ppid = getppid().as_raw();
let dumper = PtraceDumper::new(ppid, STOP_TIMEOUT)?;
let linux_gate_loc = dumper.auxv[&AT_SYSINFO_EHDR];
let dumper = PtraceDumper::new(ppid, STOP_TIMEOUT, Default::default())?;
let linux_gate_loc = dumper.auxv.get_linux_gate_address().unwrap();
test!(linux_gate_loc != 0, "linux_gate_loc == 0")?;
let mut found_linux_gate = false;
for mapping in &dumper.mappings {
Expand Down
2 changes: 1 addition & 1 deletion src/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#[cfg(target_os = "android")]
mod android;
pub mod app_memory;
pub(crate) mod auxv_reader;
pub(crate) mod auxv;
pub mod crash_context;
mod dso_debug;
mod dumper_cpu_info;
Expand Down
135 changes: 135 additions & 0 deletions src/linux/auxv/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
pub use reader::ProcfsAuxvIter;
use {
crate::linux::thread_info::Pid,
std::{collections::HashMap, fs::File, io::BufReader},
thiserror::Error,
};

mod reader;

/// The type used in auxv keys and values.
#[cfg(target_pointer_width = "32")]
pub type AuxvType = u32;
/// The type used in auxv keys and values.
#[cfg(target_pointer_width = "64")]
pub type AuxvType = u64;

#[cfg(any(target_arch = "arm", all(target_os = "android", target_arch = "x86")))]
mod consts {
use super::AuxvType;
pub const AT_PHDR: AuxvType = 3;
pub const AT_PHNUM: AuxvType = 5;
pub const AT_ENTRY: AuxvType = 9;
pub const AT_SYSINFO_EHDR: AuxvType = 33;
}
#[cfg(not(any(target_arch = "arm", all(target_os = "android", target_arch = "x86"))))]
mod consts {
use super::AuxvType;
pub const AT_PHDR: AuxvType = libc::AT_PHDR;
pub const AT_PHNUM: AuxvType = libc::AT_PHNUM;
pub const AT_ENTRY: AuxvType = libc::AT_ENTRY;
pub const AT_SYSINFO_EHDR: AuxvType = libc::AT_SYSINFO_EHDR;
}

/// An auxv key-value pair.
#[derive(Debug, PartialEq, Eq)]
pub struct AuxvPair {
pub key: AuxvType,
pub value: AuxvType,
}

#[repr(C)]
#[derive(Clone, Debug)]
pub struct DirectAuxvDumpInfo {
program_header_count: AuxvType,
program_header_address: AuxvType,
linux_gate_address: AuxvType,
entry_address: AuxvType,
}

impl From<DirectAuxvDumpInfo> for AuxvDumpInfo {
fn from(f: DirectAuxvDumpInfo) -> AuxvDumpInfo {
AuxvDumpInfo {
program_header_count: (f.program_header_count > 0).then_some(f.program_header_count),
program_header_address: (f.program_header_address > 0)
.then_some(f.program_header_address),
linux_gate_address: (f.linux_gate_address > 0).then_some(f.linux_gate_address),
entry_address: (f.entry_address > 0).then_some(f.entry_address),
}
}
}

#[derive(Debug, Default)]
pub struct AuxvDumpInfo {
program_header_count: Option<AuxvType>,
program_header_address: Option<AuxvType>,
linux_gate_address: Option<AuxvType>,
entry_address: Option<AuxvType>,
}

impl AuxvDumpInfo {
pub fn try_filling_missing_info(&mut self, pid: Pid) -> Result<(), AuxvError> {
if self.is_complete() {
return Ok(());
}

let auxv_path = format!("/proc/{pid}/auxv");
let auxv_file = File::open(&auxv_path).map_err(|e| AuxvError::OpenError(auxv_path, e))?;
let auxv: HashMap<AuxvType, AuxvType> = ProcfsAuxvIter::new(BufReader::new(auxv_file))
.filter_map(Result::ok)
.map(|x| (x.key, x.value))
.collect();

if auxv.is_empty() {
return Err(AuxvError::NoAuxvEntryFound(pid));
}

if self.program_header_count.is_none() {
self.program_header_count = auxv.get(&consts::AT_PHNUM).copied();
}

if self.program_header_address.is_none() {
self.program_header_address = auxv.get(&consts::AT_PHDR).copied();
}

if self.linux_gate_address.is_none() {
self.linux_gate_address = auxv.get(&consts::AT_SYSINFO_EHDR).copied();
}

if self.entry_address.is_none() {
self.entry_address = auxv.get(&consts::AT_ENTRY).copied();
}

Jake-Shadle marked this conversation as resolved.
Show resolved Hide resolved
Ok(())
}
pub fn get_program_header_count(&self) -> Option<AuxvType> {
self.program_header_count
}
pub fn get_program_header_address(&self) -> Option<AuxvType> {
self.program_header_address
}
pub fn get_linux_gate_address(&self) -> Option<AuxvType> {
self.linux_gate_address
}
pub fn get_entry_address(&self) -> Option<AuxvType> {
self.entry_address
}
pub fn is_complete(&self) -> bool {
self.program_header_count.is_some()
&& self.program_header_address.is_some()
&& self.linux_gate_address.is_some()
&& self.entry_address.is_some()
}
}

#[derive(Debug, Error)]
pub enum AuxvError {
#[error("Failed to open file {0}")]
OpenError(String, #[source] std::io::Error),
#[error("No auxv entry found for PID {0}")]
NoAuxvEntryFound(Pid),
#[error("Invalid auxv format (should not hit EOF before AT_NULL)")]
InvalidFormat,
#[error("IO Error")]
IOError(#[from] std::io::Error),
}
31 changes: 10 additions & 21 deletions src/linux/auxv_reader.rs → src/linux/auxv/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,15 @@
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be in…substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
use crate::errors::AuxvReaderError;
use byteorder::{NativeEndian, ReadBytesExt};
use std::fs::File;
use std::io::{BufReader, Read};

pub type Result<T> = std::result::Result<T, AuxvReaderError>;

/// The type used in auxv keys and values.
#[cfg(target_pointer_width = "32")]
pub type AuxvType = u32;
/// The type used in auxv keys and values.
#[cfg(target_pointer_width = "64")]
pub type AuxvType = u64;

/// An auxv key-value pair.
#[derive(Debug, PartialEq, Eq)]
pub struct AuxvPair {
pub key: AuxvType,
pub value: AuxvType,
}
use {
super::{AuxvError, AuxvPair, AuxvType},
byteorder::{NativeEndian, ReadBytesExt},
std::{
fs::File,
io::{BufReader, Read},
},
};

/// An iterator across auxv pairs from procfs.
pub struct ProcfsAuxvIter {
Expand All @@ -48,7 +37,7 @@ impl ProcfsAuxvIter {
}

impl Iterator for ProcfsAuxvIter {
type Item = Result<AuxvPair>;
type Item = Result<AuxvPair, AuxvError>;
fn next(&mut self) -> Option<Self::Item> {
if !self.keep_going {
return None;
Expand All @@ -65,7 +54,7 @@ impl Iterator for ProcfsAuxvIter {
Ok(n) => {
if n == 0 {
// should not hit EOF before AT_NULL
return Some(Err(AuxvReaderError::InvalidFormat));
return Some(Err(AuxvError::InvalidFormat));
}

read_bytes += n;
Expand Down
28 changes: 7 additions & 21 deletions src/linux/dso_debug.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use crate::{
linux::{auxv_reader::AuxvType, errors::SectionDsoDebugError, ptrace_dumper::PtraceDumper},
linux::{auxv::AuxvDumpInfo, errors::SectionDsoDebugError, ptrace_dumper::PtraceDumper},
mem_writer::{write_string_to_location, Buffer, MemoryArrayWriter, MemoryWriter},
minidump_format::*,
};
use std::collections::HashMap;

type Result<T> = std::result::Result<T, SectionDsoDebugError>;

Expand Down Expand Up @@ -77,26 +76,13 @@ pub struct RDebug {
pub fn write_dso_debug_stream(
buffer: &mut Buffer,
blamed_thread: i32,
auxv: &HashMap<AuxvType, AuxvType>,
auxv: &AuxvDumpInfo,
) -> Result<MDRawDirectory> {
let at_phnum;
let at_phdr;
#[cfg(any(target_arch = "arm", all(target_os = "android", target_arch = "x86")))]
{
at_phdr = 3;
at_phnum = 5;
}
#[cfg(not(any(target_arch = "arm", all(target_os = "android", target_arch = "x86"))))]
{
at_phdr = libc::AT_PHDR;
at_phnum = libc::AT_PHNUM;
}
let phnum_max = *auxv
.get(&at_phnum)
.ok_or(SectionDsoDebugError::CouldNotFind("AT_PHNUM in auxv"))?
as usize;
let phdr = *auxv
.get(&at_phdr)
let phnum_max =
auxv.get_program_header_count()
.ok_or(SectionDsoDebugError::CouldNotFind("AT_PHNUM in auxv"))? as usize;
let phdr = auxv
.get_program_header_address()
.ok_or(SectionDsoDebugError::CouldNotFind("AT_PHDR in auxv"))? as usize;

let ph = PtraceDumper::copy_from_process(
Expand Down
13 changes: 3 additions & 10 deletions src/linux/errors.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::auxv::AuxvError;
use crate::dir_section::FileWriterError;
use crate::maps_reader::MappingInfo;
use crate::mem_writer::MemoryWriterError;
Expand All @@ -9,10 +10,10 @@ use thiserror::Error;

#[derive(Debug, Error)]
pub enum InitError {
#[error("failed to read auxv")]
ReadAuxvFailed(AuxvError),
#[error("IO error for file {0}")]
IOError(String, #[source] std::io::Error),
#[error("No auxv entry found for PID {0}")]
NoAuxvEntryFound(Pid),
#[error("crash thread does not reference principal mapping")]
PrincipalMappingNotReferenced,
#[error("Failed Android specific late init")]
Expand Down Expand Up @@ -47,14 +48,6 @@ pub enum MapsReaderError {
SymlinkError(std::path::PathBuf, std::path::PathBuf),
}

#[derive(Debug, Error)]
pub enum AuxvReaderError {
#[error("Invalid auxv format (should not hit EOF before AT_NULL)")]
InvalidFormat,
#[error("IO Error")]
IOError(#[from] std::io::Error),
}

#[derive(Debug, Error)]
pub enum CpuInfoError {
#[error("IO error for file /proc/cpuinfo")]
Expand Down
2 changes: 1 addition & 1 deletion src/linux/maps_reader.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::auxv_reader::AuxvType;
use crate::auxv::AuxvType;
use crate::errors::MapsReaderError;
use byteorder::{NativeEndian, ReadBytesExt};
use goblin::elf;
Expand Down
Loading
Loading