summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter')
-rw-r--r--toolkit/crashreporter/client/crashreporter.cpp33
-rw-r--r--toolkit/crashreporter/client/moz.build1
-rw-r--r--toolkit/crashreporter/docs/index.rst5
-rw-r--r--toolkit/crashreporter/mozwer-rust/Cargo.toml1
-rw-r--r--toolkit/crashreporter/mozwer-rust/lib.rs315
-rw-r--r--toolkit/crashreporter/mozwer-rust/moz.build1
-rw-r--r--toolkit/crashreporter/nsExceptionHandler.cpp27
-rw-r--r--toolkit/crashreporter/nsExceptionHandler.h13
-rw-r--r--toolkit/crashreporter/process_reader/Cargo.toml18
-rw-r--r--toolkit/crashreporter/process_reader/src/error.rs30
-rw-r--r--toolkit/crashreporter/process_reader/src/lib.rs13
-rw-r--r--toolkit/crashreporter/process_reader/src/process_reader.rs6
-rw-r--r--toolkit/crashreporter/process_reader/src/process_reader/windows.rs226
13 files changed, 490 insertions, 199 deletions
diff --git a/toolkit/crashreporter/client/crashreporter.cpp b/toolkit/crashreporter/client/crashreporter.cpp
index 4426e6446c..a14ea14fbc 100644
--- a/toolkit/crashreporter/client/crashreporter.cpp
+++ b/toolkit/crashreporter/client/crashreporter.cpp
@@ -753,16 +753,31 @@ int main(int argc, char** argv) {
vector<string> restartArgs;
- ostringstream paramName;
- int i = 0;
- paramName << "MOZ_CRASHREPORTER_RESTART_ARG_" << i++;
- const char* param = getenv(paramName.str().c_str());
- while (param && *param) {
- restartArgs.push_back(param);
-
- paramName.str("");
+ if (!extraData.isMember("WindowsErrorReporting")) {
+ // We relaunch the application associated with the client, but only when
+ // we encountered a crash caught by the exception handler. Crashes handled
+ // by WER are prevented from directly restarting the application.
+ string programPath = GetProgramPath(MOZ_APP_NAME);
+#ifndef XP_WIN
+ const char* moz_app_launcher = getenv("MOZ_APP_LAUNCHER");
+ if (moz_app_launcher) {
+ programPath = moz_app_launcher;
+ }
+#endif // XP_WIN
+
+ restartArgs.push_back(programPath);
+
+ ostringstream paramName;
+ int i = 1;
paramName << "MOZ_CRASHREPORTER_RESTART_ARG_" << i++;
- param = getenv(paramName.str().c_str());
+ const char* param = getenv(paramName.str().c_str());
+ while (param && *param) {
+ restartArgs.push_back(param);
+
+ paramName.str("");
+ paramName << "MOZ_CRASHREPORTER_RESTART_ARG_" << i++;
+ param = getenv(paramName.str().c_str());
+ }
}
// allow override of the server url via environment variable
diff --git a/toolkit/crashreporter/client/moz.build b/toolkit/crashreporter/client/moz.build
index f678ca1cd6..82e19b8637 100644
--- a/toolkit/crashreporter/client/moz.build
+++ b/toolkit/crashreporter/client/moz.build
@@ -85,6 +85,7 @@ if CONFIG["OS_ARCH"] == "Linux" or CONFIG["OS_ARCH"] == "SunOS":
"Throbber-small.gif",
]
+DEFINES["MOZ_APP_NAME"] = '"%s"' % CONFIG["MOZ_APP_NAME"]
DEFINES["BIN_SUFFIX"] = '"%s"' % CONFIG["BIN_SUFFIX"]
RCINCLUDE = "crashreporter.rc"
diff --git a/toolkit/crashreporter/docs/index.rst b/toolkit/crashreporter/docs/index.rst
index 57b4c3a5e5..f2444a75c6 100644
--- a/toolkit/crashreporter/docs/index.rst
+++ b/toolkit/crashreporter/docs/index.rst
@@ -248,8 +248,9 @@ Environment variables used internally
- ``MOZ_CRASHREPORTER_PING_DIRECTORY`` - Path of the directory holding the
pending crash ping files.
- ``MOZ_CRASHREPORTER_RESTART_ARG_<n>`` - Each of these variable specifies one
- of the arguments that had been passed to the application, the crash reporter
- client uses them for restarting it.
+ of the arguments that had been passed to the application, starting with the
+ first after the executable, the crash reporter client uses them for restarting
+ it.
- ``MOZ_CRASHREPORTER_RESTART_XUL_APP_FILE`` - If a XUL app file was specified
when starting the app it has to be stored in this variable so that the crash
reporter client can restart the application.
diff --git a/toolkit/crashreporter/mozwer-rust/Cargo.toml b/toolkit/crashreporter/mozwer-rust/Cargo.toml
index 6b960d827d..ae45d20360 100644
--- a/toolkit/crashreporter/mozwer-rust/Cargo.toml
+++ b/toolkit/crashreporter/mozwer-rust/Cargo.toml
@@ -8,6 +8,7 @@ license = "MPL-2.0"
[dependencies]
libc = "0.2.0"
mozilla-central-workspace-hack = { path = "../../../build/workspace-hack" }
+process_reader = { path = "../process_reader/" }
rust-ini = "0.10"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }
diff --git a/toolkit/crashreporter/mozwer-rust/lib.rs b/toolkit/crashreporter/mozwer-rust/lib.rs
index fab6c4f85c..16889a0cce 100644
--- a/toolkit/crashreporter/mozwer-rust/lib.rs
+++ b/toolkit/crashreporter/mozwer-rust/lib.rs
@@ -7,17 +7,18 @@ extern crate winapi;
use ini::Ini;
use libc::time;
+use process_reader::ProcessReader;
use serde::Serialize;
use serde_json::ser::to_writer;
use std::convert::TryInto;
use std::ffi::OsString;
-use std::fs::{read_to_string, File};
+use std::fs::{read_to_string, DirBuilder, File};
use std::io::{BufRead, BufReader, Write};
-use std::mem::{size_of, zeroed};
+use std::mem::{size_of, transmute, zeroed};
use std::os::windows::ffi::{OsStrExt, OsStringExt};
-use std::os::windows::io::AsRawHandle;
+use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle, RawHandle};
use std::path::{Path, PathBuf};
-use std::ptr::{addr_of_mut, null, null_mut};
+use std::ptr::{addr_of, null, null_mut};
use std::slice::from_raw_parts;
use uuid::Uuid;
use winapi::shared::basetsd::{SIZE_T, ULONG_PTR};
@@ -26,17 +27,21 @@ use winapi::shared::minwindef::{
};
use winapi::shared::ntdef::{NTSTATUS, STRING, UNICODE_STRING};
use winapi::shared::ntstatus::STATUS_SUCCESS;
-use winapi::shared::winerror::{E_UNEXPECTED, S_OK};
+use winapi::shared::winerror::{ERROR_INSUFFICIENT_BUFFER, ERROR_SUCCESS, E_UNEXPECTED, S_OK};
use winapi::um::combaseapi::CoTaskMemFree;
+use winapi::um::errhandlingapi::{GetLastError, SetLastError};
use winapi::um::handleapi::CloseHandle;
use winapi::um::knownfolders::FOLDERID_RoamingAppData;
-use winapi::um::memoryapi::{ReadProcessMemory, WriteProcessMemory};
+use winapi::um::memoryapi::{VirtualAllocEx, VirtualFreeEx, WriteProcessMemory};
use winapi::um::minwinbase::LPTHREAD_START_ROUTINE;
use winapi::um::processthreadsapi::{
CreateProcessW, CreateRemoteThread, GetProcessId, GetProcessTimes, GetThreadId, OpenProcess,
- TerminateProcess, PROCESS_INFORMATION, STARTUPINFOW,
+ OpenProcessToken, TerminateProcess, PROCESS_INFORMATION, STARTUPINFOW,
};
use winapi::um::psapi::K32GetModuleFileNameExW;
+use winapi::um::securitybaseapi::{
+ GetSidSubAuthority, GetSidSubAuthorityCount, GetTokenInformation, IsTokenRestricted,
+};
use winapi::um::shlobj::SHGetKnownFolderPath;
use winapi::um::synchapi::WaitForSingleObject;
use winapi::um::winbase::{
@@ -44,33 +49,22 @@ use winapi::um::winbase::{
WAIT_OBJECT_0,
};
use winapi::um::winnt::{
- VerSetConditionMask, CONTEXT, DWORDLONG, EXCEPTION_POINTERS, EXCEPTION_RECORD, HANDLE, HRESULT,
- LIST_ENTRY, LPOSVERSIONINFOEXW, OSVERSIONINFOEXW, PCWSTR, PEXCEPTION_POINTERS,
- PROCESS_ALL_ACCESS, PVOID, PWSTR, VER_GREATER_EQUAL, VER_MAJORVERSION, VER_MINORVERSION,
- VER_SERVICEPACKMAJOR, VER_SERVICEPACKMINOR,
+ TokenIntegrityLevel, VerSetConditionMask, CONTEXT, DWORDLONG, EXCEPTION_POINTERS,
+ EXCEPTION_RECORD, HANDLE, HRESULT, LIST_ENTRY, LPOSVERSIONINFOEXW, MEM_COMMIT, MEM_RELEASE,
+ MEM_RESERVE, OSVERSIONINFOEXW, PAGE_READWRITE, PCWSTR, PEXCEPTION_POINTERS, PROCESS_ALL_ACCESS,
+ PVOID, PWSTR, SECURITY_MANDATORY_MEDIUM_RID, TOKEN_MANDATORY_LABEL, TOKEN_QUERY,
+ VER_GREATER_EQUAL, VER_MAJORVERSION, VER_MINORVERSION, VER_SERVICEPACKMAJOR,
+ VER_SERVICEPACKMINOR,
};
use winapi::STRUCT;
/* The following struct must be kept in sync with the identically named one in
- * nsExceptionHandler.h. There is one copy of this structure for every child
- * process and they are all stored within the main process'. WER will use it to
- * communicate with the main process when a child process is encountered. */
+ * nsExceptionHandler.h. WER will use it to communicate with the main process
+ * when a child process is encountered. */
#[repr(C)]
struct WindowsErrorReportingData {
- wer_notify_proc: LPTHREAD_START_ROUTINE,
child_pid: DWORD,
minidump_name: [u8; 40],
- oom_allocation_size: usize,
-}
-
-/* The following struct must be kept in sync with the identically named one in
- * nsExceptionHandler.h. A copy of this is stored in every process and a pointer
- * to it is passed to the runtime exception module. We will read it to gather
- * information about the crashed process. */
-#[repr(C)]
-struct InProcessWindowsErrorReportingData {
- process_type: u32,
- oom_allocation_size_ptr: *mut usize,
}
// This value comes from GeckoProcessTypes.h
@@ -141,108 +135,95 @@ fn out_of_process_exception_event_callback(
let process = unsafe { (*exception_information).hProcess };
let application_info = ApplicationInformation::from_process(process)?;
- let wer_data = read_from_process::<InProcessWindowsErrorReportingData>(
- process,
- context as *mut InProcessWindowsErrorReportingData,
- )?;
- let process = unsafe { (*exception_information).hProcess };
+ let process_type: u32 = (context as usize).try_into().map_err(|_| ())?;
let startup_time = get_startup_time(process)?;
- let oom_allocation_size = get_oom_allocation_size(process, &wer_data);
- let crash_report = CrashReport::new(&application_info, startup_time, oom_allocation_size);
+ let crash_report = CrashReport::new(&application_info, startup_time);
crash_report.write_minidump(exception_information)?;
- if wer_data.process_type == MAIN_PROCESS_TYPE {
- handle_main_process_crash(crash_report, process, &application_info)
+ if process_type == MAIN_PROCESS_TYPE {
+ match is_sandboxed_process(process) {
+ Ok(false) => handle_main_process_crash(crash_report, &application_info),
+ _ => {
+ // The parent process should never be sandboxed, bail out so the
+ // process which is impersonating it gets killed right away. Also
+ // bail out if is_sandboxed_process() failed while checking.
+ Ok(())
+ }
+ }
} else {
handle_child_process_crash(crash_report, process)
}
}
+fn get_parent_process(process: HANDLE) -> Result<HANDLE, ()> {
+ let pbi = get_process_basic_information(process)?;
+ get_process_handle(pbi.InheritedFromUniqueProcessId as u32)
+}
+
fn handle_main_process_crash(
crash_report: CrashReport,
- process: HANDLE,
application_information: &ApplicationInformation,
) -> Result<(), ()> {
crash_report.write_extra_file()?;
crash_report.write_event_file()?;
- let mut environment = read_environment_block(process)?;
- launch_crash_reporter_client(
- &application_information.install_path,
- &mut environment,
- &crash_report,
- );
+ launch_crash_reporter_client(&application_information.install_path, &crash_report);
Ok(())
}
fn handle_child_process_crash(crash_report: CrashReport, child_process: HANDLE) -> Result<(), ()> {
- let command_line = read_command_line(child_process)?;
- let (parent_pid, data_ptr) = parse_child_data(&command_line)?;
-
- let parent_process = get_process_handle(parent_pid)?;
- let mut wer_data: WindowsErrorReportingData = read_from_process(parent_process, data_ptr)?;
- wer_data.child_pid = get_process_id(child_process)?;
- wer_data.minidump_name = crash_report.get_minidump_name();
- wer_data.oom_allocation_size = crash_report.oom_allocation_size;
- let wer_notify_proc = wer_data.wer_notify_proc;
- write_to_process(parent_process, wer_data, data_ptr)?;
- notify_main_process(parent_process, wer_notify_proc, data_ptr)
-}
-
-fn read_from_process<T>(process: HANDLE, data_ptr: *mut T) -> Result<T, ()> {
- let mut data: T = unsafe { zeroed() };
- let res = unsafe {
- ReadProcessMemory(
- process,
- data_ptr as *mut _,
- addr_of_mut!(data) as *mut _,
- size_of::<T>() as SIZE_T,
- null_mut(),
- )
+ let parent_process = get_parent_process(child_process)?;
+ let process_reader = ProcessReader::new(parent_process).map_err(|_e| ())?;
+ let wer_notify_proc = process_reader
+ .find_section("xul.dll", "mozwerpt")
+ .map_err(|_e| ())?;
+ let wer_notify_proc = unsafe { transmute::<_, LPTHREAD_START_ROUTINE>(wer_notify_proc) };
+
+ let wer_data = WindowsErrorReportingData {
+ child_pid: get_process_id(child_process)?,
+ minidump_name: crash_report.get_minidump_name(),
};
-
- bool_ok_or_err(res, data)
+ let address = copy_object_into_process(parent_process, wer_data)?;
+ notify_main_process(parent_process, wer_notify_proc, address)
}
-fn read_array_from_process<T: Clone + Default>(
- process: HANDLE,
- data_ptr: LPVOID,
- count: usize,
-) -> Result<Vec<T>, ()> {
- let mut array = vec![Default::default(); count];
- let size = size_of::<T>() as SIZE_T;
- let size = size.checked_mul(count).ok_or(())?;
- let res = unsafe {
- ReadProcessMemory(
+fn copy_object_into_process<T>(process: HANDLE, data: T) -> Result<*mut T, ()> {
+ let address = unsafe {
+ VirtualAllocEx(
process,
- data_ptr,
- array.as_mut_ptr() as *mut _,
- size,
null_mut(),
+ size_of::<T>(),
+ MEM_RESERVE | MEM_COMMIT,
+ PAGE_READWRITE,
)
};
- bool_ok_or_err(res, array)
-}
+ if address.is_null() {
+ return Err(());
+ }
-fn write_to_process<T>(process: HANDLE, mut data: T, data_ptr: *mut T) -> Result<(), ()> {
let res = unsafe {
WriteProcessMemory(
process,
- data_ptr as LPVOID,
- addr_of_mut!(data) as *mut _,
- size_of::<T>() as SIZE_T,
+ address,
+ addr_of!(data) as *const _,
+ size_of::<T>(),
null_mut(),
)
};
- bool_ok_or_err(res, ())
+ if res == 0 {
+ unsafe { VirtualFreeEx(process, address as *mut _, 0, MEM_RELEASE) };
+ Err(())
+ } else {
+ Ok(address as *mut T)
+ }
}
fn notify_main_process(
process: HANDLE,
wer_notify_proc: LPTHREAD_START_ROUTINE,
- data_ptr: *mut WindowsErrorReportingData,
+ address: *mut WindowsErrorReportingData,
) -> Result<(), ()> {
let thread = unsafe {
CreateRemoteThread(
@@ -250,24 +231,29 @@ fn notify_main_process(
null_mut(),
0,
wer_notify_proc,
- data_ptr as LPVOID,
+ address as LPVOID,
0,
null_mut(),
)
};
if thread == null_mut() {
+ unsafe { VirtualFreeEx(process, address as *mut _, 0, MEM_RELEASE) };
return Err(());
}
+ // From this point on the memory pointed to by address is owned by the
+ // thread we've created in the main process, so we don't free it.
+
+ let thread = unsafe { OwnedHandle::from_raw_handle(thread as RawHandle) };
+
// Don't wait forever as we want the process to get killed eventually
- let res = unsafe { WaitForSingleObject(thread, 5000) };
+ let res = unsafe { WaitForSingleObject(thread.as_raw_handle() as HANDLE, 5000) };
if res != WAIT_OBJECT_0 {
return Err(());
}
- let res = unsafe { CloseHandle(thread) };
- bool_ok_or_err(res, ())
+ Ok(())
}
fn get_startup_time(process: HANDLE) -> Result<u64, ()> {
@@ -294,24 +280,6 @@ fn get_startup_time(process: HANDLE) -> Result<u64, ()> {
Ok((start_time_in_ticks / windows_tick) - sec_to_unix_epoch)
}
-fn get_oom_allocation_size(
- process: HANDLE,
- wer_data: &InProcessWindowsErrorReportingData,
-) -> usize {
- read_from_process(process, wer_data.oom_allocation_size_ptr).unwrap_or(0)
-}
-
-fn parse_child_data(command_line: &str) -> Result<(DWORD, *mut WindowsErrorReportingData), ()> {
- let mut itr = command_line.rsplit(' ');
- let address = itr.nth(1).ok_or(())?;
- let address = usize::from_str_radix(address, 16).map_err(|_err| (()))?;
- let address = address as *mut WindowsErrorReportingData;
- let parent_pid = itr.nth(2).ok_or(())?;
- let parent_pid = u32::from_str_radix(parent_pid, 10).map_err(|_err| (()))?;
-
- Ok((parent_pid, address))
-}
-
fn get_process_id(process: HANDLE) -> Result<DWORD, ()> {
match unsafe { GetProcessId(process) } {
0 => Err(()),
@@ -328,11 +296,7 @@ fn get_process_handle(pid: DWORD) -> Result<HANDLE, ()> {
}
}
-fn launch_crash_reporter_client(
- install_path: &Path,
- environment: &mut Vec<u16>,
- crash_report: &CrashReport,
-) {
+fn launch_crash_reporter_client(install_path: &Path, crash_report: &CrashReport) {
// Prepare the command line
let client_path = install_path.join("crashreporter.exe");
@@ -357,7 +321,7 @@ fn launch_crash_reporter_client(
null_mut(),
FALSE,
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
- environment.as_mut_ptr() as *mut _,
+ null_mut(),
null_mut(),
&mut si,
&mut pi,
@@ -422,8 +386,6 @@ struct Annotations {
BuildID: String,
CrashTime: String,
InstallTime: String,
- #[serde(skip_serializing_if = "Option::is_none")]
- OOMAllocationSize: Option<String>,
ProductID: String,
ProductName: String,
ReleaseChannel: String,
@@ -443,18 +405,11 @@ impl Annotations {
install_time: String,
crash_time: u64,
startup_time: u64,
- oom_allocation_size: usize,
) -> Annotations {
- let oom_allocation_size = if oom_allocation_size != 0 {
- Some(oom_allocation_size.to_string())
- } else {
- None
- };
Annotations {
BuildID: application_data.build_id.clone(),
CrashTime: crash_time.to_string(),
InstallTime: install_time,
- OOMAllocationSize: oom_allocation_size,
ProductID: application_data.product_id.clone(),
ProductName: application_data.name.clone(),
ReleaseChannel: release_channel,
@@ -487,7 +442,8 @@ impl ApplicationInformation {
let install_time = ApplicationInformation::get_install_time(
&crash_reports_dir,
&application_data.build_id,
- )?;
+ )
+ .unwrap_or("0".to_string());
Ok(ApplicationInformation {
install_path,
@@ -571,15 +527,10 @@ struct CrashReport {
release_channel: String,
annotations: Annotations,
crash_time: u64,
- oom_allocation_size: usize,
}
impl CrashReport {
- fn new(
- application_information: &ApplicationInformation,
- startup_time: u64,
- oom_allocation_size: usize,
- ) -> CrashReport {
+ fn new(application_information: &ApplicationInformation, startup_time: u64) -> CrashReport {
let uuid = Uuid::new_v4()
.as_hyphenated()
.encode_lower(&mut Uuid::encode_buffer())
@@ -592,7 +543,6 @@ impl CrashReport {
application_information.install_time.clone(),
crash_time,
startup_time,
- oom_allocation_size,
);
CrashReport {
uuid,
@@ -600,7 +550,6 @@ impl CrashReport {
release_channel: application_information.release_channel.clone(),
annotations,
crash_time,
- oom_allocation_size,
}
}
@@ -656,6 +605,12 @@ impl CrashReport {
&self,
exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION,
) -> Result<(), ()> {
+ // Make sure that the target directory is present
+ DirBuilder::new()
+ .recursive(true)
+ .create(self.get_pending_path())
+ .map_err(|_e| ())?;
+
let minidump_path = self.get_minidump_path();
let minidump_file = File::create(minidump_path).map_err(|_e| ())?;
let minidump_type: MINIDUMP_TYPE = self.get_minidump_type();
@@ -692,6 +647,12 @@ impl CrashReport {
}
fn write_event_file(&self) -> Result<(), ()> {
+ // Make that the target directory is present
+ DirBuilder::new()
+ .recursive(true)
+ .create(self.get_events_path())
+ .map_err(|_e| ())?;
+
let mut event_file = File::create(self.get_event_file_path()).map_err(|_e| ())?;
writeln!(event_file, "crash.main.3").map_err(|_e| ())?;
writeln!(event_file, "{}", self.crash_time).map_err(|_e| ())?;
@@ -732,28 +693,7 @@ fn bool_ok_or_err<T>(res: BOOL, data: T) -> Result<T, ()> {
}
}
-fn read_environment_block(process: HANDLE) -> Result<Vec<u16>, ()> {
- let upp = read_user_process_parameters(process)?;
-
- // Read the environment
- let buffer = upp.Environment;
- let length = upp.EnvironmentSize;
- let count = length as usize / 2;
- read_array_from_process::<u16>(process, buffer, count)
-}
-
-fn read_command_line(process: HANDLE) -> Result<String, ()> {
- let upp = read_user_process_parameters(process)?;
-
- // Read the command-line
- let buffer = upp.CommandLine.Buffer;
- let length = upp.CommandLine.Length;
- let count = (length as usize) / 2;
- let command_line = read_array_from_process::<u16>(process, buffer as *mut _, count)?;
- String::from_utf16(&command_line).map_err(|_err| ())
-}
-
-fn read_user_process_parameters(process: HANDLE) -> Result<RTL_USER_PROCESS_PARAMETERS, ()> {
+fn get_process_basic_information(process: HANDLE) -> Result<PROCESS_BASIC_INFORMATION, ()> {
let mut pbi: PROCESS_BASIC_INFORMATION = unsafe { zeroed() };
let mut length: ULONG = 0;
let result = unsafe {
@@ -770,11 +710,59 @@ fn read_user_process_parameters(process: HANDLE) -> Result<RTL_USER_PROCESS_PARA
return Err(());
}
- // Read the process environment block
- let peb: PEB = read_from_process(process, pbi.PebBaseAddress)?;
+ Ok(pbi)
+}
+
+fn is_sandboxed_process(process: HANDLE) -> Result<bool, ()> {
+ let mut token: HANDLE = null_mut();
+ let res = unsafe { OpenProcessToken(process, TOKEN_QUERY, &mut token as *mut _) };
+
+ if res != TRUE {
+ return Err(());
+ }
+
+ let is_restricted = unsafe { IsTokenRestricted(token) } != FALSE;
+
+ unsafe { SetLastError(ERROR_SUCCESS) };
+ let mut buffer_size: DWORD = 0;
+ let res = unsafe {
+ GetTokenInformation(
+ token,
+ TokenIntegrityLevel,
+ null_mut(),
+ 0,
+ &mut buffer_size as *mut _,
+ )
+ };
+
+ if (res != FALSE) || (unsafe { GetLastError() } != ERROR_INSUFFICIENT_BUFFER) {
+ return Err(());
+ }
+
+ let mut buffer: Vec<u8> = vec![Default::default(); buffer_size as usize];
+ let res = unsafe {
+ GetTokenInformation(
+ token,
+ TokenIntegrityLevel,
+ buffer.as_mut_ptr() as *mut _,
+ buffer_size,
+ &mut buffer_size as *mut _,
+ )
+ };
+
+ if res != TRUE {
+ return Err(());
+ }
+
+ let token_mandatory_label = &unsafe { *(buffer.as_ptr() as *const TOKEN_MANDATORY_LABEL) };
+ let sid = token_mandatory_label.Label.Sid;
+ // We're not checking for errors in the following two calls because these
+ // functions can only fail if provided with an invalid SID and we know the
+ // one we obtained from `GetTokenInformation()` is valid.
+ let sid_subauthority_count = unsafe { *GetSidSubAuthorityCount(sid) - 1u8 };
+ let integrity_level = unsafe { *GetSidSubAuthority(sid, sid_subauthority_count.into()) };
- // Read the user process parameters
- read_from_process::<RTL_USER_PROCESS_PARAMETERS>(process, peb.ProcessParameters)
+ Ok((integrity_level < SECURITY_MANDATORY_MEDIUM_RID as u32) || is_restricted)
}
/******************************************************************************
@@ -979,11 +967,12 @@ STRUCT! {#[allow(non_snake_case)] struct PEB {
pub type PPEB = *mut PEB;
STRUCT! {#[allow(non_snake_case)] struct PROCESS_BASIC_INFORMATION {
- Reserved1: PVOID,
+ ExitStatus: NTSTATUS,
PebBaseAddress: PPEB,
- Reserved2: [PVOID; 2],
+ AffinityMask: SIZE_T,
+ BasePriority: DWORD,
UniqueProcessId: ULONG_PTR,
- Reserved3: PVOID,
+ InheritedFromUniqueProcessId: ULONG_PTR,
}}
ENUM! {enum PROCESSINFOCLASS {
diff --git a/toolkit/crashreporter/mozwer-rust/moz.build b/toolkit/crashreporter/mozwer-rust/moz.build
index dc37e8cced..a9b049ecfe 100644
--- a/toolkit/crashreporter/mozwer-rust/moz.build
+++ b/toolkit/crashreporter/mozwer-rust/moz.build
@@ -5,6 +5,7 @@ OS_LIBS += [
"kernel32",
"ntdll",
"ole32",
+ "psapi",
"shell32",
"userenv",
"ws2_32",
diff --git a/toolkit/crashreporter/nsExceptionHandler.cpp b/toolkit/crashreporter/nsExceptionHandler.cpp
index 534f5ab48c..dacc71bd18 100644
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -2551,17 +2551,11 @@ nsresult SetRestartArgs(int argc, char** argv) {
int i;
nsAutoCString envVar;
char* env;
- char* argv0 = getenv("MOZ_APP_LAUNCHER");
- for (i = 0; i < argc; i++) {
+ for (i = 1; i < argc; i++) {
envVar = "MOZ_CRASHREPORTER_RESTART_ARG_";
envVar.AppendInt(i);
envVar += "=";
- if (argv0 && i == 0) {
- // Is there a request to suppress default binary launcher?
- envVar += argv0;
- } else {
- envVar += argv[i];
- }
+ envVar += argv[i];
// PR_SetEnv() wants the string to be available for the lifetime
// of the app, so dup it here. This conversion is not lossy.
@@ -3609,10 +3603,20 @@ bool FinalizeOrphanedMinidump(uint32_t aChildPid, GeckoProcessType aType,
// Function invoked by the WER runtime exception handler running in an
// external process. This function isn't used anywhere inside Gecko directly
// but rather invoked via CreateRemoteThread() in the main process.
-DWORD WINAPI WerNotifyProc(LPVOID aParameter) {
+
+// Store this global in a section called mozwerpt where we can find it by just
+// looking at the program headers.
+# pragma section("mozwerpt", read, executable, shared)
+
+__declspec(allocate("mozwerpt")) MOZ_EXPORT DWORD WINAPI
+ WerNotifyProc(LPVOID aParameter) {
const WindowsErrorReportingData* werData =
static_cast<const WindowsErrorReportingData*>(aParameter);
+ auto freeParameterOnExit = MakeScopeExit([&aParameter] {
+ VirtualFree(aParameter, sizeof(WindowsErrorReportingData), MEM_RELEASE);
+ });
+
// Hold the mutex until the current dump request is complete, to
// prevent UnsetExceptionHandler() from pulling the rug out from
// under us.
@@ -3639,11 +3643,6 @@ DWORD WINAPI WerNotifyProc(LPVOID aParameter) {
pd->sequence = ++crashSequence;
pd->annotations = MakeUnique<AnnotationTable>();
(*pd->annotations)[Annotation::WindowsErrorReporting] = "1"_ns;
- if (werData->mOOMAllocationSize > 0) {
- char buffer[32] = {};
- XP_STOA(werData->mOOMAllocationSize, buffer);
- (*pd->annotations)[Annotation::OOMAllocationSize] = buffer;
- }
PopulateContentProcessAnnotations(*(pd->annotations));
}
diff --git a/toolkit/crashreporter/nsExceptionHandler.h b/toolkit/crashreporter/nsExceptionHandler.h
index 1212ae2e51..d79996244a 100644
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -173,24 +173,20 @@ nsresult AppendObjCExceptionInfoToAppNotes(void* inException);
nsresult GetSubmitReports(bool* aSubmitReport);
nsresult SetSubmitReports(bool aSubmitReport);
-// Out-of-process crash reporter API.
-
#ifdef XP_WIN
// This data is stored in the parent process, there is one copy for each child
// process. The mChildPid and mMinidumpFile fields are filled by the WER runtime
// exception module when the associated child process crashes.
struct WindowsErrorReportingData {
- // Points to the WerNotifyProc function.
- LPTHREAD_START_ROUTINE mWerNotifyProc;
// PID of the child process that crashed.
DWORD mChildPid;
// Filename of the generated minidump; this is not a 0-terminated string
char mMinidumpFile[40];
- // OOM allocation size for the crash (ignore if zero)
- size_t mOOMAllocationSize;
};
#endif // XP_WIN
+// Out-of-process crash reporter API.
+
// Initializes out-of-process crash reporting. This method must be called
// before the platform-specific notification pipe APIs are called. If called
// from off the main thread, this method will synchronously proxy to the main
@@ -319,11 +315,6 @@ bool CreateNotificationPipeForChild(int* childCrashFd, int* childCrashRemapFd);
#endif // XP_WIN
-// Windows Error Reporting helper
-#if defined(XP_WIN)
-DWORD WINAPI WerNotifyProc(LPVOID aParameter);
-#endif
-
// Child-side API
bool SetRemoteExceptionHandler(
const char* aCrashPipe = nullptr,
diff --git a/toolkit/crashreporter/process_reader/Cargo.toml b/toolkit/crashreporter/process_reader/Cargo.toml
new file mode 100644
index 0000000000..ee85951d5b
--- /dev/null
+++ b/toolkit/crashreporter/process_reader/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "process_reader"
+version = "0.1.0"
+authors = ["Gabriele Svelto"]
+edition = "2018"
+license = "MPL-2.0"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+goblin = { version = "0.6", features = ["elf32", "elf64", "pe32", "pe64"] }
+memoffset = "0.8"
+mozilla-central-workspace-hack = { path = "../../../build/workspace-hack" }
+thiserror = "1.0"
+
+[target."cfg(target_os = \"windows\")".dependencies]
+[dependencies.winapi]
+version = "0.3"
diff --git a/toolkit/crashreporter/process_reader/src/error.rs b/toolkit/crashreporter/process_reader/src/error.rs
new file mode 100644
index 0000000000..af4d803a7a
--- /dev/null
+++ b/toolkit/crashreporter/process_reader/src/error.rs
@@ -0,0 +1,30 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use thiserror::Error;
+
+#[derive(Debug, Error)]
+pub enum ProcessReaderError {
+ #[error("Could not convert address {0}")]
+ ConvertAddressError(#[from] std::num::TryFromIntError),
+ #[cfg(target_os = "windows")]
+ #[error("Cannot enumerate the target process's modules")]
+ EnumProcessModulesError,
+ #[error("goblin failed to parse a module")]
+ GoblinError(#[from] goblin::error::Error),
+ #[error("Address was out of bounds")]
+ InvalidAddress,
+ #[error("Could not read from the target process address space")]
+ ReadFromProcessError(#[from] ReadError),
+ #[cfg(target_os = "windows")]
+ #[error("Section was not found")]
+ SectionNotFound,
+}
+
+#[derive(Debug, Error)]
+pub enum ReadError {
+ #[cfg(target_os = "windows")]
+ #[error("ReadProcessMemory failed")]
+ ReadProcessMemoryError,
+}
diff --git a/toolkit/crashreporter/process_reader/src/lib.rs b/toolkit/crashreporter/process_reader/src/lib.rs
new file mode 100644
index 0000000000..20bdad5207
--- /dev/null
+++ b/toolkit/crashreporter/process_reader/src/lib.rs
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#[cfg(target_os = "windows")]
+type ProcessHandle = winapi::um::winnt::HANDLE;
+
+pub struct ProcessReader {
+ process: ProcessHandle,
+}
+
+mod error;
+mod process_reader;
diff --git a/toolkit/crashreporter/process_reader/src/process_reader.rs b/toolkit/crashreporter/process_reader/src/process_reader.rs
new file mode 100644
index 0000000000..1473aafa09
--- /dev/null
+++ b/toolkit/crashreporter/process_reader/src/process_reader.rs
@@ -0,0 +1,6 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#[cfg(target_os = "windows")]
+mod windows;
diff --git a/toolkit/crashreporter/process_reader/src/process_reader/windows.rs b/toolkit/crashreporter/process_reader/src/process_reader/windows.rs
new file mode 100644
index 0000000000..c3d44c1a16
--- /dev/null
+++ b/toolkit/crashreporter/process_reader/src/process_reader/windows.rs
@@ -0,0 +1,226 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use std::{
+ convert::TryInto,
+ ffi::OsString,
+ mem::{size_of, MaybeUninit},
+ os::windows::ffi::OsStringExt,
+ ptr::null_mut,
+};
+
+use winapi::{
+ shared::minwindef::{FALSE, HMODULE, MAX_PATH},
+ um::{
+ memoryapi::ReadProcessMemory,
+ psapi::{GetModuleBaseNameW, K32EnumProcessModules, K32GetModuleInformation, MODULEINFO},
+ },
+};
+
+use crate::{
+ error::{ProcessReaderError, ReadError},
+ ProcessHandle, ProcessReader,
+};
+
+impl ProcessReader {
+ pub fn new(process: ProcessHandle) -> Result<ProcessReader, ProcessReaderError> {
+ Ok(ProcessReader { process })
+ }
+
+ pub fn find_section(
+ &self,
+ module_name: &str,
+ section_name: &str,
+ ) -> Result<usize, ProcessReaderError> {
+ let modules = self.get_module_list()?;
+
+ modules
+ .iter()
+ .filter(|&&module| {
+ let name = self.get_module_name(module);
+ // Crude way of mimicking Windows lower-case comparisons but
+ // sufficient for our use-cases.
+ if let Some(name) = name {
+ name.eq_ignore_ascii_case(module_name)
+ } else {
+ false
+ }
+ })
+ .find_map(|&module| {
+ self.get_module_info(module).and_then(|info| {
+ self.find_section_in_module(
+ section_name,
+ info.lpBaseOfDll as usize,
+ info.SizeOfImage as usize,
+ )
+ .ok()
+ })
+ })
+ .ok_or(ProcessReaderError::InvalidAddress)
+ }
+
+ fn get_module_list(&self) -> Result<Vec<HMODULE>, ProcessReaderError> {
+ let mut module_num: usize = 100;
+ let mut required_buffer_size: u32 = 0;
+ let mut module_array = Vec::<HMODULE>::with_capacity(module_num);
+
+ loop {
+ let buffer_size: u32 = (module_num * size_of::<HMODULE>()).try_into()?;
+ let res = unsafe {
+ K32EnumProcessModules(
+ self.process,
+ module_array.as_mut_ptr() as *mut _,
+ buffer_size,
+ &mut required_buffer_size as *mut _,
+ )
+ };
+
+ module_num = required_buffer_size as usize / size_of::<HMODULE>();
+
+ if res == 0 {
+ if required_buffer_size > buffer_size {
+ module_array = Vec::<HMODULE>::with_capacity(module_num);
+ } else {
+ return Err(ProcessReaderError::EnumProcessModulesError);
+ }
+ } else {
+ break;
+ }
+ }
+
+ // SAFETY: module_array has been filled by K32EnumProcessModules()
+ unsafe {
+ module_array.set_len(module_num);
+ };
+
+ Ok(module_array)
+ }
+
+ fn get_module_name(&self, module: HMODULE) -> Option<String> {
+ let mut path: [u16; MAX_PATH as usize] = [0; MAX_PATH as usize];
+ let res = unsafe {
+ GetModuleBaseNameW(
+ self.process,
+ module,
+ (&mut path).as_mut_ptr(),
+ MAX_PATH.try_into().unwrap(),
+ )
+ };
+
+ if res == 0 {
+ None
+ } else {
+ let name = OsString::from_wide(&path[0..res as usize]);
+ let name = name.to_str()?;
+ Some(name.to_string())
+ }
+ }
+
+ fn get_module_info(&self, module: HMODULE) -> Option<MODULEINFO> {
+ let mut info: MaybeUninit<MODULEINFO> = MaybeUninit::uninit();
+ let res = unsafe {
+ K32GetModuleInformation(
+ self.process,
+ module,
+ info.as_mut_ptr(),
+ size_of::<MODULEINFO>() as u32,
+ )
+ };
+
+ if res == 0 {
+ None
+ } else {
+ let info = unsafe { info.assume_init() };
+ Some(info)
+ }
+ }
+
+ fn find_section_in_module(
+ &self,
+ section_name: &str,
+ module_address: usize,
+ size: usize,
+ ) -> Result<usize, ProcessReaderError> {
+ // We read only the first page from the module, this should be more than
+ // enough to read the header and section list. In the future we might do
+ // this incrementally but for now goblin requires an array to parse
+ // so we can't do it just yet.
+ let page_size = 4096;
+ if size < page_size {
+ // Don't try to read from the target module if it's too small
+ return Err(ProcessReaderError::ReadFromProcessError(
+ ReadError::ReadProcessMemoryError,
+ ));
+ }
+
+ let bytes = self.copy_array(module_address as _, 4096)?;
+ let header = goblin::pe::header::Header::parse(&bytes)?;
+
+ // Skip the PE header so we can parse the sections
+ let optional_header_offset = header.dos_header.pe_pointer as usize
+ + goblin::pe::header::SIZEOF_PE_MAGIC
+ + goblin::pe::header::SIZEOF_COFF_HEADER;
+ let offset =
+ &mut (optional_header_offset + header.coff_header.size_of_optional_header as usize);
+
+ let sections = header.coff_header.sections(&bytes, offset)?;
+
+ for section in sections {
+ if section.name.eq(section_name.as_bytes()) {
+ let address = module_address.checked_add(section.virtual_address as usize);
+ return address.ok_or(ProcessReaderError::InvalidAddress);
+ }
+ }
+
+ Err(ProcessReaderError::SectionNotFound)
+ }
+
+ pub fn copy_object_shallow<T>(&self, src: usize) -> Result<MaybeUninit<T>, ReadError> {
+ let mut object = MaybeUninit::<T>::uninit();
+ let res = unsafe {
+ ReadProcessMemory(
+ self.process,
+ src as _,
+ object.as_mut_ptr() as _,
+ size_of::<T>(),
+ null_mut(),
+ )
+ };
+
+ if res != FALSE {
+ Ok(object)
+ } else {
+ Err(ReadError::ReadProcessMemoryError)
+ }
+ }
+
+ pub fn copy_object<T>(&self, src: usize) -> Result<T, ReadError> {
+ let object = self.copy_object_shallow(src)?;
+ Ok(unsafe { object.assume_init() })
+ }
+
+ pub fn copy_array<T>(&self, src: usize, num: usize) -> Result<Vec<T>, ReadError> {
+ let num_of_bytes = num * size_of::<T>();
+ let mut array: Vec<T> = Vec::with_capacity(num);
+ let res = unsafe {
+ ReadProcessMemory(
+ self.process,
+ src as _,
+ array.as_mut_ptr() as _,
+ num_of_bytes,
+ null_mut(),
+ )
+ };
+
+ if res != FALSE {
+ unsafe {
+ array.set_len(num);
+ }
+
+ Ok(array)
+ } else {
+ Err(ReadError::ReadProcessMemoryError)
+ }
+ }
+}