From db79866ecb23bfd05303df864ff9513af55eef46 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Tue, 16 Dec 2025 12:49:42 +0100 Subject: [PATCH 1/8] Avoid attempting to read os/lsb-release on Android --- src/linux/minidump_writer/mod.rs | 137 +++++++++++-------------------- tests/linux_minidump_writer.rs | 52 ++++++------ 2 files changed, 75 insertions(+), 114 deletions(-) diff --git a/src/linux/minidump_writer/mod.rs b/src/linux/minidump_writer/mod.rs index 8666e689..fc587da1 100644 --- a/src/linux/minidump_writer/mod.rs +++ b/src/linux/minidump_writer/mod.rs @@ -369,91 +369,60 @@ impl MinidumpWriter { let dirent = self.write_memory_info_list_stream(buffer)?; dir_section.write_to_file(buffer, Some(dirent))?; - let dirent = match write_file(buffer, "/proc/cpuinfo") { - Ok(location) => MDRawDirectory { - stream_type: MDStreamType::LinuxCpuInfo as u32, - location, - }, - Err(e) => { - soft_errors.push(WriterError::WriteCpuInfoFailed(e)); - Default::default() - } + let mut proc_root = { + let mut pr = String::with_capacity(24); + use std::fmt::Write; + write!(&mut pr, "/proc/{}/", self.blamed_thread).unwrap(); // infallbile barring OOM + pr }; - dir_section.write_to_file(buffer, Some(dirent))?; - let dirent = match write_file(buffer, &format!("/proc/{}/status", self.blamed_thread)) { - Ok(location) => MDRawDirectory { - stream_type: MDStreamType::LinuxProcStatus as u32, - location, - }, - Err(e) => { - soft_errors.push(WriterError::WriteThreadProcStatusFailed(e)); - Default::default() - } - }; - dir_section.write_to_file(buffer, Some(dirent))?; + macro_rules! file_entry { + (res $write:expr, $kind:ident, $err:ident) => { + let dirent = match $write { + Ok(location) => MDRawDirectory { + stream_type: MDStreamType::$kind as u32, + location, + }, + Err(e) => { + soft_errors.push(WriterError::$err(e)); + Default::default() + } + }; + dir_section.write_to_file(buffer, Some(dirent))?; + }; + ($fname:literal, $kind:ident, $err:ident) => { + let trunc = proc_root.len(); + proc_root.push_str($fname); - let dirent = match write_file(buffer, "/etc/lsb-release") - .or_else(|_| write_file(buffer, "/etc/os-release")) - { - Ok(location) => MDRawDirectory { - stream_type: MDStreamType::LinuxLsbRelease as u32, - location, - }, - Err(e) => { - soft_errors.push(WriterError::WriteOsReleaseInfoFailed(e)); - Default::default() - } - }; - dir_section.write_to_file(buffer, Some(dirent))?; + file_entry!(res write_file(buffer, &proc_root), $kind, $err); - let dirent = match write_file(buffer, &format!("/proc/{}/cmdline", self.blamed_thread)) { - Ok(location) => MDRawDirectory { - stream_type: MDStreamType::LinuxCmdLine as u32, - location, - }, - Err(e) => { - soft_errors.push(WriterError::WriteCommandLineFailed(e)); - Default::default() - } - }; - dir_section.write_to_file(buffer, Some(dirent))?; + proc_root.truncate(trunc); + }; + } - let dirent = match write_file(buffer, &format!("/proc/{}/environ", self.blamed_thread)) { - Ok(location) => MDRawDirectory { - stream_type: MDStreamType::LinuxEnviron as u32, - location, - }, - Err(e) => { - soft_errors.push(WriterError::WriteEnvironmentFailed(e)); - Default::default() - } - }; - dir_section.write_to_file(buffer, Some(dirent))?; + file_entry!( + res write_file(buffer, "/proc/cpuinfo"), + LinuxCpuInfo, + WriteCpuInfoFailed + ); + file_entry!("status", LinuxProcStatus, WriteThreadProcStatusFailed); - let dirent = match write_file(buffer, &format!("/proc/{}/auxv", self.blamed_thread)) { - Ok(location) => MDRawDirectory { - stream_type: MDStreamType::LinuxAuxv as u32, - location, - }, - Err(e) => { - soft_errors.push(WriterError::WriteAuxvFailed(e)); - Default::default() - } - }; - dir_section.write_to_file(buffer, Some(dirent))?; + // Unfortunately neither of these files exist on Android, and there doesn't seem + // to be a way to read equivalent information from elsewhere on the file system + #[cfg(not(target_os = "android"))] + { + file_entry!( + res write_file(buffer, "/etc/lsb-release") + .or_else(|_| write_file(buffer, "/etc/os-release")), + LinuxLsbRelease, + WriteOsReleaseInfoFailed + ); + } - let dirent = match write_file(buffer, &format!("/proc/{}/maps", self.blamed_thread)) { - Ok(location) => MDRawDirectory { - stream_type: MDStreamType::LinuxMaps as u32, - location, - }, - Err(e) => { - soft_errors.push(WriterError::WriteMapsFailed(e)); - Default::default() - } - }; - dir_section.write_to_file(buffer, Some(dirent))?; + file_entry!("cmdline", LinuxCmdLine, WriteCommandLineFailed); + file_entry!("environ", LinuxEnviron, WriteEnvironmentFailed); + file_entry!("auxv", LinuxAuxv, WriteEnvironmentFailed); + file_entry!("maps", LinuxMaps, WriteMapsFailed); let dirent = match dso_debug::write_dso_debug_stream(buffer, self.process_id, &self.auxv) { Ok(dirent) => dirent, @@ -464,17 +433,7 @@ impl MinidumpWriter { }; dir_section.write_to_file(buffer, Some(dirent))?; - let dirent = match write_file(buffer, &format!("/proc/{}/limits", self.blamed_thread)) { - Ok(location) => MDRawDirectory { - stream_type: MDStreamType::MozLinuxLimits as u32, - location, - }, - Err(e) => { - soft_errors.push(WriterError::WriteLimitsFailed(e)); - Default::default() - } - }; - dir_section.write_to_file(buffer, Some(dirent))?; + file_entry!("limits", MozLinuxLimits, WriteLimitsFailed); let dirent = self.write_thread_names_stream(buffer)?; dir_section.write_to_file(buffer, Some(dirent))?; diff --git a/tests/linux_minidump_writer.rs b/tests/linux_minidump_writer.rs index 0c7a1884..0aabfc6c 100644 --- a/tests/linux_minidump_writer.rs +++ b/tests/linux_minidump_writer.rs @@ -127,7 +127,7 @@ contextual_test! { } contextual_test! { - #[ignore] + //#[ignore] fn write_and_read_dump_from_parent(context: Context) { let mut child = start_child_and_return(&["spawn_mmap_wait"]); let pid = child.id() as i32; @@ -208,30 +208,32 @@ contextual_test! { let _: MinidumpThreadList = dump.get_stream().expect("Couldn't find MinidumpThreadList"); let _: MinidumpMemoryList = dump.get_stream().expect("Couldn't find MinidumpMemoryList"); let _: MinidumpSystemInfo = dump.get_stream().expect("Couldn't find MinidumpSystemInfo"); - let _ = dump - .get_raw_stream(LinuxCpuInfo as u32) - .expect("Couldn't find LinuxCpuInfo"); - let _ = dump - .get_raw_stream(LinuxProcStatus as u32) - .expect("Couldn't find LinuxProcStatus"); - let _ = dump - .get_raw_stream(LinuxCmdLine as u32) - .expect("Couldn't find LinuxCmdLine"); - let _ = dump - .get_raw_stream(LinuxEnviron as u32) - .expect("Couldn't find LinuxEnviron"); - let _ = dump - .get_raw_stream(LinuxAuxv as u32) - .expect("Couldn't find LinuxAuxv"); - let _ = dump - .get_raw_stream(LinuxMaps as u32) - .expect("Couldn't find LinuxMaps"); - let _ = dump - .get_raw_stream(LinuxDsoDebug as u32) - .expect("Couldn't find LinuxDsoDebug"); - let _ = dump - .get_raw_stream(MozLinuxLimits as u32) - .expect("Couldn't find MozLinuxLimits"); + + macro_rules! raw { + (get $kind:ident) => {{ + dump + .get_raw_stream($kind as u32) + .expect(concat!("Couldn't find ", stringify!($kind))) + }}; + ($kind:ident) => { + let _ = dump + .get_raw_stream($kind as u32) + .expect(concat!("Couldn't find ", stringify!($kind))); + }; + } + + raw!(LinuxCpuInfo); + raw!(LinuxProcStatus); + raw!(LinuxLsbRelease); + + let cmd_line = raw!(get LinuxCmdLine); + assert!(std::str::from_utf8(cmd_line).expect("cmd line was not utf8").ends_with("\0spawn_mmap_wait\0")); + + raw!(LinuxEnviron); + raw!(LinuxAuxv); + raw!(LinuxMaps); + raw!(LinuxDsoDebug); + raw!(MozLinuxLimits); } } From 5627439eaedda6a1d4b04e95e78dfbb316f1b60a Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Tue, 16 Dec 2025 12:51:17 +0100 Subject: [PATCH 2/8] Ignore test again --- tests/linux_minidump_writer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/linux_minidump_writer.rs b/tests/linux_minidump_writer.rs index 0aabfc6c..ed3202af 100644 --- a/tests/linux_minidump_writer.rs +++ b/tests/linux_minidump_writer.rs @@ -127,7 +127,7 @@ contextual_test! { } contextual_test! { - //#[ignore] + #[ignore] fn write_and_read_dump_from_parent(context: Context) { let mut child = start_child_and_return(&["spawn_mmap_wait"]); let pid = child.id() as i32; From 46b593495b8f3a6961ea983407ff912baa26b9a6 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Tue, 16 Dec 2025 13:00:00 +0100 Subject: [PATCH 3/8] Update CI to avoid deprecated MacOS runner --- .github/workflows/ci.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e6fa82e..cf8e7f7c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ concurrency: jobs: lint: name: Lint - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Install Rust @@ -32,11 +32,11 @@ jobs: strategy: matrix: job: - - { os: ubuntu-22.04, target: x86_64-unknown-linux-gnu, release: true } - - { os: ubuntu-22.04, target: x86_64-unknown-linux-musl } - - { os: windows-2022, target: x86_64-pc-windows-msvc } - - { os: macos-13, target: x86_64-apple-darwin } - - { os: macos-14, target: aarch64-apple-darwin } + - { os: ubuntu-24.04, target: x86_64-unknown-linux-gnu, release: true } + - { os: ubuntu-24.04, target: x86_64-unknown-linux-musl } + - { os: windows-2025, target: x86_64-pc-windows-msvc } + - { os: macos-15-intel, target: x86_64-apple-darwin } + - { os: macos-15, target: aarch64-apple-darwin } steps: - uses: actions/checkout@v4 - name: Install Rust @@ -56,7 +56,7 @@ jobs: cargo test --target ${{ matrix.job.target }} --release install-cross: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: XAMPPRocky/get-github-release@v1 id: cross @@ -73,7 +73,7 @@ jobs: # This job builds and tests non-tier1 targets build_lower_tier: name: Build sources - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: install-cross strategy: matrix: @@ -107,7 +107,7 @@ jobs: # interacts with this job. test-android: name: Test android - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: ANDROID_HOME: /usr/local/lib/android/sdk steps: @@ -139,7 +139,7 @@ jobs: cargo test --target x86_64-linux-android deny-check: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: deny check From 3266374cb6088aa39343a901a9c1e4f145e98826 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Tue, 16 Dec 2025 13:17:18 +0100 Subject: [PATCH 4/8] Fix Windows-only warning --- src/bin/test.rs | 3 +-- src/windows/minidump_writer.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/bin/test.rs b/src/bin/test.rs index 07fa81df..19986a7e 100644 --- a/src/bin/test.rs +++ b/src/bin/test.rs @@ -443,14 +443,13 @@ mod windows { GetThreadContext(GetCurrentThread(), exception_context.as_mut_ptr()); let mut exception_context = exception_context.assume_init(); + exception_record.ExceptionCode = exception_code as _; let exception_ptrs = crash_context::EXCEPTION_POINTERS { ExceptionRecord: &mut exception_record, ContextRecord: &mut exception_context, }; - exception_record.ExceptionCode = exception_code as _; - let exc_ptr_addr = &exception_ptrs as *const _ as usize; println!("{pid} {exc_ptr_addr} {tid} {exception_code:x}"); diff --git a/src/windows/minidump_writer.rs b/src/windows/minidump_writer.rs index 2576b59c..da865989 100644 --- a/src/windows/minidump_writer.rs +++ b/src/windows/minidump_writer.rs @@ -119,14 +119,13 @@ impl MinidumpWriter { }; let mut exception_record: EXCEPTION_RECORD = std::mem::zeroed(); + exception_record.ExceptionCode = exception_code; let exception_ptrs = EXCEPTION_POINTERS { ExceptionRecord: &mut exception_record, ContextRecord: &mut exception_context, }; - exception_record.ExceptionCode = exception_code; - let cc = crash_context::CrashContext { exception_pointers: (&exception_ptrs as *const EXCEPTION_POINTERS).cast(), process_id: std::process::id(), From 7b1abb6ce1a1c747bdbf2b51f891922350dbc9c4 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Tue, 16 Dec 2025 13:40:22 +0100 Subject: [PATCH 5/8] Run Android tests sequentially --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf8e7f7c..38373bae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -135,8 +135,8 @@ jobs: adb root # Copy test helper binary over as a side-effect of running it. cargo run --target x86_64-linux-android --bin test -- nop - # Build and run tests - cargo test --target x86_64-linux-android + # Build and run tests, limited to 1 at a time due to the Android emulator being terrible + cargo test --target x86_64-linux-android -j 1 deny-check: runs-on: ubuntu-24.04 From a4ba1e319a5bfa681827db1f21fd4bd777603e7a Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Wed, 17 Dec 2025 10:53:22 +0100 Subject: [PATCH 6/8] Cleanup --- src/windows/minidump_writer.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/windows/minidump_writer.rs b/src/windows/minidump_writer.rs index da865989..5987e37e 100644 --- a/src/windows/minidump_writer.rs +++ b/src/windows/minidump_writer.rs @@ -118,8 +118,10 @@ impl MinidumpWriter { ec.assume_init() }; - let mut exception_record: EXCEPTION_RECORD = std::mem::zeroed(); - exception_record.ExceptionCode = exception_code; + let mut exception_record = EXCEPTION_RECORD { + ExceptionCode: exception_code, + ..std::mem::zeroed() + }; let exception_ptrs = EXCEPTION_POINTERS { ExceptionRecord: &mut exception_record, From 09df1904b3be784f496957fe66a4536a437e959e Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Wed, 17 Dec 2025 11:12:05 +0100 Subject: [PATCH 7/8] Disable flaky tests for android --- tests/ptrace_dumper.rs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/tests/ptrace_dumper.rs b/tests/ptrace_dumper.rs index d0de9eeb..010af049 100644 --- a/tests/ptrace_dumper.rs +++ b/tests/ptrace_dumper.rs @@ -38,18 +38,18 @@ macro_rules! assert_no_soft_errors(($n: ident, $e: expr) => {{ }}); #[test] -fn test_setup() { +fn setup() { spawn_child("setup", &[]); } #[test] -fn test_thread_list_from_child() { +fn thread_list_from_child() { // Child spawns and looks in the parent (== this process) for its own thread-ID let (tx, rx) = std::sync::mpsc::sync_channel(1); - // // We also spawn another thread that we send a SIGHUP to to ensure that the - // // ptracedumper correctly handles it + // We also spawn another thread that we send a SIGHUP to to ensure that the + // ptracedumper correctly handles it let _thread = std::thread::Builder::new() .name("sighup-thread".into()) .spawn(move || { @@ -101,7 +101,7 @@ fn test_thread_list_from_child() { } #[test] -fn test_thread_list_from_parent() { +fn thread_list_from_parent() { let num_of_threads = 5; let mut child = start_child_and_wait_for_threads(num_of_threads); let pid = child.id() as i32; @@ -175,22 +175,21 @@ fn test_thread_list_from_parent() { // assert_eq!(matching_threads, num_of_threads); } -// #[cfg(not(any(target_arch = "mips", target_arch = "arm-eabi"))] -#[cfg(not(target_arch = "mips"))] +#[cfg(not(any(target_arch = "mips", target_os = "android")))] #[test] // Ensure that the linux-gate VDSO is included in the mapping list. -fn test_mappings_include_linux_gate() { +fn mappings_include_linux_gate() { spawn_child("mappings_include_linux_gate", &[]); } #[test] -fn test_linux_gate_mapping_id() { +fn linux_gate_mapping_id() { disabled_on_ci_and_android!(); spawn_child("linux_gate_mapping_id", &[]); } #[test] -fn test_merged_mappings() { +fn merges_mappings() { let page_size = nix::unistd::sysconf(nix::unistd::SysconfVar::PAGE_SIZE).unwrap(); let page_size = std::num::NonZeroUsize::new(page_size.unwrap() as usize).unwrap(); let map_size = std::num::NonZeroUsize::new(3 * page_size.get()).unwrap(); @@ -238,15 +237,16 @@ fn test_merged_mappings() { ); } -#[test] // Ensure that the linux-gate VDSO is included in the mapping list. -fn test_file_id() { +#[test] +fn file_id() { disabled_on_ci_and_android!(); spawn_child("file_id", &[]); } +#[cfg(not(target_os = "android"))] #[test] -fn test_find_mapping() { +fn finds_mappings() { spawn_child( "find_mappings", &[ @@ -257,7 +257,7 @@ fn test_find_mapping() { } #[test] -fn test_copy_from_process_self() { +fn copies_from_process_self() { disabled_on_ci_and_android!(); let stack_var: libc::c_long = 0x11223344; @@ -271,8 +271,9 @@ fn test_copy_from_process_self() { ); } +// Ensures that we sanitize the stack properly #[test] -fn test_sanitize_stack_copy() { +fn sanitizes_stack_copies() { let num_of_threads = 1; let mut child = start_child_and_return(&["spawn_alloc_wait"]); let pid = child.id() as i32; From cdcd3e74970ecdf010651d641a5466be9337755a Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Wed, 17 Dec 2025 11:12:46 +0100 Subject: [PATCH 8/8] No point limiting this apparently --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38373bae..cf8e7f7c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -135,8 +135,8 @@ jobs: adb root # Copy test helper binary over as a side-effect of running it. cargo run --target x86_64-linux-android --bin test -- nop - # Build and run tests, limited to 1 at a time due to the Android emulator being terrible - cargo test --target x86_64-linux-android -j 1 + # Build and run tests + cargo test --target x86_64-linux-android deny-check: runs-on: ubuntu-24.04