diff --git a/src/lib.rs b/src/lib.rs index a1866205..9e843c3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ cfg_if::cfg_if! { ThreadStackCopy, CrashingThreadIpCopy, EnumerateMappingsFromProc, + DebuggerRendezvousNotConsistent, } } diff --git a/src/linux/dso_debug.rs b/src/linux/dso_debug.rs index 75bad39d..ac944bd5 100644 --- a/src/linux/dso_debug.rs +++ b/src/linux/dso_debug.rs @@ -17,6 +17,7 @@ use { }, minidump_format::*, }, + failspot::failspot, plain::Plain, std::{ffi::OsString, os::unix::ffi::OsStringExt}, }; @@ -78,6 +79,8 @@ pub enum RendezvousError { ReadDebuggerRendezvousAddressFailed(#[source] CopyFromProcessError), #[error("unexpected debugger rendezvous version '{0}'")] UnexpectedDebuggerRendezvousVersion(i32), + #[error("the dynamic linker was mid-update (r_state = {0})")] + DebuggerRendezvousNotConsistent(i32), #[error("failed reading link_map entry")] ReadLinkMapEntryFailed(#[source] CopyFromProcessError), #[error("failed reading the name a link_map entry points at")] @@ -241,6 +244,11 @@ pub struct LinkMap { pub(crate) l_prev: usize, } +/// The [`RDebug::r_state`] value stating that the mapping data is reliable. +/// +/// +pub(crate) const RT_CONSISTENT: libc::c_int = 0; + /// Shared object loading information for the debugger. /// /// The Linux dynamic linker fills this in and points the main program's @@ -307,11 +315,21 @@ impl RDebug { let debugger_rendezvous: RDebug = reader .read_pod(address) .map_err(RendezvousError::ReadDebuggerRendezvousAddressFailed)?; + if debugger_rendezvous.r_version != 1 { return Err(RendezvousError::UnexpectedDebuggerRendezvousVersion( debugger_rendezvous.r_version, )); } + + if debugger_rendezvous.r_state != RT_CONSISTENT + || failspot!(DebuggerRendezvousNotConsistent) + { + return Err(RendezvousError::DebuggerRendezvousNotConsistent( + debugger_rendezvous.r_state, + )); + } + Ok(debugger_rendezvous) } diff --git a/tests/linux_module_list_failspot.rs b/tests/linux_module_list_failspot.rs index a96c2ce8..3b14082a 100644 --- a/tests/linux_module_list_failspot.rs +++ b/tests/linux_module_list_failspot.rs @@ -11,6 +11,7 @@ use { common::*, + memmap2::Mmap, minidump::*, minidump_writer::{ FailSpotName, @@ -20,25 +21,30 @@ use { mod common; -#[test] -fn module_list_procmaps_fallback_to_rendezvous() { - let mut failspot_client = FailSpotName::testing_client(); - failspot_client.set_enabled(FailSpotName::EnumerateMappingsFromProc, true); - +fn make_dump_with_source(source: ModuleListSource) -> Minidump<'static, Mmap> { let mut child = start_child_and_wait_for_threads(1); let pid = child.id() as i32; let mut tmpfile = tempfile::Builder::new().tempfile().unwrap(); let mut writer = MinidumpWriterConfig::new(pid, pid); - writer.set_module_list_source(ModuleListSource::ProcMaps); + writer.set_module_list_source(source); writer.write(&mut tmpfile).unwrap(); child.kill().expect("Failed to kill process"); child.wait().expect("Failed to wait on killed process"); - let dump = Minidump::read_path(tmpfile.path()).expect("failed to read minidump"); + Minidump::read_path(tmpfile.path()).expect("failed to read minidump") +} + +#[test] +fn module_list_procmaps_fallback_to_rendezvous() { + let mut failspot_client = FailSpotName::testing_client(); + failspot_client.set_enabled(FailSpotName::EnumerateMappingsFromProc, true); + + let dump = make_dump_with_source(ModuleListSource::ProcMaps); let modules: MinidumpModuleList = dump.get_stream().expect("no module list"); + let names = modules .iter() .map(|module| module.name.clone()) @@ -49,3 +55,22 @@ fn module_list_procmaps_fallback_to_rendezvous() { assert_minidump_contains_soft_error(&dump, "AggregateMappingsFailed"); assert_minidump_contains_soft_error(&dump, "NoModulesInProcessMappings"); } + +#[test] +fn inconsistent_debugger_rendezvous_fallback() { + let mut failspot_client = FailSpotName::testing_client(); + failspot_client.set_enabled(FailSpotName::DebuggerRendezvousNotConsistent, true); + + let dump = make_dump_with_source(ModuleListSource::DebuggerRendezvous); + let modules: MinidumpModuleList = dump.get_stream().expect("no module list"); + + let names = modules + .iter() + .map(|module| module.name.clone()) + .collect::>(); + + // Check that we have some valid data. + assert!(!names.is_empty(), "empty module list"); + assert!(names.iter().any(|name| name.contains("libc"))); + assert_minidump_contains_soft_error(&dump, "DebuggerRendezvousNotConsistent"); +}