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()), };