1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5 extern crate ini;
6 extern crate winapi;
7
8 use ini::Ini;
9 use libc::time;
10 use serde::Serialize;
11 use serde_json::ser::to_writer;
12 use std::convert::TryInto;
13 use std::ffi::OsString;
14 use std::fs::{read_to_string, File};
15 use std::io::{BufRead, BufReader, Write};
16 use std::mem::{size_of, zeroed};
17 use std::os::windows::ffi::{OsStrExt, OsStringExt};
18 use std::os::windows::io::AsRawHandle;
19 use std::path::{Path, PathBuf};
20 use std::ptr::{addr_of_mut, null, null_mut};
21 use std::slice::from_raw_parts;
22 use uuid::Uuid;
23 use winapi::shared::basetsd::{SIZE_T, ULONG_PTR};
24 use winapi::shared::minwindef::{
25 BOOL, BYTE, DWORD, FALSE, FILETIME, LPVOID, MAX_PATH, PBOOL, PDWORD, PULONG, TRUE, ULONG, WORD,
26 };
27 use winapi::shared::ntdef::{NTSTATUS, STRING, UNICODE_STRING};
28 use winapi::shared::ntstatus::STATUS_SUCCESS;
29 use winapi::shared::winerror::{E_UNEXPECTED, S_OK};
30 use winapi::um::combaseapi::CoTaskMemFree;
31 use winapi::um::handleapi::CloseHandle;
32 use winapi::um::knownfolders::FOLDERID_RoamingAppData;
33 use winapi::um::memoryapi::{ReadProcessMemory, WriteProcessMemory};
34 use winapi::um::minwinbase::LPTHREAD_START_ROUTINE;
35 use winapi::um::processthreadsapi::{
36 CreateProcessW, CreateRemoteThread, GetProcessId, GetProcessTimes, GetThreadId, OpenProcess,
37 TerminateProcess, PROCESS_INFORMATION, STARTUPINFOW,
38 };
39 use winapi::um::psapi::K32GetModuleFileNameExW;
40 use winapi::um::shlobj::SHGetKnownFolderPath;
41 use winapi::um::synchapi::WaitForSingleObject;
42 use winapi::um::winbase::{
43 VerifyVersionInfoW, CREATE_NO_WINDOW, CREATE_UNICODE_ENVIRONMENT, NORMAL_PRIORITY_CLASS,
44 WAIT_OBJECT_0,
45 };
46 use winapi::um::winnt::{
47 VerSetConditionMask, CONTEXT, DWORDLONG, EXCEPTION_POINTERS, EXCEPTION_RECORD, HANDLE, HRESULT,
48 LIST_ENTRY, LPOSVERSIONINFOEXW, OSVERSIONINFOEXW, PCWSTR, PEXCEPTION_POINTERS,
49 PROCESS_ALL_ACCESS, PVOID, PWSTR, VER_GREATER_EQUAL, VER_MAJORVERSION, VER_MINORVERSION,
50 VER_SERVICEPACKMAJOR, VER_SERVICEPACKMINOR,
51 };
52 use winapi::STRUCT;
53
54 /* The following struct must be kept in sync with the identically named one in
55 * nsExceptionHandler.h. There is one copy of this structure for every child
56 * process and they are all stored within the main process'. WER will use it to
57 * communicate with the main process when a child process is encountered. */
58 #[repr(C)]
59 struct WindowsErrorReportingData {
60 wer_notify_proc: LPTHREAD_START_ROUTINE,
61 child_pid: DWORD,
62 minidump_name: [u8; 40],
63 oom_allocation_size: usize,
64 }
65
66 /* The following struct must be kept in sync with the identically named one in
67 * nsExceptionHandler.h. A copy of this is stored in every process and a pointer
68 * to it is passed to the runtime exception module. We will read it to gather
69 * information about the crashed process. */
70 #[repr(C)]
71 struct InProcessWindowsErrorReportingData {
72 process_type: u32,
73 oom_allocation_size_ptr: *mut usize,
74 }
75
76 // This value comes from GeckoProcessTypes.h
77 static MAIN_PROCESS_TYPE: u32 = 0;
78
79 #[no_mangle]
OutOfProcessExceptionEventCallback( context: PVOID, exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION, b_ownership_claimed: PBOOL, _wsz_event_name: PWSTR, _pch_size: PDWORD, _dw_signature_count: PDWORD, ) -> HRESULT80 pub extern "C" fn OutOfProcessExceptionEventCallback(
81 context: PVOID,
82 exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION,
83 b_ownership_claimed: PBOOL,
84 _wsz_event_name: PWSTR,
85 _pch_size: PDWORD,
86 _dw_signature_count: PDWORD,
87 ) -> HRESULT {
88 let result = out_of_process_exception_event_callback(context, exception_information);
89
90 match result {
91 Ok(_) => {
92 unsafe {
93 // Inform WER that we claim ownership of this crash
94 *b_ownership_claimed = TRUE;
95 // Make sure that the process shuts down
96 TerminateProcess((*exception_information).hProcess, 1);
97 }
98 S_OK
99 }
100 Err(_) => E_UNEXPECTED,
101 }
102 }
103
104 #[no_mangle]
OutOfProcessExceptionEventSignatureCallback( _context: PVOID, _exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION, _w_index: DWORD, _wsz_name: PWSTR, _ch_name: PDWORD, _wsz_value: PWSTR, _ch_value: PDWORD, ) -> HRESULT105 pub extern "C" fn OutOfProcessExceptionEventSignatureCallback(
106 _context: PVOID,
107 _exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION,
108 _w_index: DWORD,
109 _wsz_name: PWSTR,
110 _ch_name: PDWORD,
111 _wsz_value: PWSTR,
112 _ch_value: PDWORD,
113 ) -> HRESULT {
114 S_OK
115 }
116
117 #[no_mangle]
OutOfProcessExceptionEventDebuggerLaunchCallback( _context: PVOID, _exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION, b_is_custom_debugger: PBOOL, _wsz_debugger_launch: PWSTR, _ch_debugger_launch: PDWORD, _b_is_debugger_autolaunch: PBOOL, ) -> HRESULT118 pub extern "C" fn OutOfProcessExceptionEventDebuggerLaunchCallback(
119 _context: PVOID,
120 _exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION,
121 b_is_custom_debugger: PBOOL,
122 _wsz_debugger_launch: PWSTR,
123 _ch_debugger_launch: PDWORD,
124 _b_is_debugger_autolaunch: PBOOL,
125 ) -> HRESULT {
126 unsafe {
127 *b_is_custom_debugger = FALSE;
128 }
129
130 S_OK
131 }
132
out_of_process_exception_event_callback( context: PVOID, exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION, ) -> Result<(), ()>133 fn out_of_process_exception_event_callback(
134 context: PVOID,
135 exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION,
136 ) -> Result<(), ()> {
137 let is_fatal = unsafe { (*exception_information).bIsFatal } != FALSE;
138 if !is_fatal {
139 return Ok(());
140 }
141
142 let process = unsafe { (*exception_information).hProcess };
143 let application_info = ApplicationInformation::from_process(process)?;
144 let wer_data = read_from_process::<InProcessWindowsErrorReportingData>(
145 process,
146 context as *mut InProcessWindowsErrorReportingData,
147 )?;
148 let process = unsafe { (*exception_information).hProcess };
149 let startup_time = get_startup_time(process)?;
150 let oom_allocation_size = get_oom_allocation_size(process, &wer_data);
151 let crash_report = CrashReport::new(&application_info, startup_time, oom_allocation_size);
152 crash_report.write_minidump(exception_information)?;
153 if wer_data.process_type == MAIN_PROCESS_TYPE {
154 handle_main_process_crash(crash_report, process, &application_info)
155 } else {
156 handle_child_process_crash(crash_report, process)
157 }
158 }
159
handle_main_process_crash( crash_report: CrashReport, process: HANDLE, application_information: &ApplicationInformation, ) -> Result<(), ()>160 fn handle_main_process_crash(
161 crash_report: CrashReport,
162 process: HANDLE,
163 application_information: &ApplicationInformation,
164 ) -> Result<(), ()> {
165 crash_report.write_extra_file()?;
166 crash_report.write_event_file()?;
167
168 let mut environment = read_environment_block(process)?;
169 launch_crash_reporter_client(
170 &application_information.install_path,
171 &mut environment,
172 &crash_report,
173 );
174
175 Ok(())
176 }
177
handle_child_process_crash(crash_report: CrashReport, child_process: HANDLE) -> Result<(), ()>178 fn handle_child_process_crash(crash_report: CrashReport, child_process: HANDLE) -> Result<(), ()> {
179 let command_line = read_command_line(child_process)?;
180 let (parent_pid, data_ptr) = parse_child_data(&command_line)?;
181
182 let parent_process = get_process_handle(parent_pid)?;
183 let mut wer_data: WindowsErrorReportingData = read_from_process(parent_process, data_ptr)?;
184 wer_data.child_pid = get_process_id(child_process)?;
185 wer_data.minidump_name = crash_report.get_minidump_name();
186 wer_data.oom_allocation_size = crash_report.oom_allocation_size;
187 let wer_notify_proc = wer_data.wer_notify_proc;
188 write_to_process(parent_process, wer_data, data_ptr)?;
189 notify_main_process(parent_process, wer_notify_proc, data_ptr)
190 }
191
read_from_process<T>(process: HANDLE, data_ptr: *mut T) -> Result<T, ()>192 fn read_from_process<T>(process: HANDLE, data_ptr: *mut T) -> Result<T, ()> {
193 let mut data: T = unsafe { zeroed() };
194 let res = unsafe {
195 ReadProcessMemory(
196 process,
197 data_ptr as *mut _,
198 addr_of_mut!(data) as *mut _,
199 size_of::<T>() as SIZE_T,
200 null_mut(),
201 )
202 };
203
204 bool_ok_or_err(res, data)
205 }
206
read_array_from_process<T: Clone + Default>( process: HANDLE, data_ptr: LPVOID, count: usize, ) -> Result<Vec<T>, ()>207 fn read_array_from_process<T: Clone + Default>(
208 process: HANDLE,
209 data_ptr: LPVOID,
210 count: usize,
211 ) -> Result<Vec<T>, ()> {
212 let mut array = vec![Default::default(); count];
213 let size = size_of::<T>() as SIZE_T;
214 let size = size.checked_mul(count).ok_or(())?;
215 let res = unsafe {
216 ReadProcessMemory(
217 process,
218 data_ptr,
219 array.as_mut_ptr() as *mut _,
220 size,
221 null_mut(),
222 )
223 };
224
225 bool_ok_or_err(res, array)
226 }
227
write_to_process<T>(process: HANDLE, mut data: T, data_ptr: *mut T) -> Result<(), ()>228 fn write_to_process<T>(process: HANDLE, mut data: T, data_ptr: *mut T) -> Result<(), ()> {
229 let res = unsafe {
230 WriteProcessMemory(
231 process,
232 data_ptr as LPVOID,
233 addr_of_mut!(data) as *mut _,
234 size_of::<T>() as SIZE_T,
235 null_mut(),
236 )
237 };
238
239 bool_ok_or_err(res, ())
240 }
241
notify_main_process( process: HANDLE, wer_notify_proc: LPTHREAD_START_ROUTINE, data_ptr: *mut WindowsErrorReportingData, ) -> Result<(), ()>242 fn notify_main_process(
243 process: HANDLE,
244 wer_notify_proc: LPTHREAD_START_ROUTINE,
245 data_ptr: *mut WindowsErrorReportingData,
246 ) -> Result<(), ()> {
247 let thread = unsafe {
248 CreateRemoteThread(
249 process,
250 null_mut(),
251 0,
252 wer_notify_proc,
253 data_ptr as LPVOID,
254 0,
255 null_mut(),
256 )
257 };
258
259 if thread == null_mut() {
260 return Err(());
261 }
262
263 // Don't wait forever as we want the process to get killed eventually
264 let res = unsafe { WaitForSingleObject(thread, 5000) };
265 if res != WAIT_OBJECT_0 {
266 return Err(());
267 }
268
269 let res = unsafe { CloseHandle(thread) };
270 bool_ok_or_err(res, ())
271 }
272
get_startup_time(process: HANDLE) -> Result<u64, ()>273 fn get_startup_time(process: HANDLE) -> Result<u64, ()> {
274 let mut create_time: FILETIME = Default::default();
275 let mut exit_time: FILETIME = Default::default();
276 let mut kernel_time: FILETIME = Default::default();
277 let mut user_time: FILETIME = Default::default();
278 unsafe {
279 if GetProcessTimes(
280 process,
281 &mut create_time as *mut _,
282 &mut exit_time as *mut _,
283 &mut kernel_time as *mut _,
284 &mut user_time as *mut _,
285 ) == 0
286 {
287 return Err(());
288 }
289 }
290 let start_time_in_ticks =
291 ((create_time.dwHighDateTime as u64) << 32) + create_time.dwLowDateTime as u64;
292 let windows_tick: u64 = 10000000;
293 let sec_to_unix_epoch = 11644473600;
294 Ok((start_time_in_ticks / windows_tick) - sec_to_unix_epoch)
295 }
296
get_oom_allocation_size( process: HANDLE, wer_data: &InProcessWindowsErrorReportingData, ) -> usize297 fn get_oom_allocation_size(
298 process: HANDLE,
299 wer_data: &InProcessWindowsErrorReportingData,
300 ) -> usize {
301 read_from_process(process, wer_data.oom_allocation_size_ptr).unwrap_or(0)
302 }
303
parse_child_data(command_line: &str) -> Result<(DWORD, *mut WindowsErrorReportingData), ()>304 fn parse_child_data(command_line: &str) -> Result<(DWORD, *mut WindowsErrorReportingData), ()> {
305 let mut itr = command_line.rsplit(' ');
306 let address = itr.nth(1).ok_or(())?;
307 let address = usize::from_str_radix(address, 16).map_err(|_err| (()))?;
308 let address = address as *mut WindowsErrorReportingData;
309 let parent_pid = itr.nth(2).ok_or(())?;
310 let parent_pid = u32::from_str_radix(parent_pid, 10).map_err(|_err| (()))?;
311
312 Ok((parent_pid, address))
313 }
314
get_process_id(process: HANDLE) -> Result<DWORD, ()>315 fn get_process_id(process: HANDLE) -> Result<DWORD, ()> {
316 match unsafe { GetProcessId(process) } {
317 0 => Err(()),
318 pid => Ok(pid),
319 }
320 }
321
get_process_handle(pid: DWORD) -> Result<HANDLE, ()>322 fn get_process_handle(pid: DWORD) -> Result<HANDLE, ()> {
323 let handle = unsafe { OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid) };
324 if handle != null_mut() {
325 Ok(handle)
326 } else {
327 Err(())
328 }
329 }
330
launch_crash_reporter_client( install_path: &Path, environment: &mut Vec<u16>, crash_report: &CrashReport, )331 fn launch_crash_reporter_client(
332 install_path: &Path,
333 environment: &mut Vec<u16>,
334 crash_report: &CrashReport,
335 ) {
336 // Prepare the command line
337 let client_path = install_path.join("crashreporter.exe");
338
339 let mut cmd_line = OsString::from("\"");
340 cmd_line.push(client_path);
341 cmd_line.push("\" \"");
342 cmd_line.push(crash_report.get_minidump_path());
343 cmd_line.push("\"\0");
344 let mut cmd_line: Vec<u16> = cmd_line.encode_wide().collect();
345
346 let mut pi: PROCESS_INFORMATION = Default::default();
347 let mut si = STARTUPINFOW {
348 cb: size_of::<STARTUPINFOW>().try_into().unwrap(),
349 ..Default::default()
350 };
351
352 unsafe {
353 if CreateProcessW(
354 null_mut(),
355 cmd_line.as_mut_ptr(),
356 null_mut(),
357 null_mut(),
358 FALSE,
359 NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
360 environment.as_mut_ptr() as *mut _,
361 null_mut(),
362 &mut si,
363 &mut pi,
364 ) != 0
365 {
366 CloseHandle(pi.hProcess);
367 CloseHandle(pi.hThread);
368 }
369 }
370 }
371
372 #[derive(Debug)]
373 struct ApplicationData {
374 vendor: Option<String>,
375 name: String,
376 version: String,
377 build_id: String,
378 product_id: String,
379 server_url: String,
380 }
381
382 impl ApplicationData {
load_from_disk(install_path: &Path) -> Result<ApplicationData, ()>383 fn load_from_disk(install_path: &Path) -> Result<ApplicationData, ()> {
384 let ini_path = ApplicationData::get_path(install_path);
385 let conf = Ini::load_from_file(ini_path).map_err(|_e| ())?;
386
387 // Parse the "App" section
388 let app_section = conf.section(Some("App")).ok_or(())?;
389 let vendor = app_section.get("Vendor").map(|s| s.to_owned());
390 let name = app_section.get("Name").ok_or(())?.to_owned();
391 let version = app_section.get("Version").ok_or(())?.to_owned();
392 let build_id = app_section.get("BuildID").ok_or(())?.to_owned();
393 let product_id = app_section.get("ID").ok_or(())?.to_owned();
394
395 // Parse the "Crash Reporter" section
396 let crash_reporter_section = conf.section(Some("Crash Reporter")).ok_or(())?;
397 let server_url = crash_reporter_section
398 .get("ServerURL")
399 .ok_or(())?
400 .to_owned();
401
402 // InstallTime<build_id>
403
404 Ok(ApplicationData {
405 vendor,
406 name,
407 version,
408 build_id,
409 product_id,
410 server_url,
411 })
412 }
413
get_path(install_path: &Path) -> PathBuf414 fn get_path(install_path: &Path) -> PathBuf {
415 install_path.join("application.ini")
416 }
417 }
418
419 #[derive(Serialize)]
420 #[allow(non_snake_case)]
421 struct Annotations {
422 BuildID: String,
423 CrashTime: String,
424 InstallTime: String,
425 #[serde(skip_serializing_if = "Option::is_none")]
426 OOMAllocationSize: Option<String>,
427 ProductID: String,
428 ProductName: String,
429 ReleaseChannel: String,
430 ServerURL: String,
431 StartupTime: String,
432 UptimeTS: String,
433 #[serde(skip_serializing_if = "Option::is_none")]
434 Vendor: Option<String>,
435 Version: String,
436 WindowsErrorReporting: String,
437 }
438
439 impl Annotations {
from_application_data( application_data: &ApplicationData, release_channel: String, install_time: String, crash_time: u64, startup_time: u64, oom_allocation_size: usize, ) -> Annotations440 fn from_application_data(
441 application_data: &ApplicationData,
442 release_channel: String,
443 install_time: String,
444 crash_time: u64,
445 startup_time: u64,
446 oom_allocation_size: usize,
447 ) -> Annotations {
448 let oom_allocation_size = if oom_allocation_size != 0 {
449 Some(oom_allocation_size.to_string())
450 } else {
451 None
452 };
453 Annotations {
454 BuildID: application_data.build_id.clone(),
455 CrashTime: crash_time.to_string(),
456 InstallTime: install_time,
457 OOMAllocationSize: oom_allocation_size,
458 ProductID: application_data.product_id.clone(),
459 ProductName: application_data.name.clone(),
460 ReleaseChannel: release_channel,
461 ServerURL: application_data.server_url.clone(),
462 StartupTime: startup_time.to_string(),
463 UptimeTS: (crash_time - startup_time).to_string() + ".0",
464 Vendor: application_data.vendor.clone(),
465 Version: application_data.version.clone(),
466 WindowsErrorReporting: "1".to_string(),
467 }
468 }
469 }
470
471 /// Encapsulates the information about the application that crashed. This includes the install path as well as version information
472 struct ApplicationInformation {
473 install_path: PathBuf,
474 application_data: ApplicationData,
475 release_channel: String,
476 crash_reports_dir: PathBuf,
477 install_time: String,
478 }
479
480 impl ApplicationInformation {
from_process(process: HANDLE) -> Result<ApplicationInformation, ()>481 fn from_process(process: HANDLE) -> Result<ApplicationInformation, ()> {
482 let mut install_path = ApplicationInformation::get_application_path(process)?;
483 install_path.pop();
484 let application_data = ApplicationData::load_from_disk(install_path.as_ref())?;
485 let release_channel = ApplicationInformation::get_release_channel(install_path.as_ref())?;
486 let crash_reports_dir = ApplicationInformation::get_crash_reports_dir(&application_data)?;
487 let install_time = ApplicationInformation::get_install_time(
488 &crash_reports_dir,
489 &application_data.build_id,
490 )?;
491
492 Ok(ApplicationInformation {
493 install_path,
494 application_data,
495 release_channel,
496 crash_reports_dir,
497 install_time,
498 })
499 }
500
get_application_path(process: HANDLE) -> Result<PathBuf, ()>501 fn get_application_path(process: HANDLE) -> Result<PathBuf, ()> {
502 let mut path: [u16; MAX_PATH + 1] = [0; MAX_PATH + 1];
503 unsafe {
504 let res = K32GetModuleFileNameExW(
505 process,
506 null_mut(),
507 (&mut path).as_mut_ptr(),
508 (MAX_PATH + 1) as DWORD,
509 );
510
511 if res == 0 {
512 return Err(());
513 }
514
515 let application_path = PathBuf::from(OsString::from_wide(&path[0..res as usize]));
516 Ok(application_path)
517 }
518 }
519
get_release_channel(install_path: &Path) -> Result<String, ()>520 fn get_release_channel(install_path: &Path) -> Result<String, ()> {
521 let channel_prefs =
522 File::open(install_path.join("defaults/pref/channel-prefs.js")).map_err(|_e| ())?;
523 let lines = BufReader::new(channel_prefs).lines();
524 let line = lines
525 .filter_map(Result::ok)
526 .find(|line| line.contains("app.update.channel"))
527 .ok_or(())?;
528 line.split("\"").nth(3).map(|s| s.to_string()).ok_or(())
529 }
530
get_crash_reports_dir(application_data: &ApplicationData) -> Result<PathBuf, ()>531 fn get_crash_reports_dir(application_data: &ApplicationData) -> Result<PathBuf, ()> {
532 let mut psz_path: PWSTR = null_mut();
533 unsafe {
534 let res = SHGetKnownFolderPath(
535 &FOLDERID_RoamingAppData as *const _,
536 0,
537 null_mut(),
538 &mut psz_path as *mut _,
539 );
540
541 if res == S_OK {
542 let mut len = 0;
543 while psz_path.offset(len).read() != 0 {
544 len += 1;
545 }
546 let str = OsString::from_wide(from_raw_parts(psz_path, len as usize));
547 CoTaskMemFree(psz_path as _);
548 let mut path = PathBuf::from(str);
549 if let Some(vendor) = &application_data.vendor {
550 path.push(vendor);
551 }
552 path.push(&application_data.name);
553 path.push("Crash Reports");
554 Ok(path)
555 } else {
556 Err(())
557 }
558 }
559 }
560
get_install_time(crash_reports_path: &Path, build_id: &str) -> Result<String, ()>561 fn get_install_time(crash_reports_path: &Path, build_id: &str) -> Result<String, ()> {
562 let file_name = "InstallTime".to_owned() + build_id;
563 let file_path = crash_reports_path.join(file_name);
564 read_to_string(file_path).map_err(|_e| ())
565 }
566 }
567
568 struct CrashReport {
569 uuid: String,
570 crash_reports_path: PathBuf,
571 release_channel: String,
572 annotations: Annotations,
573 crash_time: u64,
574 oom_allocation_size: usize,
575 }
576
577 impl CrashReport {
new( application_information: &ApplicationInformation, startup_time: u64, oom_allocation_size: usize, ) -> CrashReport578 fn new(
579 application_information: &ApplicationInformation,
580 startup_time: u64,
581 oom_allocation_size: usize,
582 ) -> CrashReport {
583 let uuid = Uuid::new_v4()
584 .to_hyphenated()
585 .encode_lower(&mut Uuid::encode_buffer())
586 .to_owned();
587 let crash_reports_path = application_information.crash_reports_dir.clone();
588 let crash_time: u64 = unsafe { time(null_mut()) as u64 };
589 let annotations = Annotations::from_application_data(
590 &application_information.application_data,
591 application_information.release_channel.clone(),
592 application_information.install_time.clone(),
593 crash_time,
594 startup_time,
595 oom_allocation_size,
596 );
597 CrashReport {
598 uuid,
599 crash_reports_path,
600 release_channel: application_information.release_channel.clone(),
601 annotations,
602 crash_time,
603 oom_allocation_size,
604 }
605 }
606
is_nightly(&self) -> bool607 fn is_nightly(&self) -> bool {
608 self.release_channel == "nightly" || self.release_channel == "default"
609 }
610
get_minidump_type(&self) -> MINIDUMP_TYPE611 fn get_minidump_type(&self) -> MINIDUMP_TYPE {
612 let mut minidump_type = MiniDumpWithFullMemoryInfo | MiniDumpWithUnloadedModules;
613 if self.is_nightly() {
614 // This is Nightly only because this doubles the size of minidumps based
615 // on the experimental data.
616 minidump_type = minidump_type | MiniDumpWithProcessThreadData;
617
618 // dbghelp.dll on Win7 can't handle overlapping memory regions so we only
619 // enable this feature on Win8 or later.
620 if is_windows8_or_later() {
621 // This allows us to examine heap objects referenced from stack objects
622 // at the cost of further doubling the size of minidumps.
623 minidump_type = minidump_type | MiniDumpWithIndirectlyReferencedMemory
624 }
625 }
626 minidump_type
627 }
628
get_pending_path(&self) -> PathBuf629 fn get_pending_path(&self) -> PathBuf {
630 self.crash_reports_path.join("pending")
631 }
632
get_events_path(&self) -> PathBuf633 fn get_events_path(&self) -> PathBuf {
634 self.crash_reports_path.join("events")
635 }
636
get_minidump_path(&self) -> PathBuf637 fn get_minidump_path(&self) -> PathBuf {
638 self.get_pending_path().join(self.uuid.to_string() + ".dmp")
639 }
640
get_minidump_name(&self) -> [u8; 40]641 fn get_minidump_name(&self) -> [u8; 40] {
642 let bytes = (self.uuid.to_string() + ".dmp").into_bytes();
643 bytes[0..40].try_into().unwrap()
644 }
645
get_extra_file_path(&self) -> PathBuf646 fn get_extra_file_path(&self) -> PathBuf {
647 self.get_pending_path()
648 .join(self.uuid.to_string() + ".extra")
649 }
650
get_event_file_path(&self) -> PathBuf651 fn get_event_file_path(&self) -> PathBuf {
652 self.get_events_path().join(self.uuid.to_string())
653 }
654
write_minidump( &self, exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION, ) -> Result<(), ()>655 fn write_minidump(
656 &self,
657 exception_information: PWER_RUNTIME_EXCEPTION_INFORMATION,
658 ) -> Result<(), ()> {
659 let minidump_path = self.get_minidump_path();
660 let minidump_file = File::create(minidump_path).map_err(|_e| ())?;
661 let minidump_type: MINIDUMP_TYPE = self.get_minidump_type();
662
663 unsafe {
664 let mut exception_pointers = EXCEPTION_POINTERS {
665 ExceptionRecord: &mut ((*exception_information).exceptionRecord),
666 ContextRecord: &mut ((*exception_information).context),
667 };
668
669 let mut exception = MINIDUMP_EXCEPTION_INFORMATION {
670 ThreadId: GetThreadId((*exception_information).hThread),
671 ExceptionPointers: &mut exception_pointers,
672 ClientPointers: FALSE,
673 };
674
675 let res = MiniDumpWriteDump(
676 (*exception_information).hProcess,
677 get_process_id((*exception_information).hProcess)?,
678 minidump_file.as_raw_handle() as _,
679 minidump_type,
680 &mut exception,
681 /* userStream */ null(),
682 /* callback */ null(),
683 );
684
685 bool_ok_or_err(res, ())
686 }
687 }
688
write_extra_file(&self) -> Result<(), ()>689 fn write_extra_file(&self) -> Result<(), ()> {
690 let extra_file = File::create(self.get_extra_file_path()).map_err(|_e| ())?;
691 to_writer(extra_file, &self.annotations).map_err(|_e| ())
692 }
693
write_event_file(&self) -> Result<(), ()>694 fn write_event_file(&self) -> Result<(), ()> {
695 let mut event_file = File::create(self.get_event_file_path()).map_err(|_e| ())?;
696 writeln!(event_file, "crash.main.3").map_err(|_e| ())?;
697 writeln!(event_file, "{}", self.crash_time).map_err(|_e| ())?;
698 writeln!(event_file, "{}", self.uuid).map_err(|_e| ())?;
699 to_writer(event_file, &self.annotations).map_err(|_e| ())
700 }
701 }
702
is_windows8_or_later() -> bool703 fn is_windows8_or_later() -> bool {
704 let mut info = OSVERSIONINFOEXW {
705 dwOSVersionInfoSize: size_of::<OSVERSIONINFOEXW>().try_into().unwrap(),
706 dwMajorVersion: 6,
707 dwMinorVersion: 2,
708 ..Default::default()
709 };
710
711 unsafe {
712 let mut mask: DWORDLONG = 0;
713 mask = VerSetConditionMask(mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
714 mask = VerSetConditionMask(mask, VER_MINORVERSION, VER_GREATER_EQUAL);
715 mask = VerSetConditionMask(mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
716 mask = VerSetConditionMask(mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
717
718 let res = VerifyVersionInfoW(
719 &mut info as LPOSVERSIONINFOEXW,
720 VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
721 mask,
722 );
723
724 res != FALSE
725 }
726 }
727
bool_ok_or_err<T>(res: BOOL, data: T) -> Result<T, ()>728 fn bool_ok_or_err<T>(res: BOOL, data: T) -> Result<T, ()> {
729 match res {
730 FALSE => Err(()),
731 _ => Ok(data),
732 }
733 }
734
read_environment_block(process: HANDLE) -> Result<Vec<u16>, ()>735 fn read_environment_block(process: HANDLE) -> Result<Vec<u16>, ()> {
736 let upp = read_user_process_parameters(process)?;
737
738 // Read the environment
739 let buffer = upp.Environment;
740 let length = upp.EnvironmentSize;
741 let count = length as usize / 2;
742 read_array_from_process::<u16>(process, buffer, count)
743 }
744
read_command_line(process: HANDLE) -> Result<String, ()>745 fn read_command_line(process: HANDLE) -> Result<String, ()> {
746 let upp = read_user_process_parameters(process)?;
747
748 // Read the command-line
749 let buffer = upp.CommandLine.Buffer;
750 let length = upp.CommandLine.Length;
751 let count = (length as usize) / 2;
752 let command_line = read_array_from_process::<u16>(process, buffer as *mut _, count)?;
753 String::from_utf16(&command_line).map_err(|_err| ())
754 }
755
read_user_process_parameters(process: HANDLE) -> Result<RTL_USER_PROCESS_PARAMETERS, ()>756 fn read_user_process_parameters(process: HANDLE) -> Result<RTL_USER_PROCESS_PARAMETERS, ()> {
757 let mut pbi: PROCESS_BASIC_INFORMATION = unsafe { zeroed() };
758 let mut length: ULONG = 0;
759 let result = unsafe {
760 NtQueryInformationProcess(
761 process,
762 ProcessBasicInformation,
763 &mut pbi as *mut _ as _,
764 size_of::<PROCESS_BASIC_INFORMATION>().try_into().unwrap(),
765 &mut length,
766 )
767 };
768
769 if result != STATUS_SUCCESS {
770 return Err(());
771 }
772
773 // Read the process environment block
774 let peb: PEB = read_from_process(process, pbi.PebBaseAddress)?;
775
776 // Read the user process parameters
777 read_from_process::<RTL_USER_PROCESS_PARAMETERS>(process, peb.ProcessParameters)
778 }
779
780 /******************************************************************************
781 * The stuff below should be migrated to the winapi crate, see bug 1696414 *
782 ******************************************************************************/
783
784 // we can't use winapi's ENUM macro directly because it doesn't support
785 // attributes, so let's define this one here until we migrate this code
786 macro_rules! ENUM {
787 {enum $name:ident { $($variant:ident = $value:expr,)+ }} => {
788 #[allow(non_camel_case_types)] pub type $name = u32;
789 $(#[allow(non_upper_case_globals)] pub const $variant: $name = $value;)+
790 };
791 }
792
793 // winapi doesn't export the FN macro, so we duplicate it here
794 macro_rules! FN {
795 (stdcall $func:ident($($t:ty,)*) -> $ret:ty) => (
796 #[allow(non_camel_case_types)] pub type $func = Option<unsafe extern "system" fn($($t,)*) -> $ret>;
797 );
798 (stdcall $func:ident($($p:ident: $t:ty,)*) -> $ret:ty) => (
799 #[allow(non_camel_case_types)] pub type $func = Option<unsafe extern "system" fn($($p: $t,)*) -> $ret>;
800 );
801 }
802
803 // From um/WerApi.h
804
805 STRUCT! {#[allow(non_snake_case)] struct WER_RUNTIME_EXCEPTION_INFORMATION
806 {
807 dwSize: DWORD,
808 hProcess: HANDLE,
809 hThread: HANDLE,
810 exceptionRecord: EXCEPTION_RECORD,
811 context: CONTEXT,
812 pwszReportId: PCWSTR,
813 bIsFatal: BOOL,
814 dwReserved: DWORD,
815 }}
816
817 #[allow(non_camel_case_types)]
818 pub type PWER_RUNTIME_EXCEPTION_INFORMATION = *mut WER_RUNTIME_EXCEPTION_INFORMATION;
819
820 // From minidumpapiset.hProcess
821
822 STRUCT! {#[allow(non_snake_case)] #[repr(packed(4))] struct MINIDUMP_EXCEPTION_INFORMATION {
823 ThreadId: DWORD,
824 ExceptionPointers: PEXCEPTION_POINTERS,
825 ClientPointers: BOOL,
826 }}
827
828 #[allow(non_camel_case_types)]
829 pub type PMINIDUMP_EXCEPTION_INFORMATION = *mut MINIDUMP_EXCEPTION_INFORMATION;
830
831 ENUM! { enum MINIDUMP_TYPE {
832 MiniDumpNormal = 0x00000000,
833 MiniDumpWithDataSegs = 0x00000001,
834 MiniDumpWithFullMemory = 0x00000002,
835 MiniDumpWithHandleData = 0x00000004,
836 MiniDumpFilterMemory = 0x00000008,
837 MiniDumpScanMemory = 0x00000010,
838 MiniDumpWithUnloadedModules = 0x00000020,
839 MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
840 MiniDumpFilterModulePaths = 0x00000080,
841 MiniDumpWithProcessThreadData = 0x00000100,
842 MiniDumpWithPrivateReadWriteMemory = 0x00000200,
843 MiniDumpWithoutOptionalData = 0x00000400,
844 MiniDumpWithFullMemoryInfo = 0x00000800,
845 MiniDumpWithThreadInfo = 0x00001000,
846 MiniDumpWithCodeSegs = 0x00002000,
847 MiniDumpWithoutAuxiliaryState = 0x00004000,
848 MiniDumpWithFullAuxiliaryState = 0x00008000,
849 MiniDumpWithPrivateWriteCopyMemory = 0x00010000,
850 MiniDumpIgnoreInaccessibleMemory = 0x00020000,
851 MiniDumpWithTokenInformation = 0x00040000,
852 MiniDumpWithModuleHeaders = 0x00080000,
853 MiniDumpFilterTriage = 0x00100000,
854 MiniDumpWithAvxXStateContext = 0x00200000,
855 MiniDumpWithIptTrace = 0x00400000,
856 MiniDumpScanInaccessiblePartialPages = 0x00800000,
857 MiniDumpValidTypeFlags = 0x00ffffff,
858 }}
859
860 // We don't actually need the following three structs so we use placeholders
861 STRUCT! {#[allow(non_snake_case)] struct MINIDUMP_CALLBACK_INPUT {
862 dummy: u32,
863 }}
864
865 #[allow(non_camel_case_types)]
866 pub type PMINIDUMP_CALLBACK_INPUT = *const MINIDUMP_CALLBACK_INPUT;
867
868 STRUCT! {#[allow(non_snake_case)] struct MINIDUMP_USER_STREAM_INFORMATION {
869 dummy: u32,
870 }}
871
872 #[allow(non_camel_case_types)]
873 pub type PMINIDUMP_USER_STREAM_INFORMATION = *const MINIDUMP_USER_STREAM_INFORMATION;
874
875 STRUCT! {#[allow(non_snake_case)] struct MINIDUMP_CALLBACK_OUTPUT {
876 dummy: u32,
877 }}
878
879 #[allow(non_camel_case_types)]
880 pub type PMINIDUMP_CALLBACK_OUTPUT = *const MINIDUMP_CALLBACK_OUTPUT;
881
882 // MiniDumpWriteDump() function and structs
883 FN! {stdcall MINIDUMP_CALLBACK_ROUTINE(
884 CallbackParam: PVOID,
885 CallbackInput: PMINIDUMP_CALLBACK_INPUT,
886 CallbackOutput: PMINIDUMP_CALLBACK_OUTPUT,
887 ) -> BOOL}
888
889 STRUCT! {#[allow(non_snake_case)] #[repr(packed(4))] struct MINIDUMP_CALLBACK_INFORMATION {
890 CallbackRoutine: MINIDUMP_CALLBACK_ROUTINE,
891 CallbackParam: PVOID,
892 }}
893
894 #[allow(non_camel_case_types)]
895 pub type PMINIDUMP_CALLBACK_INFORMATION = *const MINIDUMP_CALLBACK_INFORMATION;
896
897 extern "system" {
MiniDumpWriteDump( hProcess: HANDLE, ProcessId: DWORD, hFile: HANDLE, DumpType: MINIDUMP_TYPE, Exceptionparam: PMINIDUMP_EXCEPTION_INFORMATION, UserStreamParam: PMINIDUMP_USER_STREAM_INFORMATION, CallbackParam: PMINIDUMP_CALLBACK_INFORMATION, ) -> BOOL898 pub fn MiniDumpWriteDump(
899 hProcess: HANDLE,
900 ProcessId: DWORD,
901 hFile: HANDLE,
902 DumpType: MINIDUMP_TYPE,
903 Exceptionparam: PMINIDUMP_EXCEPTION_INFORMATION,
904 UserStreamParam: PMINIDUMP_USER_STREAM_INFORMATION,
905 CallbackParam: PMINIDUMP_CALLBACK_INFORMATION,
906 ) -> BOOL;
907 }
908
909 // From um/winternl.h
910
911 STRUCT! {#[allow(non_snake_case)] struct PEB_LDR_DATA {
912 Reserved1: [BYTE; 8],
913 Reserved2: [PVOID; 3],
914 InMemoryOrderModuleList: LIST_ENTRY,
915 }}
916
917 #[allow(non_camel_case_types)]
918 pub type PPEB_LDR_DATA = *mut PEB_LDR_DATA;
919
920 STRUCT! {#[allow(non_snake_case)] struct RTL_DRIVE_LETTER_CURDIR {
921 Flags: WORD,
922 Length: WORD,
923 TimeStamp: ULONG,
924 DosPath: STRING,
925 }}
926
927 STRUCT! {#[allow(non_snake_case)] struct RTL_USER_PROCESS_PARAMETERS {
928 Reserved1: [BYTE; 16],
929 Reserved2: [PVOID; 10],
930 ImagePathName: UNICODE_STRING,
931 CommandLine: UNICODE_STRING,
932 // Everything below this point is undocumented
933 Environment: PVOID,
934 StartingX: ULONG,
935 StartingY: ULONG,
936 CountX: ULONG,
937 CountY: ULONG,
938 CountCharsX: ULONG,
939 CountCharsY: ULONG,
940 FillAttribute: ULONG,
941 WindowFlags: ULONG,
942 ShowWindowFlags: ULONG,
943 WindowTitle: UNICODE_STRING,
944 DesktopInfo: UNICODE_STRING,
945 ShellInfo: UNICODE_STRING,
946 RuntimeData: UNICODE_STRING,
947 CurrentDirectores: [RTL_DRIVE_LETTER_CURDIR; 32],
948 EnvironmentSize: ULONG,
949 }}
950
951 #[allow(non_camel_case_types)]
952 pub type PRTL_USER_PROCESS_PARAMETERS = *mut RTL_USER_PROCESS_PARAMETERS;
953
954 FN! {stdcall PPS_POST_PROCESS_INIT_ROUTINE() -> ()}
955
956 STRUCT! {#[allow(non_snake_case)] struct PEB {
957 Reserved1: [BYTE; 2],
958 BeingDebugged: BYTE,
959 Reserved2: [BYTE; 1],
960 Reserved3: [PVOID; 2],
961 Ldr: PPEB_LDR_DATA,
962 ProcessParameters: PRTL_USER_PROCESS_PARAMETERS,
963 Reserved4: [PVOID; 3],
964 AtlThunkSListPtr: PVOID,
965 Reserved5: PVOID,
966 Reserved6: ULONG,
967 Reserved7: PVOID,
968 Reserved8: ULONG,
969 AtlThunkSListPtr32: ULONG,
970 Reserved9: [PVOID; 45],
971 Reserved10: [BYTE; 96],
972 PostProcessInitRoutine: PPS_POST_PROCESS_INIT_ROUTINE,
973 Reserved11: [BYTE; 128],
974 Reserved12: [PVOID; 1],
975 SessionId: ULONG,
976 }}
977
978 #[allow(non_camel_case_types)]
979 pub type PPEB = *mut PEB;
980
981 STRUCT! {#[allow(non_snake_case)] struct PROCESS_BASIC_INFORMATION {
982 Reserved1: PVOID,
983 PebBaseAddress: PPEB,
984 Reserved2: [PVOID; 2],
985 UniqueProcessId: ULONG_PTR,
986 Reserved3: PVOID,
987 }}
988
989 ENUM! {enum PROCESSINFOCLASS {
990 ProcessBasicInformation = 0,
991 ProcessDebugPort = 7,
992 ProcessWow64Information = 26,
993 ProcessImageFileName = 27,
994 ProcessBreakOnTermination = 29,
995 }}
996
997 extern "system" {
NtQueryInformationProcess( ProcessHandle: HANDLE, ProcessInformationClass: PROCESSINFOCLASS, ProcessInformation: PVOID, ProcessInformationLength: ULONG, ReturnLength: PULONG, ) -> NTSTATUS998 pub fn NtQueryInformationProcess(
999 ProcessHandle: HANDLE,
1000 ProcessInformationClass: PROCESSINFOCLASS,
1001 ProcessInformation: PVOID,
1002 ProcessInformationLength: ULONG,
1003 ReturnLength: PULONG,
1004 ) -> NTSTATUS;
1005 }
1006