From f46d32cd0916cd7780562ce9d3cdca6c16c80132 Mon Sep 17 00:00:00 2001
From: Gabriele Svelto
Date: Mon, 5 Jun 2023 11:08:50 +0200
Subject: [PATCH 1/2] Stabilize ARM and AArch64 support on both Linux and
Android, add support for x86-64 on Android
This *does not* add support for i686 on Android as it would require
significant work for almost no benefit. Android emulators usually use
x86-64 and only very old versions run on i686.
---
src/linux/ptrace_dumper.rs | 4 +-
src/linux/thread_info.rs | 18 +++----
src/linux/thread_info/aarch64.rs | 84 +++++++++++++-------------------
src/linux/thread_info/arm.rs | 68 ++++++++------------------
src/linux/thread_info/x86.rs | 31 +++++++-----
tests/linux_minidump_writer.rs | 10 ++--
6 files changed, 91 insertions(+), 124 deletions(-)
diff --git a/src/linux/ptrace_dumper.rs b/src/linux/ptrace_dumper.rs
index d4bb1466..93c175a1 100644
--- a/src/linux/ptrace_dumper.rs
+++ b/src/linux/ptrace_dumper.rs
@@ -1,5 +1,7 @@
#[cfg(target_os = "android")]
use crate::linux::android::late_process_mappings;
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+use crate::thread_info;
use crate::{
linux::{
auxv_reader::{AuxvType, ProcfsAuxvIter},
@@ -147,7 +149,7 @@ impl PtraceDumper {
// We thus test the stack pointer and exclude any threads that are part of
// the seccomp sandbox's trusted code.
let skip_thread;
- let regs = ptrace::getregs(pid);
+ let regs = thread_info::ThreadInfo::getregs(pid.into());
if let Ok(regs) = regs {
#[cfg(target_arch = "x86_64")]
{
diff --git a/src/linux/thread_info.rs b/src/linux/thread_info.rs
index 2b653438..5bb1f9e8 100644
--- a/src/linux/thread_info.rs
+++ b/src/linux/thread_info.rs
@@ -34,6 +34,7 @@ enum NT_Elf {
//NT_PRPSINFO = 3,
//NT_TASKSTRUCT = 4,
//NT_AUXV = 6,
+ NT_ARM_VFP = 0x400, // ARM VFP/NEON registers
}
#[inline]
@@ -96,14 +97,14 @@ trait CommonThreadInfo {
/// and therefore use the data field to return values. This function handles these
/// requests.
fn ptrace_get_data(
- request: ptrace::Request,
+ request: ptrace::RequestType,
flag: Option,
pid: nix::unistd::Pid,
) -> Result {
let mut data = std::mem::MaybeUninit::uninit();
let res = unsafe {
libc::ptrace(
- request as ptrace::RequestType,
+ request,
libc::pid_t::from(pid),
flag.unwrap_or(NT_Elf::NT_NONE),
data.as_mut_ptr(),
@@ -119,7 +120,7 @@ trait CommonThreadInfo {
/// and therefore use the data field to return values. This function handles these
/// requests.
fn ptrace_get_data_via_io(
- request: ptrace::Request,
+ request: ptrace::RequestType,
flag: Option,
pid: nix::unistd::Pid,
) -> Result {
@@ -130,7 +131,7 @@ trait CommonThreadInfo {
};
let res = unsafe {
libc::ptrace(
- request as ptrace::RequestType,
+ request,
libc::pid_t::from(pid),
flag.unwrap_or(NT_Elf::NT_NONE),
&io as *const _,
@@ -142,19 +143,14 @@ trait CommonThreadInfo {
/// COPY FROM CRATE nix BECAUSE ITS NOT PUBLIC
fn ptrace_peek(
- request: ptrace::Request,
+ request: ptrace::RequestType,
pid: unistd::Pid,
addr: ptrace::AddressType,
data: *mut libc::c_void,
) -> nix::Result {
let ret = unsafe {
Errno::clear();
- libc::ptrace(
- request as ptrace::RequestType,
- libc::pid_t::from(pid),
- addr,
- data,
- )
+ libc::ptrace(request, libc::pid_t::from(pid), addr, data)
};
match Errno::result(ret) {
Ok(..) | Err(Errno::UnknownErrno) => Ok(ret),
diff --git a/src/linux/thread_info/aarch64.rs b/src/linux/thread_info/aarch64.rs
index 4c69b54d..3eff56d8 100644
--- a/src/linux/thread_info/aarch64.rs
+++ b/src/linux/thread_info/aarch64.rs
@@ -1,9 +1,8 @@
-use super::{CommonThreadInfo, Pid};
+use super::{CommonThreadInfo, NT_Elf, Pid};
use crate::{
errors::ThreadInfoError,
minidump_cpu::{RawContextCPU, FP_REG_COUNT, GP_REG_COUNT},
};
-#[cfg(not(target_os = "android"))]
use nix::sys::ptrace;
/// https://github.com/rust-lang/libc/pull/2719
@@ -34,55 +33,40 @@ impl ThreadInfoAarch64 {
self.regs.pc as usize
}
- // nix currently doesn't support PTRACE_GETFPREGS, so we have to do it ourselves
- fn getfpregs(pid: Pid) -> Result {
- cfg_if::cfg_if! {
- if #[cfg(target_os = "android")] {
- // TODO: nix restricts PTRACE_GETFPREGS to arm android for some reason
- let mut data = std::mem::MaybeUninit::::uninit();
- let res = unsafe {
- libc::ptrace(
- 14,
- libc::pid_t::from(pid),
- super::NT_Elf::NT_NONE,
- data.as_mut_ptr(),
- )
- };
- nix::errno::Errno::result(res)?;
- Ok(unsafe { data.assume_init() })
- } else {
- Self::ptrace_get_data_via_io::(
- ptrace::Request::PTRACE_GETREGSET,
- Some(super::NT_Elf::NT_PRFPREGSET),
- nix::unistd::Pid::from_raw(pid),
- )
- }
- }
+ // nix currently doesn't support PTRACE_GETREGSET, so we have to do it ourselves
+ fn getregset(pid: Pid) -> Result {
+ Self::ptrace_get_data_via_io(
+ 0x4204 as ptrace::RequestType, // PTRACE_GETREGSET
+ Some(NT_Elf::NT_PRSTATUS),
+ nix::unistd::Pid::from_raw(pid),
+ )
}
fn getregs(pid: Pid) -> Result {
- cfg_if::cfg_if! {
- if #[cfg(target_os = "android")] {
- // TODO: nix restricts PTRACE_GETREGS to arm android for some reason
- let mut data = std::mem::MaybeUninit::::uninit();
- let res = unsafe {
- libc::ptrace(
- 12,
- libc::pid_t::from(pid),
- super::NT_Elf::NT_NONE,
- data.as_mut_ptr(),
- )
- };
- nix::errno::Errno::result(res)?;
- Ok(unsafe { data.assume_init() })
- } else {
- Self::ptrace_get_data_via_io::(
- ptrace::Request::PTRACE_GETREGSET,
- Some(super::NT_Elf::NT_PRSTATUS),
- nix::unistd::Pid::from_raw(pid),
- )
- }
- }
+ // TODO: nix restricts PTRACE_GETREGS to arm android for some reason
+ Self::ptrace_get_data(
+ 12 as ptrace::RequestType, // PTRACE_GETREGS
+ None,
+ nix::unistd::Pid::from_raw(pid),
+ )
+ }
+
+ // nix currently doesn't support PTRACE_GETREGSET, so we have to do it ourselves
+ fn getfpregset(pid: Pid) -> Result {
+ Self::ptrace_get_data_via_io(
+ 0x4204 as ptrace::RequestType, // PTRACE_GETREGSET
+ Some(NT_Elf::NT_PRFPREGSET),
+ nix::unistd::Pid::from_raw(pid),
+ )
+ }
+
+ // nix currently doesn't support PTRACE_GETFPREGS, so we have to do it ourselves
+ fn getfpregs(pid: Pid) -> Result {
+ Self::ptrace_get_data(
+ 14 as ptrace::RequestType, // PTRACE_GETFPREGS
+ None,
+ nix::unistd::Pid::from_raw(pid),
+ )
}
pub fn fill_cpu_context(&self, out: &mut RawContextCPU) {
@@ -104,8 +88,8 @@ impl ThreadInfoAarch64 {
pub fn create_impl(_pid: Pid, tid: Pid) -> Result {
let (ppid, tgid) = Self::get_ppid_and_tgid(tid)?;
- let regs = Self::getregs(tid)?;
- let fpregs = Self::getfpregs(tid)?;
+ let regs = Self::getregset(tid).or_else(|_| Self::getregs(tid))?;
+ let fpregs = Self::getfpregset(tid).or_else(|_| Self::getfpregs(tid))?;
let stack_pointer = regs.sp as usize;
diff --git a/src/linux/thread_info/arm.rs b/src/linux/thread_info/arm.rs
index 6359f9b8..954aec61 100644
--- a/src/linux/thread_info/arm.rs
+++ b/src/linux/thread_info/arm.rs
@@ -1,43 +1,21 @@
-use super::{CommonThreadInfo, Pid};
+use super::{CommonThreadInfo, NT_Elf, Pid};
use crate::{errors::ThreadInfoError, minidump_cpu::RawContextCPU};
use nix::sys::ptrace;
type Result = std::result::Result;
-// These are not (yet) part of the libc-crate
-// #[repr(C)]
-// #[derive(Debug, Eq, Hash, PartialEq, Copy, Clone, Default)]
-// pub struct fp_reg {
-// // TODO: No bitfields at the moment, just the next best integer-type
-// sign1: u8,
-// unused: u16,
-// sign2: u8,
-// exponent: u16,
-// j: u8,
-// mantissa1: u32,
-// mantissa2: u32,
-// // unsigned int sign1:1;
-// // unsigned int unused:15;
-// // unsigned int sign2:1;
-// // unsigned int exponent:14;
-// // unsigned int j:1;
-// // unsigned int mantissa1:31;
-// // unsigned int mantissa0:32;
-// }
+// Not defined by libc because this works only for cores support VFP
+#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Debug, Eq, Hash, PartialEq, Copy, Clone, Default)]
-pub struct user_fpregs {
- // fpregs: [fp_reg; 8],
- fpregs: [u32; 8 * 3], // Fields not used, so shortening the struct to 3 x u32
- fpsr: u32,
- fpcr: u32,
- ftype: [u8; 8],
- init_flag: u32,
+pub struct user_fpregs_struct {
+ pub fpregs: [u64; 32],
+ pub fpscr: u32,
}
#[repr(C)]
#[derive(Debug, Eq, Hash, PartialEq, Copy, Clone, Default)]
-pub struct user_regs {
+pub struct user_regs_struct {
uregs: [u32; 18],
}
@@ -46,26 +24,26 @@ pub struct ThreadInfoArm {
pub stack_pointer: usize,
pub tgid: Pid, // thread group id
pub ppid: Pid, // parent process
- pub regs: user_regs,
- pub fpregs: user_fpregs,
+ pub regs: user_regs_struct,
+ pub fpregs: user_fpregs_struct,
}
impl CommonThreadInfo for ThreadInfoArm {}
impl ThreadInfoArm {
// nix currently doesn't support PTRACE_GETFPREGS, so we have to do it ourselves
- fn getfpregs(pid: Pid) -> Result {
- Self::ptrace_get_data::(
- ptrace::Request::PTRACE_GETFPREGS,
- None,
+ fn getfpregs(pid: Pid) -> Result {
+ Self::ptrace_get_data_via_io(
+ 0x4204 as ptrace::RequestType, // PTRACE_GETREGSET
+ Some(NT_Elf::NT_ARM_VFP),
nix::unistd::Pid::from_raw(pid),
)
}
- // nix currently doesn't support PTRACE_GETFPREGS, so we have to do it ourselves
- fn getregs(pid: Pid) -> Result {
- Self::ptrace_get_data::(
- ptrace::Request::PTRACE_GETFPREGS,
+ // nix currently doesn't support PTRACE_GETREGS, so we have to do it ourselves
+ fn getregs(pid: Pid) -> Result {
+ Self::ptrace_get_data::(
+ ptrace::Request::PTRACE_GETREGS as ptrace::RequestType,
None,
nix::unistd::Pid::from_raw(pid),
)
@@ -80,19 +58,15 @@ impl ThreadInfoArm {
crate::minidump_format::format::ContextFlagsArm::CONTEXT_ARM_FULL.bits();
out.iregs.copy_from_slice(&self.regs.uregs[..16]);
- // No CPSR register in ThreadInfo(it's not accessible via ptrace)
- out.cpsr = 0;
-
- #[cfg(not(target_os = "android"))]
- {
- out.float_save.fpscr = self.fpregs.fpsr as u64 | ((self.fpregs.fpcr as u64) << 32);
- }
+ out.cpsr = self.regs.uregs[16];
+ out.float_save.fpscr = self.fpregs.fpscr as u64;
+ out.float_save.regs = self.fpregs.fpregs;
}
pub fn create_impl(_pid: Pid, tid: Pid) -> Result {
let (ppid, tgid) = Self::get_ppid_and_tgid(tid)?;
let regs = Self::getregs(tid)?;
- let fpregs = Self::getfpregs(tid)?;
+ let fpregs = Self::getfpregs(tid).unwrap_or(Default::default());
let stack_pointer = regs.uregs[13] as usize;
diff --git a/src/linux/thread_info/x86.rs b/src/linux/thread_info/x86.rs
index 9ede4f44..0447ed09 100644
--- a/src/linux/thread_info/x86.rs
+++ b/src/linux/thread_info/x86.rs
@@ -2,7 +2,7 @@ use super::{CommonThreadInfo, NT_Elf, Pid};
use crate::{errors::ThreadInfoError, minidump_cpu::RawContextCPU, minidump_format::format};
use core::mem::size_of_val;
use libc::user;
-use nix::{sys::ptrace, unistd};
+use nix::sys::ptrace;
use scroll::Pwrite;
type Result = std::result::Result;
@@ -28,17 +28,26 @@ impl CommonThreadInfo for ThreadInfoX86 {}
impl ThreadInfoX86 {
// nix currently doesn't support PTRACE_GETREGSET, so we have to do it ourselves
fn getregset(pid: Pid) -> Result {
- Self::ptrace_get_data_via_io::(
- ptrace::Request::PTRACE_GETREGSET,
+ Self::ptrace_get_data_via_io(
+ 0x4204 as ptrace::RequestType, // PTRACE_GETREGSET
Some(NT_Elf::NT_PRSTATUS),
nix::unistd::Pid::from_raw(pid),
)
}
+ pub fn getregs(pid: Pid) -> Result {
+ // TODO: nix restricts PTRACE_GETREGS to arm android for some reason
+ Self::ptrace_get_data(
+ 12 as ptrace::RequestType, // PTRACE_GETREGS
+ None,
+ nix::unistd::Pid::from_raw(pid),
+ )
+ }
+
// nix currently doesn't support PTRACE_GETREGSET, so we have to do it ourselves
fn getfpregset(pid: Pid) -> Result {
- Self::ptrace_get_data_via_io::(
- ptrace::Request::PTRACE_GETREGSET,
+ Self::ptrace_get_data_via_io(
+ 0x4204 as ptrace::RequestType, // PTRACE_GETREGSET
Some(NT_Elf::NT_PRFPREGSET),
nix::unistd::Pid::from_raw(pid),
)
@@ -46,8 +55,8 @@ impl ThreadInfoX86 {
// nix currently doesn't support PTRACE_GETFPREGS, so we have to do it ourselves
fn getfpregs(pid: Pid) -> Result {
- Self::ptrace_get_data::(
- ptrace::Request::PTRACE_GETFPREGS,
+ Self::ptrace_get_data(
+ 14 as ptrace::RequestType, // PTRACE_GETFPREGS
None,
nix::unistd::Pid::from_raw(pid),
)
@@ -56,8 +65,8 @@ impl ThreadInfoX86 {
// nix currently doesn't support PTRACE_GETFPXREGS, so we have to do it ourselves
#[cfg(target_arch = "x86")]
fn getfpxregs(pid: Pid) -> Result {
- Self::ptrace_get_data::(
- ptrace::Request::PTRACE_GETFPXREGS,
+ Self::ptrace_get_data(
+ ptrace::Request::PTRACE_GETFPXREGS as ptrace::RequestType,
None,
nix::unistd::Pid::from_raw(pid),
)
@@ -65,7 +74,7 @@ impl ThreadInfoX86 {
fn peek_user(pid: Pid, addr: ptrace::AddressType) -> nix::Result {
Self::ptrace_peek(
- ptrace::Request::PTRACE_PEEKUSER,
+ ptrace::Request::PTRACE_PEEKUSER as ptrace::RequestType,
nix::unistd::Pid::from_raw(pid),
addr,
std::ptr::null_mut(),
@@ -74,7 +83,7 @@ impl ThreadInfoX86 {
pub fn create_impl(_pid: Pid, tid: Pid) -> Result {
let (ppid, tgid) = Self::get_ppid_and_tgid(tid)?;
- let regs = Self::getregset(tid).or_else(|_| ptrace::getregs(unistd::Pid::from_raw(tid)))?;
+ let regs = Self::getregset(tid).or_else(|_| Self::getregs(tid))?;
let fpregs = Self::getfpregset(tid).or_else(|_| Self::getfpregs(tid))?;
#[cfg(target_arch = "x86")]
let fpxregs: libc::user_fpxregs_struct;
diff --git a/tests/linux_minidump_writer.rs b/tests/linux_minidump_writer.rs
index 3493dd2f..f8eb7c61 100644
--- a/tests/linux_minidump_writer.rs
+++ b/tests/linux_minidump_writer.rs
@@ -34,7 +34,7 @@ enum Context {
impl Context {
pub fn minidump_writer(&self, pid: Pid) -> MinidumpWriter {
let mut mw = MinidumpWriter::new(pid, pid);
- #[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
+ #[cfg(not(target_arch = "mips"))]
if self == &Context::With {
let crash_context = get_crash_context(pid);
mw.set_crash_context(crash_context);
@@ -43,7 +43,7 @@ impl Context {
}
}
-#[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
+#[cfg(not(target_arch = "mips"))]
fn get_ucontext() -> Result {
let mut context = std::mem::MaybeUninit::uninit();
unsafe {
@@ -54,10 +54,11 @@ fn get_ucontext() -> Result {
}
}
-#[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
+#[cfg(not(target_arch = "mips"))]
fn get_crash_context(tid: Pid) -> CrashContext {
let siginfo: libc::signalfd_siginfo = unsafe { std::mem::zeroed() };
let context = get_ucontext().expect("Failed to get ucontext");
+ #[cfg(not(target_arch = "arm"))]
let float_state = unsafe { std::mem::zeroed() };
CrashContext {
inner: crash_context::CrashContext {
@@ -65,6 +66,7 @@ fn get_crash_context(tid: Pid) -> CrashContext {
pid: std::process::id() as _,
tid,
context,
+ #[cfg(not(target_arch = "arm"))]
float_state,
},
}
@@ -83,7 +85,7 @@ macro_rules! contextual_tests {
test(Context::Without)
}
- #[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
+ #[cfg(not(target_arch = "mips"))]
#[test]
fn run_with_context() {
test(Context::With)
From a88cc13ce625f899513fdafa226234abc252bd8c Mon Sep 17 00:00:00 2001
From: Gabriele Svelto
Date: Wed, 7 Jun 2023 17:58:05 +0200
Subject: [PATCH 2/2] Properly merge properties when merging adjacent mappings
This fixes a regression introduced in
e70690238948fca9c23f7f8e5bcce30c00ce79bb which prevented stack mappings
from being correctly merged on Android.
---
src/linux/maps_reader.rs | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/src/linux/maps_reader.rs b/src/linux/maps_reader.rs
index ade63d45..b0017f73 100644
--- a/src/linux/maps_reader.rs
+++ b/src/linux/maps_reader.rs
@@ -108,7 +108,7 @@ impl MappingInfo {
{
module.system_mapping_info.end_address = end_address;
module.size = end_address - module.start_address;
- module.permissions |= mm.perms & MMPermissions::EXECUTE;
+ module.permissions |= mm.perms;
continue;
}
} else {
@@ -433,7 +433,10 @@ ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsysca
end_address: 0x559748406000,
},
offset: 0,
- permissions: MMPermissions::READ | MMPermissions::EXECUTE | MMPermissions::PRIVATE,
+ permissions: MMPermissions::READ
+ | MMPermissions::WRITE
+ | MMPermissions::EXECUTE
+ | MMPermissions::PRIVATE,
name: Some("/usr/bin/cat".into()),
};
@@ -540,7 +543,10 @@ ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsysca
end_address: 0x7efd96d8c000, // ..but this is not visible here
},
offset: 0,
- permissions: MMPermissions::READ | MMPermissions::EXECUTE | MMPermissions::PRIVATE,
+ permissions: MMPermissions::READ
+ | MMPermissions::WRITE
+ | MMPermissions::EXECUTE
+ | MMPermissions::PRIVATE,
name: Some("/lib64/libc-2.32.so".into()),
};