diff --git a/crates/linux/process-backend/src/lib.rs b/crates/linux/process-backend/src/lib.rs index 6c9c72ce..d52ec9b6 100644 --- a/crates/linux/process-backend/src/lib.rs +++ b/crates/linux/process-backend/src/lib.rs @@ -9,9 +9,10 @@ mod drop_fail_handler; pub mod local; pub mod regs; -/// This is the longest path length we guarantee we can handle, since we won't be able to allocate -/// in the fork of the crashed process. We can increase if necessary. -pub const MAX_PATH_LEN: usize = 256; +mod wrapper; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub const PTRACE_DATA_LEN: usize = core::mem::size_of::(); #[derive(Debug, serde::Deserialize, serde::Serialize)] pub enum ProcessReaderKind { diff --git a/crates/linux/process-backend/src/local/mod.rs b/crates/linux/process-backend/src/local/mod.rs index 288f4e06..77086186 100644 --- a/crates/linux/process-backend/src/local/mod.rs +++ b/crates/linux/process-backend/src/local/mod.rs @@ -1,4 +1,8 @@ -use crate::{ProcessReaderKind, regs::*}; +use crate::{ + ProcessReaderKind, + regs::*, + wrapper::{OwnedFd, errno, set_errno}, +}; use core::{ cell::RefCell, ffi::{CStr, c_int, c_long, c_void}, @@ -7,12 +11,15 @@ use core::{ use libc::pid_t; use syscall_invoker::SyscallInvoker; -pub use self::{error::Error, module_reader::MappedModuleMemoryReader}; +pub use error::Error; +pub use module_reader::MappedModuleMemoryReader; mod error; mod module_reader; mod syscall_invoker; +pub type Result = core::result::Result; + #[cfg(target_env = "gnu")] type PtraceRequestType = core::ffi::c_uint; @@ -34,22 +41,25 @@ impl Backend { syscall_invoker: Default::default(), } } + pub fn pid(&self) -> libc::pid_t { + self.pid + } pub fn process_reader(&self) -> ProcessReader<'_> { ProcessReader(&self.process_reader) } - pub fn stop_process(&self) -> Result<(), Error> { + pub fn stop_process(&self) -> Result<()> { self.standard_syscall(|| unsafe { libc::kill(self.pid, libc::SIGSTOP) }) .map_err(Error::SigStopFailed)?; Ok(()) } - pub fn continue_process(&self) -> Result<(), Error> { + pub fn continue_process(&self) -> Result<()> { self.standard_syscall(|| unsafe { libc::kill(self.pid, libc::SIGCONT) }) .map_err(Error::SigContFailed)?; Ok(()) } - pub fn suspend_thread(&self, tid: libc::pid_t) -> Result<(), Error> { + pub fn suspend_thread(&self, tid: libc::pid_t) -> Result<()> { self.standard_syscall(|| unsafe { ptrace(libc::PTRACE_ATTACH, tid, ptr::null_mut(), ptr::null_mut()) }) @@ -91,7 +101,7 @@ impl Backend { Ok(()) } - pub fn resume_thread(&self, tid: libc::pid_t) -> Result<(), Error> { + pub fn resume_thread(&self, tid: libc::pid_t) -> Result<()> { self.ptrace_detach(tid) } @@ -99,22 +109,22 @@ impl Backend { &self, path: &CStr, offset: u64, - ) -> Result { + ) -> Result { MappedModuleMemoryReader::new(&mut self.syscall_invoker.borrow_mut(), path, offset) } - pub fn stat_file(&self, path: &CStr) -> Result { + pub fn stat_file(&self, path: &CStr) -> Result { let mut output = unsafe { mem::zeroed::() }; self.standard_syscall(|| unsafe { libc::stat(path.as_ptr(), &mut output) }) .map_err(Error::StatFailed)?; Ok(output) } - pub fn read_file(&self, path: &CStr) -> Result { + pub fn read_file(&self, path: &CStr) -> Result { self.open_file(path).map(FileReader) } - pub fn read_dir(&self, path: &CStr) -> Result { + pub fn read_dir(&self, path: &CStr) -> Result { self.special_syscall(|| unsafe { let dirp = libc::opendir(path.as_ptr()); if dirp.is_null() { @@ -126,7 +136,7 @@ impl Backend { .map_err(Error::OpenDirFailed) } - pub fn read_link(&self, path: &CStr, buf: &mut [u8]) -> Result { + pub fn read_link(&self, path: &CStr, buf: &mut [u8]) -> Result { let bytes_read = self .standard_syscall(|| unsafe { libc::readlink(path.as_ptr(), buf.as_mut_ptr().cast(), buf.len()) @@ -141,31 +151,27 @@ impl Backend { Ok(bytes_read) } - pub fn get_gen_regs(&self, tid: libc::pid_t) -> Result { + pub fn get_gen_regs(&self, tid: libc::pid_t) -> Result { self.getregset(tid).or_else(|_| self.getregs(tid)) } - pub fn get_fp_regs(&self, tid: libc::pid_t) -> Result { + pub fn get_fp_regs(&self, tid: libc::pid_t) -> Result { self.getfpregset(tid).or_else(|_| self.getfpregs(tid)) } #[cfg(target_arch = "x86")] - pub fn get_fpx_regs(&self, tid: libc::pid_t) -> Result { + pub fn get_fpx_regs(&self, tid: libc::pid_t) -> Result { const PTRACE_GETFPXREGS: PtraceRequestType = 18; unsafe { self.ptrace_getregs::(PTRACE_GETFPXREGS, tid) } } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn ptrace_peekuser( - &self, - pid: libc::pid_t, - addr: usize, - ) -> Result<[u8; mem::size_of::()], Error> { + pub fn ptrace_peekuser(&self, addr: usize) -> Result<[u8; crate::PTRACE_DATA_LEN]> { self.special_syscall(|| unsafe { set_errno(0); let rv = ptrace( libc::PTRACE_PEEKUSER, - pid, + self.pid, addr as *mut _, core::ptr::null_mut(), ); @@ -177,7 +183,7 @@ impl Backend { .map_err(Error::PtracePeekUserFailed) } - pub fn force_process_reader_kind(&mut self, kind: ProcessReaderKind) -> Result<(), Error> { + pub fn force_process_reader_kind(&mut self, kind: ProcessReaderKind) -> Result<()> { use ProcessReaderKind as K; self.process_reader = match kind { K::Unspecified => process_reader::ProcessReader::new(self.pid), @@ -190,7 +196,7 @@ impl Backend { Ok(()) } - fn open_file(&self, path: &CStr) -> Result { + fn open_file(&self, path: &CStr) -> Result { self.standard_syscall(|| unsafe { libc::open(path.as_ptr(), libc::O_RDONLY | libc::O_CLOEXEC, 0) }) @@ -198,7 +204,7 @@ impl Backend { .map_err(Error::OpenFileFailed) } - fn getregset(&self, _pid: libc::pid_t) -> Result { + fn getregset(&self, _tid: libc::pid_t) -> Result { #[cfg(target_arch = "arm")] { Err(Error::NotSupported) @@ -206,29 +212,29 @@ impl Backend { #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] { const NT_PRSTATUS: usize = 1; - self.ptrace_getregset(NT_PRSTATUS, _pid) + self.ptrace_getregset(NT_PRSTATUS, _tid) } } - fn getregs(&self, pid: libc::pid_t) -> Result { + fn getregs(&self, tid: libc::pid_t) -> Result { const PTRACE_GETREGS: PtraceRequestType = 12; - unsafe { self.ptrace_getregs::(PTRACE_GETREGS, pid) } + unsafe { self.ptrace_getregs::(PTRACE_GETREGS, tid) } } - fn getfpregset(&self, pid: libc::pid_t) -> Result { + fn getfpregset(&self, tid: libc::pid_t) -> Result { #[cfg(target_arch = "arm")] { const NT_ARM_VFP: usize = 0x400; - self.ptrace_getregset(NT_ARM_VFP, pid) + self.ptrace_getregset(NT_ARM_VFP, tid) } #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] { const NT_PRFPREGSET: usize = 2; - self.ptrace_getregset(NT_PRFPREGSET, pid) + self.ptrace_getregset(NT_PRFPREGSET, tid) } } - fn getfpregs(&self, _pid: libc::pid_t) -> Result { + fn getfpregs(&self, _tid: libc::pid_t) -> Result { #[cfg(target_arch = "arm")] { Err(Error::NotSupported) @@ -236,21 +242,17 @@ impl Backend { #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] { const PTRACE_GETFPREGS: PtraceRequestType = 14; - unsafe { self.ptrace_getregs::(PTRACE_GETFPREGS, _pid) } + unsafe { self.ptrace_getregs::(PTRACE_GETFPREGS, _tid) } } } /// Safety: RequestType and T must agree on the size of the returned type - unsafe fn ptrace_getregs( - &self, - request: PtraceRequestType, - pid: libc::pid_t, - ) -> Result { + unsafe fn ptrace_getregs(&self, request: PtraceRequestType, tid: libc::pid_t) -> Result { let mut output = mem::MaybeUninit::::uninit(); self.standard_syscall(|| unsafe { ptrace( request, - pid, + tid, core::ptr::null_mut(), output.as_mut_ptr().cast(), ) @@ -259,7 +261,7 @@ impl Backend { Ok(unsafe { output.assume_init() }) } - fn ptrace_getregset(&self, regset_type: usize, pid: libc::pid_t) -> Result { + fn ptrace_getregset(&self, regset_type: usize, tid: libc::pid_t) -> Result { let mut output = mem::MaybeUninit::::uninit(); let mut io = libc::iovec { iov_base: output.as_mut_ptr().cast(), @@ -269,7 +271,7 @@ impl Backend { self.standard_syscall(|| unsafe { ptrace( libc::PTRACE_GETREGSET, - pid, + tid, regset_type as *mut _, (&raw mut io).cast(), ) @@ -285,7 +287,7 @@ impl Backend { Ok(unsafe { output.assume_init() }) } - fn ptrace_detach(&self, tid: libc::pid_t) -> Result<(), Error> { + fn ptrace_detach(&self, tid: libc::pid_t) -> Result<()> { self.standard_syscall(|| unsafe { ptrace(libc::PTRACE_DETACH, tid, ptr::null_mut(), ptr::null_mut()) }) @@ -293,7 +295,7 @@ impl Backend { Ok(()) } - fn standard_syscall(&self, f: F) -> Result + fn standard_syscall(&self, f: F) -> core::result::Result where F: FnOnce() -> T, T: From + core::cmp::PartialEq, @@ -301,9 +303,9 @@ impl Backend { self.syscall_invoker.borrow_mut().invoke_standard(f) } - fn special_syscall(&self, f: F) -> Result + fn special_syscall(&self, f: F) -> core::result::Result where - F: FnOnce() -> Result, + F: FnOnce() -> core::result::Result, { self.syscall_invoker.borrow_mut().invoke(f) } @@ -320,14 +322,14 @@ impl Backend { pub struct FileReader(OwnedFd); impl FileReader { - pub fn read(&mut self, buf: &mut [u8]) -> Result { + pub fn read(&mut self, buf: &mut [u8]) -> Result { let rv = unsafe { libc::read(self.0.as_raw_fd(), buf.as_mut_ptr().cast(), buf.len()) }; if rv == -1 { return Err(Error::ReadFileFailed(errno())); } Ok(rv.try_into().unwrap()) } - pub fn read_at(&self, buf: &mut [u8], offset: u64) -> Result { + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> Result { let rv = unsafe { libc::pread( self.0.as_raw_fd(), @@ -350,7 +352,7 @@ pub struct DirReader { } impl DirReader { - pub fn read_name(&mut self) -> Result, Error> { + pub fn read_next_name(&mut self) -> Result> { if self.eof { return Ok(None); } @@ -396,33 +398,11 @@ impl Drop for DirReader { pub struct ProcessReader<'a>(&'a process_reader::ProcessReader); impl<'a> ProcessReader<'a> { - pub fn read_at(&self, address: usize, buf: &mut [u8]) -> Result { + pub fn read_at(&self, address: usize, buf: &mut [u8]) -> Result { self.0.read_at(address, buf).map_err(Error::ProcessReader) } } -#[derive(Debug)] -struct OwnedFd(c_int); - -impl OwnedFd { - // SAFETY: Must be a valid fd - pub unsafe fn new(fd: c_int) -> Self { - Self(fd) - } - pub fn as_raw_fd(&self) -> c_int { - self.0 - } -} - -impl Drop for OwnedFd { - fn drop(&mut self) { - let rv = unsafe { libc::close(self.0) }; - if rv == -1 { - report_drop_failed!("failed to close file: {}", errno()); - } - } -} - /// This is just a typesafe wrapper around ptrace(), which is vararg... But this is Rust, and /// playing loosey-goosey with types is really more of a C thing ;) unsafe fn ptrace( @@ -433,23 +413,3 @@ unsafe fn ptrace( ) -> c_long { unsafe { libc::ptrace(request, pid, addr, data) } } - -fn errno() -> c_int { - unsafe { *errno_location() } -} - -fn set_errno(value: c_int) { - unsafe { - *errno_location() = value; - } -} - -#[cfg(target_os = "android")] -fn errno_location() -> *mut c_int { - unsafe { libc::__errno() } -} - -#[cfg(not(target_os = "android"))] -fn errno_location() -> *mut c_int { - unsafe { libc::__errno_location() } -} diff --git a/crates/linux/process-backend/src/local/module_reader.rs b/crates/linux/process-backend/src/local/module_reader.rs index dfcb5762..e3c5e2b4 100644 --- a/crates/linux/process-backend/src/local/module_reader.rs +++ b/crates/linux/process-backend/src/local/module_reader.rs @@ -1,10 +1,10 @@ -use { - super::{Error, OwnedFd, SyscallInvoker, errno}, - core::{ - ffi::{CStr, c_void}, - mem, ptr, - }, +use super as local; +use crate::wrapper::{OwnedFd, errno}; +use core::{ + ffi::{CStr, c_void}, + mem, ptr, }; +use local::{Error, Result, SyscallInvoker}; #[derive(Debug)] pub struct MappedModuleMemoryReader { @@ -14,11 +14,24 @@ pub struct MappedModuleMemoryReader { } impl MappedModuleMemoryReader { - pub fn new( + pub fn read_at(&self, offset: usize, length: usize) -> Result<&[u8]> { + let s = self.as_slice(); + let requested_end = offset.checked_add(length).ok_or(Error::IndexOutOfBounds)?; + let maximum_end = s.len(); + let end = usize::min(requested_end, maximum_end); + s.get(offset..end).ok_or(Error::IndexOutOfBounds) + } + pub fn len(&self) -> usize { + self.as_slice().len() + } + pub fn is_empty(&self) -> bool { + self.as_slice().is_empty() + } + pub(crate) fn new( syscall_invoker: &mut SyscallInvoker, path: &CStr, start_position: u64, - ) -> Result { + ) -> Result { let fd = Self::open_file(syscall_invoker, path)?; // So far, we only ever map files from the start position to EOF - We never specify a @@ -51,22 +64,7 @@ impl MappedModuleMemoryReader { Ok(MappedModuleMemoryReader { mapped, ptr, len }) } - pub fn read(&self, offset: u64, length: u64) -> Result<&[u8], Error> { - (|| { - let offset = usize::try_from(offset).ok()?; - let length = usize::try_from(length).ok()?; - let end = offset.checked_add(length)?; - self.as_slice().get(offset..end) - })() - .ok_or(Error::IndexOutOfBounds) - } - pub fn len(&self) -> Result { - Ok(self.as_slice().len()) - } - pub fn is_empty(&self) -> Result { - self.len().map(|l| l == 0) - } - fn open_file(syscall_invoker: &mut SyscallInvoker, path: &CStr) -> Result { + fn open_file(syscall_invoker: &mut SyscallInvoker, path: &CStr) -> Result { syscall_invoker .invoke_standard(|| unsafe { libc::open(path.as_ptr(), libc::O_RDONLY | libc::O_CLOEXEC, 0) @@ -74,7 +72,7 @@ impl MappedModuleMemoryReader { .map(|fd| unsafe { OwnedFd::new(fd) }) .map_err(Error::OpenFileFailed) } - fn get_file_size(syscall_invoker: &mut SyscallInvoker, fd: &OwnedFd) -> Result { + fn get_file_size(syscall_invoker: &mut SyscallInvoker, fd: &OwnedFd) -> Result { let mut stat: libc::stat = unsafe { mem::zeroed() }; syscall_invoker @@ -93,7 +91,7 @@ impl MappedModuleMemoryReader { fd: &OwnedFd, page_aligned_start_position: u64, len: usize, - ) -> Result { + ) -> Result { // Linux requires the mapping length to be non-zero, even though we want to support // zero-length mappings -- So we just make it a one-byte mapping (and ignore the byte). let len = usize::max(len, 1); diff --git a/crates/linux/process-backend/src/local/syscall_invoker.rs b/crates/linux/process-backend/src/local/syscall_invoker.rs index f0e7596f..15a013f3 100644 --- a/crates/linux/process-backend/src/local/syscall_invoker.rs +++ b/crates/linux/process-backend/src/local/syscall_invoker.rs @@ -1,7 +1,8 @@ -use {super::errno, core::ffi::c_int}; +use crate::wrapper::errno; +use core::ffi::c_int; #[derive(Debug, Default)] -pub struct SyscallInvoker(Option); +pub(crate) struct SyscallInvoker(Option); impl SyscallInvoker { /// Helper function to invoke a syscall and capture errno if it fails @@ -12,7 +13,7 @@ impl SyscallInvoker { /// /// If testing requests a failure, will never actually make the syscall and just returns /// the errno requested by testing - pub fn invoke(&mut self, f: F) -> Result + pub(crate) fn invoke(&mut self, f: F) -> Result where F: FnOnce() -> Result, { @@ -24,7 +25,7 @@ impl SyscallInvoker { } /// Ergonomics for `invoke` for the standard case where `-1` indicates the syscall failed - pub fn invoke_standard(&mut self, f: F) -> Result + pub(crate) fn invoke_standard(&mut self, f: F) -> Result where F: FnOnce() -> T, T: From + core::cmp::PartialEq, @@ -40,7 +41,7 @@ impl SyscallInvoker { /// Force the next syscall to fail with the given errno #[cfg(feature = "testing")] - pub fn fail_one_syscall_with(&mut self, errno: c_int) { + pub(crate) fn fail_one_syscall_with(&mut self, errno: c_int) { self.0 = Some(errno); } } diff --git a/crates/linux/process-backend/src/wrapper.rs b/crates/linux/process-backend/src/wrapper.rs new file mode 100644 index 00000000..26a8de7e --- /dev/null +++ b/crates/linux/process-backend/src/wrapper.rs @@ -0,0 +1,43 @@ +use core::ffi::c_int; + +#[derive(Debug)] +pub(crate) struct OwnedFd(c_int); + +impl OwnedFd { + // SAFETY: Must be a valid fd + pub(crate) unsafe fn new(fd: c_int) -> Self { + Self(fd) + } + pub(crate) fn as_raw_fd(&self) -> c_int { + self.0 + } +} + +impl Drop for OwnedFd { + fn drop(&mut self) { + let rv = unsafe { libc::close(self.0) }; + if rv == -1 { + report_drop_failed!("failed to close file: {}", errno()); + } + } +} + +pub(crate) fn errno() -> c_int { + unsafe { *errno_location() } +} + +pub(crate) fn set_errno(value: c_int) { + unsafe { + *errno_location() = value; + } +} + +#[cfg(target_os = "android")] +fn errno_location() -> *mut c_int { + unsafe { libc::__errno() } +} + +#[cfg(not(target_os = "android"))] +fn errno_location() -> *mut c_int { + unsafe { libc::__errno_location() } +} diff --git a/src/linux/auxv/mod.rs b/src/linux/auxv/mod.rs index 9eb0a897..7c804dcd 100644 --- a/src/linux/auxv/mod.rs +++ b/src/linux/auxv/mod.rs @@ -97,7 +97,7 @@ impl AuxvDumpInfo { let auxv_path = format!("/proc/{pid}/auxv"); let auxv_file = process_inspector - .read_file(&auxv_path) + .read_file(auxv_path.clone().into()) .map_err(|e| AuxvError::OpenError(auxv_path, e))?; for pair_result in ProcfsAuxvIter::new(BufReader::new(auxv_file)) { diff --git a/src/linux/dumper_cpu_info/arm.rs b/src/linux/dumper_cpu_info/arm.rs index 0bc1290f..1a331d4b 100644 --- a/src/linux/dumper_cpu_info/arm.rs +++ b/src/linux/dumper_cpu_info/arm.rs @@ -171,12 +171,14 @@ pub fn write_cpu_information( // because the content of /proc/cpuinfo will only mirror the number // of 'online' cores, and thus will vary with time. // See http://www.kernel.org/doc/Documentation/cputopology.txt - if let Ok(mut present_file) = process_inspector.read_file("/sys/devices/system/cpu/present") { + if let Ok(mut present_file) = + process_inspector.read_file("/sys/devices/system/cpu/present".into()) + { // Ignore unparsable content let cpus_present = parse_cpus_from_sysfile(&mut present_file).unwrap_or_default(); if let Ok(mut possible_file) = - process_inspector.read_file("/sys/devices/system/cpu/possible") + process_inspector.read_file("/sys/devices/system/cpu/possible".into()) { // Ignore unparsable content let cpus_possible = parse_cpus_from_sysfile(&mut possible_file).unwrap_or_default(); @@ -197,7 +199,7 @@ pub fn write_cpu_information( } let cpuinfo_file = process_inspector - .read_file("/proc/cpuinfo") + .read_file("/proc/cpuinfo".into()) .map_err(CpuInfoError::ReadFileError)?; let mut cpuid = 0; diff --git a/src/linux/dumper_cpu_info/x86.rs b/src/linux/dumper_cpu_info/x86.rs index 5bfb678d..b1e8ddcc 100644 --- a/src/linux/dumper_cpu_info/x86.rs +++ b/src/linux/dumper_cpu_info/x86.rs @@ -50,7 +50,7 @@ pub fn write_cpu_information( } let cpuinfo_file = process_inspector - .read_file("/proc/cpuinfo") + .read_file("/proc/cpuinfo".into()) .map_err(CpuInfoError::ReadFileError)?; let mut vendor_id = String::new(); diff --git a/src/linux/maps_reader.rs b/src/linux/maps_reader.rs index 57986db1..0a19072a 100644 --- a/src/linux/maps_reader.rs +++ b/src/linux/maps_reader.rs @@ -142,7 +142,7 @@ impl MappingInfo { ) -> Result> { let maps_path = format!("/proc/{}/maps", pid); let maps_file = process_inspector - .read_file(&maps_path) + .read_file(maps_path.into()) .map_err(MapsReaderError::ReadFileFailed)?; let maps = MemoryMaps::from_read(maps_file)?; Self::aggregate(maps, linux_gate_loc) @@ -728,7 +728,7 @@ 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 process_inspector = process_inspection::local(0); let (file_path, file_name, _version) = mappings[0] .get_mapping_effective_path_name_and_version(&process_inspector, None) diff --git a/src/linux/minidump_writer/handle_data_stream.rs b/src/linux/minidump_writer/handle_data_stream.rs index b715bd11..0e1db981 100644 --- a/src/linux/minidump_writer/handle_data_stream.rs +++ b/src/linux/minidump_writer/handle_data_stream.rs @@ -14,9 +14,9 @@ fn descriptor_from_path( path: &Path, ) -> Option { let handle = filename_to_fd(path.file_name().unwrap())?; - let realpath = process_inspector.read_link(path).ok()?; + let realpath = process_inspector.read_link(path.into()).ok()?; let path_rva = write_string_to_location(buffer, realpath.to_string_lossy().as_ref()).ok()?; - let stat = process_inspector.stat_file(path).ok()?; + let stat = process_inspector.stat_file(path.into()).ok()?; // TODO: We store the contents of `st_mode` into the `attributes` field, but // we could also store a human-readable string of the file type inside @@ -67,7 +67,7 @@ impl MinidumpWriter { let proc_fd_path = PathBuf::from(format!("/proc/{}/fd", self.process_id)); let proc_fd_iter = self .process_inspector - .read_dir(&proc_fd_path) + .read_dir(proc_fd_path.clone()) .map_err(SectionHandleDataStreamError::ReadDirFailed)?; let descriptors: Vec<_> = proc_fd_iter .filter_map(|filename| filename.ok()) diff --git a/src/linux/minidump_writer/memory_info_list_stream.rs b/src/linux/minidump_writer/memory_info_list_stream.rs index 63fc56a0..e9f07855 100644 --- a/src/linux/minidump_writer/memory_info_list_stream.rs +++ b/src/linux/minidump_writer/memory_info_list_stream.rs @@ -27,7 +27,7 @@ impl MinidumpWriter { let path = format!("/proc/{}/maps", self.blamed_thread); let reader = self .process_inspector - .read_file(&path) + .read_file(path.into()) .map_err(SectionMemInfoListError::ReadFileFailed)?; let maps = procfs_core::process::MemoryMaps::from_read(reader)?; diff --git a/src/linux/minidump_writer/mod.rs b/src/linux/minidump_writer/mod.rs index e66ede2a..2afd1fcc 100644 --- a/src/linux/minidump_writer/mod.rs +++ b/src/linux/minidump_writer/mod.rs @@ -29,6 +29,7 @@ use { }, std::{ io::{Read, Seek, Write}, + path::PathBuf, time::{Duration, Instant}, }, thiserror::Error, @@ -132,7 +133,7 @@ impl MinidumpWriterConfig { crashing_thread_context: Default::default(), stop_timeout: STOP_TIMEOUT, direct_auxv_dump_info: Default::default(), - process_inspector: ProcessInspector::local(process_id), + process_inspector: process_inspection::local(process_id), } } @@ -600,6 +601,10 @@ impl MinidumpWriter { /// /// This will block waiting for the process to stop until `timeout` has passed. fn stop_process(&mut self, timeout: Duration) -> Result<(), StopProcessError> { + failspot!(if StopProcess { + self.process_inspector.fail_one_syscall_with(libc::EPERM); + }); + self.process_inspector .stop_process() .map_err(StopProcessError::Stop)?; @@ -607,13 +612,13 @@ impl MinidumpWriter { // Something like waitpid for non-child processes would be better, but we have no such // tool, so we poll the status. const POLL_INTERVAL: Duration = Duration::from_millis(1); - let proc_file = format!("/proc/{}/stat", self.process_id); + let proc_file = PathBuf::from(format!("/proc/{}/stat", self.process_id)); let end = Instant::now() + timeout; loop { let stat_file = self .process_inspector - .read_file(&proc_file) + .read_file(proc_file.clone()) .map_err(StopProcessError::ReadFileFailed)?; if let Ok(ProcState::Stopped) = Stat::from_read(stat_file)?.state() { return Ok(()); @@ -646,7 +651,7 @@ impl MinidumpWriter { for file_name in self .process_inspector - .read_dir(&task_path) + .read_dir(task_path.into()) .map_err(InitError::ReadProcTaskFailed)? { let file_name = match file_name { @@ -671,7 +676,7 @@ impl MinidumpWriter { // Read the thread-name (if there is any) let name_result = self .process_inspector - .read_file(format!("/proc/{pid}/task/{tid}/comm")) + .read_file(format!("/proc/{pid}/task/{tid}/comm").into()) .map_err(std::io::Error::other) .and_then(|mut file| { let mut s = String::new(); @@ -971,7 +976,7 @@ fn write_file( filename: &str, ) -> std::result::Result { let content = process_inspector - .read_file(filename) + .read_file(filename.into()) .map_err(std::io::Error::other) .and_then(|mut file| { let mut v = Vec::new(); diff --git a/src/linux/module_reader.rs b/src/linux/module_reader.rs index 7a1ae939..a42f95c8 100644 --- a/src/linux/module_reader.rs +++ b/src/linux/module_reader.rs @@ -164,7 +164,7 @@ pub fn read_build_id_from_file( path: &Path, ) -> Result, Error> { let module_memory_reader = process_inspector - .map_module_into_memory(path, 0) + .map_module_into_memory(path.into(), 0) .map_err(Error::MapModuleFailed)?; read_build_id_from_module(module_memory_reader) } @@ -206,7 +206,7 @@ pub fn read_soname_from_file( } let module_memory_reader = process_inspector - .map_module_into_memory(path, offset) + .map_module_into_memory(path.into(), offset) .map_err(Error::MapModuleFailed)?; let memory_len = module_memory_reader.len().map_err(Error::MapModuleFailed)?; diff --git a/src/linux/process_inspection/mod.rs b/src/linux/process_inspection/mod.rs index 52a03f58..9a328153 100644 --- a/src/linux/process_inspection/mod.rs +++ b/src/linux/process_inspection/mod.rs @@ -1,23 +1,30 @@ -use super::maps_reader; +use super as linux; use crate::module_reader::{ModuleMemoryReadError, ReadError, ReadModuleMemory}; -use core::ffi::c_int; -use failspot::failspot; -use process_backend::{MAX_PATH_LEN, local, regs::*}; +use linux::maps_reader; +use process_backend::{local, regs::*}; use process_reader::ProcessReader; use std::{ borrow::Cow, - ffi::{CString, OsString}, + ffi::{CString, OsString, c_int}, io, os::unix::ffi::OsStringExt, path::PathBuf, }; -pub use process_backend::regs; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use process_backend::PTRACE_DATA_LEN; + +pub(crate) use process_backend::regs; pub use process_backend::ProcessReaderKind; pub mod process_reader; +pub(crate) type Result = core::result::Result; + +// This is an arbitrary choice and may need to be tweaked +const MAX_PATH_LEN: usize = 65536; + #[derive(Debug)] pub struct ProcessInspector { pid: libc::pid_t, @@ -25,47 +32,51 @@ pub struct ProcessInspector { } #[derive(Debug)] -pub enum Backend { +enum Backend { Local { backend: local::Backend }, } -impl ProcessInspector { - pub fn local(pid: libc::pid_t) -> Self { - set_process_backend_drop_fail_handler(); +pub(crate) fn local(pid: libc::pid_t) -> ProcessInspector { + set_process_backend_drop_fail_handler(); - let backend = local::Backend::new(pid); + let backend = local::Backend::new(pid); - ProcessInspector { - pid, - backend: Backend::Local { backend }, - } + ProcessInspector { + pid, + backend: Backend::Local { backend }, } +} + +impl ProcessInspector { pub fn process_reader(&self) -> ProcessReader<'_> { ProcessReader::new(self) } - pub fn stop_process(&self) -> Result<(), Error> { - failspot!(if StopProcess { - return Err(Error::Local(local::Error::SigStopFailed(libc::EPERM))); - }); + pub fn pid(&self) -> Result { + match &self.backend { + Backend::Local { backend, .. } => Ok(backend.pid()), + } + } + + pub fn stop_process(&self) -> Result<()> { match &self.backend { Backend::Local { backend, .. } => backend.stop_process().map_err(Error::Local), } } - pub fn continue_process(&self) -> Result<(), Error> { + pub fn continue_process(&self) -> Result<()> { match &self.backend { Backend::Local { backend, .. } => backend.continue_process().map_err(Error::Local), } } - pub fn suspend_thread(&self, tid: libc::pid_t) -> Result<(), Error> { + pub fn suspend_thread(&self, tid: libc::pid_t) -> Result<()> { match &self.backend { Backend::Local { backend, .. } => backend.suspend_thread(tid).map_err(Error::Local), } } - pub fn resume_thread(&self, tid: libc::pid_t) -> Result<(), Error> { + pub fn resume_thread(&self, tid: libc::pid_t) -> Result<()> { match &self.backend { Backend::Local { backend, .. } => backend.resume_thread(tid).map_err(Error::Local), } @@ -73,10 +84,10 @@ impl ProcessInspector { pub fn map_module_into_memory( &self, - path: impl Into, + path: PathBuf, offset: u64, - ) -> Result { - let c_path = CString::new(path.into().into_os_string().into_vec()).unwrap(); + ) -> Result { + let c_path = CString::new(path.into_os_string().into_vec()).unwrap(); match &self.backend { Backend::Local { backend, .. } => backend .map_module_into_memory(&c_path, offset) @@ -85,15 +96,15 @@ impl ProcessInspector { } } - pub fn stat_file(&self, path: impl Into) -> Result { - let c_path = CString::new(path.into().into_os_string().into_vec()).unwrap(); + pub fn stat_file(&self, path: PathBuf) -> Result { + let c_path = CString::new(path.into_os_string().into_vec()).unwrap(); match &self.backend { Backend::Local { backend, .. } => backend.stat_file(&c_path).map_err(Error::Local), } } - pub fn read_file(&self, path: impl Into) -> Result { - let c_path = CString::new(path.into().into_os_string().into_vec()).unwrap(); + pub fn read_file(&self, path: PathBuf) -> Result { + let c_path = CString::new(path.into_os_string().into_vec()).unwrap(); match &self.backend { Backend::Local { backend, .. } => backend .read_file(&c_path) @@ -102,8 +113,8 @@ impl ProcessInspector { } } - pub fn read_dir(&self, path: impl Into) -> Result { - let c_path = CString::new(path.into().into_os_string().into_vec()).unwrap(); + pub fn read_dir(&self, path: PathBuf) -> Result { + let c_path = CString::new(path.into_os_string().into_vec()).unwrap(); match &self.backend { Backend::Local { backend, .. } => backend .read_dir(&c_path) @@ -112,8 +123,8 @@ impl ProcessInspector { } } - pub fn read_link(&self, path: impl Into) -> Result { - let c_path = CString::new(path.into().into_os_string().into_vec()).unwrap(); + pub fn read_link(&self, path: PathBuf) -> Result { + let c_path = CString::new(path.into_os_string().into_vec()).unwrap(); let mut buf = vec![0u8; MAX_PATH_LEN]; @@ -127,45 +138,46 @@ impl ProcessInspector { Ok(PathBuf::from(OsString::from_vec(buf))) } - pub fn get_gen_regs(&self, tid: libc::pid_t) -> Result { + pub fn get_gen_regs(&self, tid: libc::pid_t) -> Result { match &self.backend { Backend::Local { backend, .. } => backend.get_gen_regs(tid).map_err(Error::Local), } } - pub fn get_fp_regs(&self, tid: libc::pid_t) -> Result { + pub fn get_fp_regs(&self, tid: libc::pid_t) -> Result { match &self.backend { Backend::Local { backend, .. } => backend.get_fp_regs(tid).map_err(Error::Local), } } #[cfg(target_arch = "x86")] - pub fn get_fpx_regs(&self, tid: libc::pid_t) -> Result { + pub fn get_fpx_regs(&self, tid: libc::pid_t) -> Result { match &self.backend { Backend::Local { backend, .. } => backend.get_fpx_regs(tid).map_err(Error::Local), } } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn ptrace_peekuser( - &self, - pid: libc::pid_t, - addr: usize, - ) -> Result<[u8; core::mem::size_of::()], Error> { + pub fn ptrace_peekuser(&self, addr: usize) -> Result<[u8; PTRACE_DATA_LEN]> { match &self.backend { - Backend::Local { backend, .. } => { - backend.ptrace_peekuser(pid, addr).map_err(Error::Local) - } + Backend::Local { backend, .. } => backend.ptrace_peekuser(addr).map_err(Error::Local), } } - pub fn force_process_reader_kind(&mut self, kind: ProcessReaderKind) -> Result<(), Error> { + pub fn force_process_reader_kind(&mut self, kind: ProcessReaderKind) -> Result<()> { match &mut self.backend { Backend::Local { backend, .. } => backend .force_process_reader_kind(kind) .map_err(Error::Local), } } + + #[doc(hidden)] + pub fn fail_one_syscall_with(&self, errno: c_int) { + match &self.backend { + Backend::Local { backend, .. } => backend.fail_one_syscall_with(errno), + } + } } #[derive(Debug)] @@ -188,23 +200,16 @@ pub enum DirReader { } impl Iterator for DirReader { - type Item = Result; + type Item = Result; fn next(&mut self) -> Option { match self { - Self::Local(l) => match l.read_name().map_err(Error::Local) { - Ok(Some(name_bytes)) => Some(Ok(OsString::from_vec(name_bytes.to_vec()))), - Ok(None) => None, - Err(e) => Some(Err(e)), - }, - } - } -} - -#[doc(hidden)] -impl ProcessInspector { - pub fn fail_one_syscall_with(&self, errno: c_int) { - match &self.backend { - Backend::Local { backend, .. } => backend.fail_one_syscall_with(errno), + Self::Local(l) => Some( + l.read_next_name() + .transpose()? + .map(<[u8]>::to_vec) + .map(OsString::from_vec) + .map_err(Error::Local), + ), } } } @@ -215,32 +220,63 @@ pub enum MappedModuleMemoryReader { } impl MappedModuleMemoryReader { - pub fn read(&self, offset: u64, length: u64) -> Result<&[u8], Error> { - match self { - Self::Local(l) => l.read(offset, length).map_err(Error::Local), + pub fn read_exact_at(&self, mut offset: usize, mut buf: &mut [u8]) -> Result<()> { + if buf.is_empty() { + return Ok(()); } - } - pub fn len(&self) -> Result { + match self { - Self::Local(l) => l.len().map_err(Error::Local), + Self::Local(l) => loop { + let bytes = l.read_at(offset, buf.len()).map_err(Error::Local)?; + if bytes.is_empty() { + return Err(Error::UnexpectedEndOfBuffer); + } + let (dst, tail) = buf.split_at_mut(bytes.len()); + dst.copy_from_slice(bytes); + if tail.is_empty() { + return Ok(()); + } + offset = offset + .checked_add(dst.len()) + .ok_or(Error::AddressOverflowed)?; + buf = tail; + }, } } - pub fn is_empty(&self) -> Result { + pub fn len(&self) -> Result { match self { - Self::Local(l) => l.is_empty().map_err(Error::Local), + Self::Local(l) => Ok(l.len()), } } } impl ReadModuleMemory for MappedModuleMemoryReader { - fn read(&self, offset: u64, length: u64) -> Result, ModuleMemoryReadError> { - self.read(offset, length) - .map(Cow::Borrowed) - .map_err(|e| ModuleMemoryReadError { + fn read( + &self, + offset: u64, + length: u64, + ) -> core::result::Result, ModuleMemoryReadError> { + let result = (|| { + let (offset, length) = match (usize::try_from(offset), usize::try_from(length)) { + (Ok(o), Ok(l)) => (o, l), + _ => return Err(ReadError::OutOfBounds), + }; + + let mut buf = vec![0u8; length]; + + self.read_exact_at(offset, &mut buf) + .map_err(ReadError::PlatformSpecific)?; + + Ok(buf) + })(); + + result + .map(Cow::Owned) + .map_err(|error| ModuleMemoryReadError { offset, length, start_address: None, - error: ReadError::PlatformSpecific(e), + error, }) } fn absolute_to_relative(&self, addr: u64) -> Option { @@ -255,10 +291,14 @@ impl ReadModuleMemory for MappedModuleMemoryReader { } } -#[derive(Debug, thiserror::Error, serde::Serialize, serde::Deserialize)] +#[derive(Debug, thiserror::Error, serde::Serialize)] pub enum Error { #[error("an error occurred running a syscall directly")] Local(#[source] local::Error), + #[error("an address overflowed")] + AddressOverflowed, + #[error("unexpected end of buffer reached")] + UnexpectedEndOfBuffer, } fn set_process_backend_drop_fail_handler() { diff --git a/src/linux/process_inspection/process_reader.rs b/src/linux/process_inspection/process_reader.rs index a9f6a856..ed467c7f 100644 --- a/src/linux/process_inspection/process_reader.rs +++ b/src/linux/process_inspection/process_reader.rs @@ -12,10 +12,6 @@ pub struct ProcessReader<'a> { } impl<'a> ProcessReader<'a> { - pub fn new(process_inspector: &'a ProcessInspector) -> Self { - Self { process_inspector } - } - /// Read memory from the process into the given buffer. /// /// Returns the number of bytes read. @@ -58,9 +54,12 @@ impl<'a> ProcessReader<'a> { }) .ok_or(FindModuleError::ModuleNotFound) } + pub(crate) fn new(process_inspector: &'a ProcessInspector) -> Self { + Self { process_inspector } + } } -#[derive(Debug, thiserror::Error, serde::Serialize, serde::Deserialize)] +#[derive(Debug, thiserror::Error, serde::Serialize)] pub enum CopyFromProcessError { #[error("an error occurred calling ProcessReader")] Backend(Error), diff --git a/src/linux/thread_info/x86.rs b/src/linux/thread_info/x86.rs index 0f2e2d8f..b6e5e95b 100644 --- a/src/linux/thread_info/x86.rs +++ b/src/linux/thread_info/x86.rs @@ -42,7 +42,7 @@ impl ThreadInfoX86 { let debug_offset = mem::offset_of!(user, u_debugreg); for (idx, dreg) in dregs.iter_mut().enumerate() { let chunk = process_inspector - .ptrace_peekuser(tid, debug_offset + idx * mem::size_of::()) + .ptrace_peekuser(debug_offset + idx * mem::size_of::()) .map_err(ThreadInfoError::PtraceError)?; *dreg = RegType::from_ne_bytes(chunk[0..mem::size_of::()].try_into().unwrap()); diff --git a/tests/common/mod.rs b/tests/common/mod.rs index bf6368b1..7c8321bd 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,6 +1,6 @@ use std::{ error, - io::{BufRead, BufReader, Write}, + io::{BufRead, BufReader}, process::{Child, Command, Stdio}, result, }; @@ -47,8 +47,15 @@ pub fn spawn_child(command: &str, args: &[&str]) { let child = cmd.output().expect("failed to execute child"); println!("Child output:"); - std::io::stdout().write_all(&child.stdout).unwrap(); - std::io::stdout().write_all(&child.stderr).unwrap(); + println!("===stdout==="); + + print_stdio(&child.stdout); + + println!("\n===stderr==="); + + print_stdio(&child.stderr); + + println!("\n============"); assert_eq!(child.status.code().expect("No return value"), 0); } @@ -193,3 +200,19 @@ mod linux { } } } + +fn print_stdio(bytes: &[u8]) { + if let Ok(s) = str::from_utf8(bytes) { + print!("{s}"); + } else { + for (idx, b) in bytes.iter().enumerate() { + if idx == 0 { + print!("{b:02x}"); + } else if idx % 16 == 0 { + print!("\n{b:02x}"); + } else { + print!(" {b:02x}"); + } + } + } +} diff --git a/tests/linux_minidump_writer_soft_error.rs b/tests/linux_minidump_writer_soft_error.rs index 0b2bd1a0..7032d6b1 100644 --- a/tests/linux_minidump_writer_soft_error.rs +++ b/tests/linux_minidump_writer_soft_error.rs @@ -1,11 +1,9 @@ #![cfg(any(target_os = "linux", target_os = "android"))] -use { - common::*, - minidump::Minidump, - minidump_writer::{FailSpotName, minidump_writer::MinidumpWriterConfig}, - serde_json::json, -}; +use common::*; +use minidump::Minidump; +use minidump_writer::{FailSpotName, minidump_writer::MinidumpWriterConfig}; +use serde_json as json; mod common; @@ -34,43 +32,29 @@ fn soft_error_stream() { read_minidump_soft_errors_or_panic(&dump); } +fn visit_json_terminals(json: &json::Value, path: &mut Vec, visit: &mut V) +where + V: FnMut(&[String], &json::Value), +{ + if let Some(obj) = json.as_object() { + for (key, value) in obj.iter() { + path.push(key.clone()); + visit_json_terminals(value, path, visit); + path.pop(); + } + return; + } + if let Some(arr) = json.as_array() { + for value in arr.iter() { + visit_json_terminals(value, path, visit); + } + return; + } + visit(path, json); +} + #[test] fn soft_error_stream_content() { - let expected_errors = vec![ - json!({"InitErrors": [ - {"StopProcessFailed": - {"Stop": - {"Local": - {"SigStopFailed": 1} - } - } - }, - {"FillMissingAuxvInfoErrors": ["InvalidFormat"]}, - {"EnumerateThreadsErrors": [ - {"ReadThreadNameFailed": "\ - Custom {\n \ - kind: Other,\n \ - error: Local(\n \ - OpenFileFailed(\n \ - 1,\n \ - ),\n \ - ),\n\ - }" - } - ]}, - {"SuspendThreadsErrors": [{"PtraceAttachError": [1234, libc::EPERM]}]} - ]}), - json!({"WriteSystemInfoErrors": [ - {"WriteCpuInformationFailed": { - "ReadFileError": { - "Local": { - "OpenFileFailed": 1 - } - } - }} - ]}), - ]; - let mut child = start_child_and_wait_for_threads(1); let pid = child.id() as i32; @@ -99,5 +83,46 @@ fn soft_error_stream_content() { // Ensure the MozSoftErrors stream contains the expected errors let dump = Minidump::read_path(tmpfile.path()).expect("failed to read minidump"); - assert_soft_errors_in_minidump(&dump, &expected_errors); + + let actual_json = read_minidump_soft_errors_or_panic(&dump); + + let mut stop_process_error_found = false; + let mut missing_auxv_error_found = false; + let mut thread_name_error_found = false; + let mut suspend_thread_error_found = false; + let mut cpu_info_error_found = false; + + let mut path = Vec::new(); + + visit_json_terminals(&actual_json, &mut path, &mut |path, value| { + if value.as_i64() == Some(libc::EPERM.into()) + && path.contains(&"StopProcessFailed".to_string()) + { + stop_process_error_found = true; + } + if value.as_str() == Some("InvalidFormat") + && path.contains(&"FillMissingAuxvInfoErrors".to_string()) + { + missing_auxv_error_found = true; + } + if path.contains(&"ReadThreadNameFailed".to_string()) { + thread_name_error_found = true; + } + if value.as_i64() == Some(libc::EPERM.into()) + && path.contains(&"SuspendThreadsErrors".to_string()) + { + suspend_thread_error_found = true; + } + if value.as_i64() == Some(libc::EPERM.into()) + && path.contains(&"WriteCpuInformationFailed".to_string()) + { + cpu_info_error_found = true; + } + }); + + assert!(stop_process_error_found); + assert!(missing_auxv_error_found); + assert!(thread_name_error_found); + assert!(suspend_thread_error_found); + assert!(cpu_info_error_found); } diff --git a/tests/ptrace_dumper.rs b/tests/ptrace_dumper.rs index cdbb6f79..75be7002 100644 --- a/tests/ptrace_dumper.rs +++ b/tests/ptrace_dumper.rs @@ -12,6 +12,7 @@ use { mem::size_of, os::unix::process::ExitStatusExt, ptr, + sync::Mutex, }, }; @@ -35,13 +36,22 @@ macro_rules! assert_no_soft_errors(($n: ident, $e: expr) => {{ __result }}); +static GLOBAL_LOCK: Mutex<()> = Mutex::new(()); + +macro_rules! one_at_a_time(() => { + let _guard_ = GLOBAL_LOCK.lock().unwrap_or_else(|e| e.into_inner()); +}); + #[test] fn setup() { + one_at_a_time!(); + spawn_child("setup", &[]); } #[test] fn thread_list_from_child() { + one_at_a_time!(); // Child spawns and looks in the parent (== this process) for its own thread-ID let (tx, rx) = std::sync::mpsc::sync_channel(1); @@ -100,6 +110,8 @@ fn thread_list_from_child() { #[test] fn thread_list_from_parent() { + one_at_a_time!(); + let num_of_threads = 5; let mut child = start_child_and_wait_for_threads(num_of_threads); let pid = child.id() as i32; @@ -175,17 +187,23 @@ fn thread_list_from_parent() { #[test] // Ensure that the linux-gate VDSO is included in the mapping list. fn mappings_include_linux_gate() { + one_at_a_time!(); + spawn_child("mappings_include_linux_gate", &[]); } #[test] fn linux_gate_mapping_id() { + one_at_a_time!(); + disabled_on_ci_and_android!(); spawn_child("linux_gate_mapping_id", &[]); } #[test] fn merges_mappings() { + one_at_a_time!(); + let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) }; assert!(page_size > 0); let page_size = usize::try_from(page_size).unwrap(); @@ -238,6 +256,8 @@ fn merges_mappings() { // Ensure that the linux-gate VDSO is included in the mapping list. #[test] fn file_id() { + one_at_a_time!(); + disabled_on_ci_and_android!(); spawn_child("file_id", &[]); } @@ -245,6 +265,8 @@ fn file_id() { #[cfg(not(target_os = "android"))] #[test] fn finds_mappings() { + one_at_a_time!(); + spawn_child( "find_mappings", &[ @@ -256,6 +278,8 @@ fn finds_mappings() { #[test] fn copies_from_process_self() { + one_at_a_time!(); + disabled_on_ci_and_android!(); let stack_var: libc::c_long = 0x11223344; @@ -272,6 +296,8 @@ fn copies_from_process_self() { // Ensures that we sanitize the stack properly #[test] fn sanitizes_stack_copies() { + one_at_a_time!(); + let num_of_threads = 1; let mut child = start_child_and_return(&["spawn_alloc_wait"]); let pid = child.id() as i32;