diff --git a/toolkit/components/processtools/nsIProcessToolsService.idl b/toolkit/components/processtools/nsIProcessToolsService.idl index e257b424c421..4298cdddf8fb 100644 --- a/toolkit/components/processtools/nsIProcessToolsService.idl +++ b/toolkit/components/processtools/nsIProcessToolsService.idl @@ -33,6 +33,14 @@ interface nsIProcessToolsService: nsISupports { */ void kill(in unsigned long long pid); + /** + * Crash a process running on this system. Generates a SIGABRT on Linux and + * macOS, and a STATUS_ILLEGAL_INSTRUCTION on Windows. + * + * Does cause a crash report to be generated. + */ + void crash(in unsigned long long pid); + /** * The pid for the current process. */ diff --git a/toolkit/components/processtools/src/lib.rs b/toolkit/components/processtools/src/lib.rs index c1357354ed2e..7b55d03fb5d7 100644 --- a/toolkit/components/processtools/src/lib.rs +++ b/toolkit/components/processtools/src/lib.rs @@ -25,6 +25,19 @@ pub unsafe extern "C" fn new_process_tools_service(result: *mut *const nsIProces RefPtr::new(service.coerce::()).forget(&mut *result); } +#[cfg(target_os = "windows")] +use std::ffi::c_void; + +// We want to generate an exception that can be caught by the exception handler +// when injecting in a remote process, STATUS_ACCESS_VIOLATION seems not to do, +// it but the following code generates a STATUS_ILLEGAL_INSTRUCTION that seems +// to do the trick +#[cfg(target_os = "windows")] +pub unsafe extern "system" fn crash_illegal_instruction(_arg: *mut c_void) -> u32 { + std::ptr::null_mut::().write(1); + 0 +} + // Implementation note: // // We're following the strategy employed by the `kvstore`. @@ -87,6 +100,65 @@ impl ProcessToolsService { } } + // Method `crash` + + xpcom_method!( + crash => Crash(id: u64) + ); + + #[cfg(target_os = "windows")] + pub fn crash(&self, pid: u64) -> Result<(), nsresult> { + let target_proc = unsafe { + winapi::um::processthreadsapi::OpenProcess( + /* dwDesiredAccess */ + winapi::um::winnt::PROCESS_VM_OPERATION + | winapi::um::winnt::PROCESS_CREATE_THREAD + | winapi::um::winnt::PROCESS_QUERY_INFORMATION, + /* bInheritHandle */ 0, + /* dwProcessId */ pid.try_into().unwrap(), + ) + }; + if target_proc.is_null() { + // Could not open process. + return Err(NS_ERROR_NOT_AVAILABLE); + } + + let new_thread = unsafe { + winapi::um::processthreadsapi::CreateRemoteThread( + /* hProcess */ target_proc, + /* lpThreadAttributes */ std::ptr::null_mut(), + /* dwStackSize */ 0, + /* lpStartAddress */ Some(crash_illegal_instruction), + /* lpParameter */ std::ptr::null_mut(), + /* dwCreationFlags */ 0, + /* lpThreadId */ std::ptr::null_mut(), + ) + }; + + // Close handle regardless of success. + let _ = unsafe { + winapi::um::synchapi::WaitForSingleObject(new_thread, winapi::um::winbase::INFINITE); + winapi::um::handleapi::CloseHandle(new_thread); + winapi::um::handleapi::CloseHandle(target_proc); + }; + + if new_thread.is_null() { + return Err(NS_ERROR_FAILURE); + } + Ok(()) + } + + #[cfg(not(target_os = "windows"))] + pub fn crash(&self, pid: u64) -> Result<(), nsresult> { + let pid = pid.try_into().or(Err(NS_ERROR_FAILURE))?; + let result = unsafe { libc::kill(pid, libc::SIGABRT) }; + if result == 0 { + Ok(()) + } else { + Err(NS_ERROR_FAILURE) + } + } + // Attribute `pid` xpcom_method!(