Source code

Revision control

Copy as Markdown

Other Tools

/* 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 bytes::{Buf, BufMut, Bytes, BytesMut, TryGetError};
#[cfg(any(target_os = "android", target_os = "linux"))]
use minidump_writer::minidump_writer::{AuxvType, DirectAuxvDumpInfo};
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::FromPrimitive;
use std::{
array::TryFromSliceError,
ffi::{CString, FromBytesWithNulError, NulError, OsString},
mem::size_of,
};
use thiserror::Error;
#[cfg(target_os = "windows")]
use windows_sys::Win32::System::Diagnostics::Debug::{CONTEXT, EXCEPTION_RECORD};
use crate::{
breakpad::Pid, ipc_connector::CONNECTOR_ANCILLARY_DATA_LEN,
platform::CHILD_RENDEZVOUS_ANCILLARY_DATA_LEN, AncillaryData, BreakpadString, GeckoChildId,
};
#[derive(Debug, Error)]
pub enum MessageError {
#[error("Nul terminator found within a string")]
InteriorNul(#[from] NulError),
#[error("The message contained an invalid payload")]
InvalidData,
#[error("Message kind is invalid")]
InvalidKind,
#[error("Invalid message size")]
InvalidSize(#[from] TryFromSliceError),
#[error("Missing ancillary data")]
MissingAncillary,
#[error("Missing nul terminator")]
MissingNul(#[from] FromBytesWithNulError),
#[error("Truncated message")]
Truncated(#[from] TryGetError),
#[error("Unexpected ancillary data")]
UnexpectedAncillaryData,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, FromPrimitive, ToPrimitive, PartialEq)]
pub enum Kind {
/// Changes the folder where crash reports are generated
SetCrashReportPath = 1,
/// Request the transfer of an already generated minidump for the specified
/// PID back to the client. The message type is followed by a 32-bit
/// integer containing the PID.
TransferMinidump = 2,
TransferMinidumpReply = 3,
/// Request the generation of a minidump of the specified process.
GenerateMinidump = 4,
GenerateMinidumpReply = 5,
/// Request the generation of a minidump based on data obtained via the
/// Windows Error Reporting runtime exception module. The reply is empty
/// and only used to inform the WER module that it's time to shut down the
/// crashed process. This is only enabled on Windows.
#[cfg(target_os = "windows")]
WindowsErrorReporting = 6,
#[cfg(target_os = "windows")]
WindowsErrorReportingReply = 7,
/// Register and unregister additional information for the auxiliary
/// vector of a process.
#[cfg(any(target_os = "android", target_os = "linux"))]
RegisterAuxvInfo = 8,
#[cfg(any(target_os = "android", target_os = "linux"))]
UnregisterAuxvInfo = 9,
/// Register a new child process and carry over the IPC channel it will use
/// to talk to the crash helper. This is sent from the main process to the
/// crash helper.
RegisterChildProcess = 10,
/// Message sent by the crash helper to rendez-vous with a newly-createdù
/// child process.
ChildProcessRendezVous = 11,
/// Reply to a `ChildProcessRendezVous` message sent by a child after
/// preparing itself for being dumped.
ChildProcessRendezVousReply = 12,
}
// Bytes helpers to serialize/deserialize values not supported directly by the
// `bytes` crate. Note that all of these could be implemented by simply
// transmuting a generic type T into an appropriate byte-sized slice. However
// doing so for arbitrary objects would also copy whatever data might be in
// the padding between the fields or at the end of the object, potentially
// creating a channel to leak information from a process, which is why we only
// have helpers for types that we know are not padded.
trait BytesMutExtensions {
fn put_usize_ne(&mut self, n: usize);
fn put_pid_ne(&mut self, pid: Pid);
#[cfg(target_os = "windows")]
fn put_context(&mut self, contextref: &CONTEXT);
#[cfg(target_os = "windows")]
fn put_exception_record(&mut self, recordref: &EXCEPTION_RECORD);
}
impl BytesMutExtensions for BytesMut {
fn put_usize_ne(&mut self, n: usize) {
#[cfg(target_pointer_width = "32")]
self.put_u32_ne(n as u32);
#[cfg(target_pointer_width = "64")]
self.put_u64_ne(n as u64);
}
fn put_pid_ne(&mut self, pid: Pid) {
#[cfg(target_os = "windows")]
self.put_u32_ne(pid);
#[cfg(not(target_os = "windows"))]
self.put_i32_ne(pid);
}
#[cfg(target_os = "windows")]
fn put_context(&mut self, contextref: &CONTEXT) {
// SAFETY: The `CONTEXT` structure does not contain padding and can
// thus be stored as a simple slice of bytes of the same size.
let bytes: [u8; size_of::<CONTEXT>()] = unsafe { std::mem::transmute(*contextref) };
self.put_slice(&bytes);
}
#[cfg(target_os = "windows")]
fn put_exception_record(&mut self, recordref: &EXCEPTION_RECORD) {
self.put_i32_ne(recordref.ExceptionCode);
self.put_u32_ne(recordref.ExceptionFlags);
// We skip the `ExceptionRecord` field because it's a pointer and it
// would be invalid in the destination process, we rebuild it there.
self.put_usize_ne(recordref.ExceptionAddress as usize);
self.put_u32_ne(recordref.NumberParameters);
for info in recordref.ExceptionInformation {
self.put_usize_ne(info);
}
}
}
trait BytesExtensions {
fn try_get_usize_ne(&mut self) -> Result<usize, TryGetError>;
fn try_get_pid_ne(&mut self) -> Result<Pid, TryGetError>;
fn try_get_vec(&mut self, len: usize) -> Result<Vec<u8>, TryGetError>;
#[cfg(target_os = "windows")]
fn try_get_context(&mut self) -> Result<CONTEXT, TryGetError>;
#[cfg(target_os = "windows")]
fn try_get_exception_record(&mut self) -> Result<EXCEPTION_RECORD, TryGetError>;
}
impl BytesExtensions for Bytes {
fn try_get_usize_ne(&mut self) -> Result<usize, TryGetError> {
#[cfg(target_pointer_width = "32")]
return self.try_get_u32_ne().map(|v| v as usize);
#[cfg(target_pointer_width = "64")]
return self.try_get_u64_ne().map(|v| v as usize);
}
fn try_get_pid_ne(&mut self) -> Result<Pid, TryGetError> {
#[cfg(target_os = "windows")]
return self.try_get_u32_ne();
#[cfg(not(target_os = "windows"))]
return self.try_get_i32_ne();
}
fn try_get_vec(&mut self, len: usize) -> Result<Vec<u8>, TryGetError> {
let mut buffer = vec![0u8; len];
self.try_copy_to_slice(&mut buffer)?;
Ok(buffer)
}
#[cfg(target_os = "windows")]
fn try_get_context(&mut self) -> Result<CONTEXT, TryGetError> {
let buffer = self.try_get_vec(size_of::<CONTEXT>())?;
// SAFETY: The `CONTEXT` structure has no padding, so it can be
// populated by copying over its contents from a slice of bytes.
// Unrwapping the result of the slice `try_into()` will also never
// fail as the size is guaranteed to be right.
let context = unsafe {
std::mem::transmute::<[u8; size_of::<CONTEXT>()], CONTEXT>(
(*buffer.as_slice()).try_into().unwrap(),
)
};
Ok(context)
}
#[cfg(target_os = "windows")]
fn try_get_exception_record(&mut self) -> Result<EXCEPTION_RECORD, TryGetError> {
let exception_code = self.try_get_i32_ne()?;
let exception_flags = self.try_get_u32_ne()?;
let exception_address = self.try_get_usize_ne()?;
let number_parameters = self.try_get_u32_ne()?;
let mut exception_information: [usize; 15] = [0usize; 15];
for ei in exception_information.iter_mut() {
*ei = self.try_get_usize_ne()?;
}
Ok(EXCEPTION_RECORD {
ExceptionCode: exception_code,
ExceptionFlags: exception_flags,
ExceptionRecord: std::ptr::null_mut(),
ExceptionAddress: exception_address as *mut std::ffi::c_void,
NumberParameters: number_parameters,
ExceptionInformation: exception_information,
})
}
}
pub trait Message {
fn kind() -> Kind;
fn payload_size(&self) -> usize;
fn ancillary_data_len(&self) -> usize;
fn encode(self) -> (Bytes, Bytes, Vec<AncillaryData>);
fn decode(data: Vec<u8>, ancillary_data: Vec<AncillaryData>) -> Result<Self, MessageError>
where
Self: Sized;
}
/* Message header, all messages are prefixed with this. The header is sent as
* a single message over the underlying transport and contains the size of the
* message payload as well as the type of the message. This allows the receiver
* to validate and prepare for the reception of the payload. */
pub const HEADER_SIZE: usize = size_of::<Kind>() + size_of::<usize>();
pub struct Header {
pub kind: Kind,
pub size: usize,
}
impl Header {
fn encode(kind: Kind, size: usize) -> Bytes {
let mut buffer = BytesMut::with_capacity(HEADER_SIZE);
buffer.put_u8(kind as u8);
buffer.put_usize_ne(size);
debug_assert!(buffer.len() == HEADER_SIZE, "Header size mismatch");
buffer.freeze()
}
pub fn decode(buffer: Vec<u8>) -> Result<Header, MessageError> {
let mut buffer = Bytes::from(buffer);
let kind = buffer.try_get_u8()?;
let kind = Kind::from_u8(kind).ok_or(MessageError::InvalidKind)?;
let size = buffer.try_get_usize_ne()?;
Ok(Header { kind, size })
}
}
/* Message used to change the path where crash reports are generated. */
pub struct SetCrashReportPath {
pub path: OsString,
}
impl SetCrashReportPath {
pub fn new(path: OsString) -> SetCrashReportPath {
SetCrashReportPath { path }
}
}
impl Message for SetCrashReportPath {
fn kind() -> Kind {
Kind::SetCrashReportPath
}
fn payload_size(&self) -> usize {
let path_len = self.path.clone().serialize().len();
size_of::<usize>().checked_add(path_len).unwrap()
}
fn ancillary_data_len(&self) -> usize {
0
}
fn encode(self) -> (Bytes, Bytes, Vec<AncillaryData>) {
let header = Header::encode(Self::kind(), self.payload_size());
let mut payload = BytesMut::with_capacity(self.payload_size());
let path = self.path.serialize();
payload.put_usize_ne(path.len());
payload.put(path);
(header, payload.freeze(), vec![])
}
fn decode(data: Vec<u8>, ancillary_data: Vec<AncillaryData>) -> Result<Self, MessageError> {
if !ancillary_data.is_empty() {
return Err(MessageError::UnexpectedAncillaryData);
}
let mut data = Bytes::from(data);
let path_len = data.try_get_usize_ne()?;
let path = data.try_get_vec(path_len)?;
let path = <OsString as BreakpadString>::deserialize(path)
.map_err(|_| MessageError::InvalidData)?;
Ok(SetCrashReportPath { path })
}
}
/* Transfer minidump message, used to request the minidump which has been
* generated for the specified pid. */
pub struct TransferMinidump {
pub id: GeckoChildId,
}
impl TransferMinidump {
pub fn new(id: GeckoChildId) -> TransferMinidump {
TransferMinidump { id }
}
}
impl Message for TransferMinidump {
fn kind() -> Kind {
Kind::TransferMinidump
}
fn payload_size(&self) -> usize {
size_of::<GeckoChildId>()
}
fn ancillary_data_len(&self) -> usize {
0
}
fn encode(self) -> (Bytes, Bytes, Vec<AncillaryData>) {
let header = Header::encode(Self::kind(), self.payload_size());
let mut payload = BytesMut::with_capacity(self.payload_size());
payload.put_i32_ne(self.id);
(header, payload.freeze(), vec![])
}
fn decode(data: Vec<u8>, ancillary_data: Vec<AncillaryData>) -> Result<Self, MessageError> {
if !ancillary_data.is_empty() {
return Err(MessageError::UnexpectedAncillaryData);
}
let mut data = Bytes::from(data);
let id = data.try_get_i32_ne()?;
Ok(TransferMinidump { id })
}
}
/* Transfer minidump reply, received from the server after having sent a
* TransferMinidump message. */
pub struct TransferMinidumpReply {
pub path: OsString,
pub error: Option<CString>,
}
impl TransferMinidumpReply {
pub fn new(path: OsString, error: Option<CString>) -> TransferMinidumpReply {
TransferMinidumpReply { path, error }
}
}
impl Message for TransferMinidumpReply {
fn kind() -> Kind {
Kind::TransferMinidumpReply
}
fn payload_size(&self) -> usize {
let path_len = self.path.clone().serialize().len();
(size_of::<usize>() * 2)
.checked_add(path_len)
.and_then(|l| {
l.checked_add(
self.error
.as_ref()
.map_or(0, |error| error.as_bytes().len()),
)
})
.unwrap()
}
fn ancillary_data_len(&self) -> usize {
0
}
fn encode(self) -> (Bytes, Bytes, Vec<AncillaryData>) {
let header = Header::encode(Self::kind(), self.payload_size());
let mut payload = BytesMut::with_capacity(self.payload_size());
let path_bytes = self.path.serialize();
let error_bytes = self
.error
.as_ref()
.map_or(Vec::new(), |error| Vec::from(error.as_bytes()));
payload.put_usize_ne(path_bytes.len());
payload.put_usize_ne(error_bytes.len());
payload.put(path_bytes);
payload.put(error_bytes.as_slice());
(header, payload.freeze(), vec![])
}
fn decode(
data: Vec<u8>,
ancillary_data: Vec<AncillaryData>,
) -> Result<TransferMinidumpReply, MessageError> {
if !ancillary_data.is_empty() {
return Err(MessageError::UnexpectedAncillaryData);
}
let mut data = Bytes::from(data);
let path_len = data.try_get_usize_ne()?;
let error_len = data.try_get_usize_ne()?;
let path = data.try_get_vec(path_len)?;
let path = <OsString as BreakpadString>::deserialize(path)
.map_err(|_| MessageError::InvalidData)?;
let error = if error_len > 0 {
Some(CString::new(data.try_get_vec(error_len)?)?)
} else {
None
};
Ok(TransferMinidumpReply::new(path, error))
}
}
/* Generate a minidump based on information captured by the Windows Error Reporting runtime exception module. */
#[cfg(target_os = "windows")]
pub struct WindowsErrorReportingMinidump {
pub process: AncillaryData,
pub thread: AncillaryData,
pub exception_records: Vec<EXCEPTION_RECORD>,
pub context: CONTEXT,
}
#[cfg(target_os = "windows")]
impl WindowsErrorReportingMinidump {
pub fn new(
process: AncillaryData,
thread: AncillaryData,
exception_records: Vec<EXCEPTION_RECORD>,
context: CONTEXT,
) -> WindowsErrorReportingMinidump {
WindowsErrorReportingMinidump {
process,
thread,
exception_records,
context,
}
}
}
// The size of the structure minus the `ExceptionRecord` pointer and the
// 4-bytes padding after the `NumberParameters` field.
#[cfg(all(target_os = "windows", target_pointer_width = "64"))]
const EXCEPTION_RECORD_SERIALIZED_SIZE: usize =
size_of::<EXCEPTION_RECORD>() - (size_of::<usize>() + size_of::<u32>());
// The size of the structure minus the `ExceptionRecord` pointer.
#[cfg(all(target_os = "windows", target_pointer_width = "32"))]
const EXCEPTION_RECORD_SERIALIZED_SIZE: usize = size_of::<EXCEPTION_RECORD>() - size_of::<usize>();
#[cfg(target_os = "windows")]
const WINDOWS_ERROR_REPORTING_MINIDUMP_ANCILLARY_DATA_LEN: usize = 2;
#[cfg(target_os = "windows")]
impl Message for WindowsErrorReportingMinidump {
fn kind() -> Kind {
Kind::WindowsErrorReporting
}
fn payload_size(&self) -> usize {
size_of::<usize>()
+ (EXCEPTION_RECORD_SERIALIZED_SIZE * self.exception_records.len())
+ size_of::<CONTEXT>()
}
fn ancillary_data_len(&self) -> usize {
WINDOWS_ERROR_REPORTING_MINIDUMP_ANCILLARY_DATA_LEN
}
fn encode(self) -> (Bytes, Bytes, Vec<AncillaryData>) {
let header = Header::encode(Self::kind(), self.payload_size());
let mut payload = BytesMut::with_capacity(self.payload_size());
payload.put_usize_ne(self.exception_records.len());
for exception_record in self.exception_records.iter() {
payload.put_exception_record(exception_record);
}
payload.put_context(&self.context);
(header, payload.freeze(), vec![])
}
fn decode(
data: Vec<u8>,
ancillary_data: Vec<AncillaryData>,
) -> Result<WindowsErrorReportingMinidump, MessageError> {
if ancillary_data.len() < WINDOWS_ERROR_REPORTING_MINIDUMP_ANCILLARY_DATA_LEN {
return Err(MessageError::MissingAncillary);
} else if ancillary_data.len() > WINDOWS_ERROR_REPORTING_MINIDUMP_ANCILLARY_DATA_LEN {
return Err(MessageError::UnexpectedAncillaryData);
}
let mut bytes = Bytes::from(data);
let exception_records_n = bytes.try_get_usize_ne()?;
let mut exception_records = Vec::<EXCEPTION_RECORD>::with_capacity(exception_records_n);
for _i in 0..exception_records_n {
let exception_record = bytes.try_get_exception_record()?;
exception_records.push(exception_record);
}
let context = bytes.try_get_context()?;
let mut iter = ancillary_data.into_iter();
let process = iter.next().unwrap();
let thread = iter.next().unwrap();
Ok(WindowsErrorReportingMinidump {
process,
thread,
exception_records,
context,
})
}
}
/* Windows Error Reporting minidump reply, received from the server after
* having sent a WindowsErrorReportingMinidumpReply. Informs the client that
* it can tear down the crashed process. */
#[cfg(target_os = "windows")]
pub struct WindowsErrorReportingMinidumpReply {}
#[cfg(target_os = "windows")]
impl Default for WindowsErrorReportingMinidumpReply {
fn default() -> Self {
Self::new()
}
}
#[cfg(target_os = "windows")]
impl WindowsErrorReportingMinidumpReply {
pub fn new() -> WindowsErrorReportingMinidumpReply {
WindowsErrorReportingMinidumpReply {}
}
}
#[cfg(target_os = "windows")]
impl Message for WindowsErrorReportingMinidumpReply {
fn kind() -> Kind {
Kind::WindowsErrorReportingReply
}
fn payload_size(&self) -> usize {
0
}
fn ancillary_data_len(&self) -> usize {
0
}
fn encode(self) -> (Bytes, Bytes, Vec<AncillaryData>) {
let header = Header::encode(Self::kind(), self.payload_size());
(header, Bytes::new(), vec![])
}
fn decode(
data: Vec<u8>,
ancillary_data: Vec<AncillaryData>,
) -> Result<WindowsErrorReportingMinidumpReply, MessageError> {
if !ancillary_data.is_empty() {
return Err(MessageError::UnexpectedAncillaryData);
}
if !data.is_empty() {
return Err(MessageError::InvalidData);
}
Ok(WindowsErrorReportingMinidumpReply::new())
}
}
/* Message used to send information about a process' auxiliary vector. */
#[cfg(any(target_os = "android", target_os = "linux"))]
pub struct RegisterAuxvInfo {
pub id: GeckoChildId,
pub auxv_info: DirectAuxvDumpInfo,
}
#[cfg(any(target_os = "android", target_os = "linux"))]
impl RegisterAuxvInfo {
pub fn new(id: GeckoChildId, auxv_info: DirectAuxvDumpInfo) -> RegisterAuxvInfo {
RegisterAuxvInfo { id, auxv_info }
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
impl Message for RegisterAuxvInfo {
fn kind() -> Kind {
Kind::RegisterAuxvInfo
}
fn payload_size(&self) -> usize {
// A bit hacky but we'll change this when we make
// serialization/deserialization later.
size_of::<GeckoChildId>() + (size_of::<AuxvType>() * 4)
}
fn ancillary_data_len(&self) -> usize {
0
}
fn encode(self) -> (Bytes, Bytes, Vec<AncillaryData>) {
let header = Header::encode(Self::kind(), self.payload_size());
let mut payload = BytesMut::with_capacity(self.payload_size());
payload.put_i32_ne(self.id);
// AuxvType is the size of a pointer
payload.put_usize_ne(self.auxv_info.program_header_count as usize);
payload.put_usize_ne(self.auxv_info.program_header_address as usize);
payload.put_usize_ne(self.auxv_info.linux_gate_address as usize);
payload.put_usize_ne(self.auxv_info.entry_address as usize);
(header, payload.freeze(), vec![])
}
fn decode(
data: Vec<u8>,
ancillary_data: Vec<AncillaryData>,
) -> Result<RegisterAuxvInfo, MessageError> {
if !ancillary_data.is_empty() {
return Err(MessageError::UnexpectedAncillaryData);
}
let mut data = Bytes::from(data);
let id = data.try_get_i32_ne()?;
let program_header_count = data.try_get_usize_ne()? as AuxvType;
let program_header_address = data.try_get_usize_ne()? as AuxvType;
let linux_gate_address = data.try_get_usize_ne()? as AuxvType;
let entry_address = data.try_get_usize_ne()? as AuxvType;
let auxv_info = DirectAuxvDumpInfo {
program_header_count,
program_header_address,
entry_address,
linux_gate_address,
};
Ok(RegisterAuxvInfo { id, auxv_info })
}
}
/* Message used to inform the crash helper that a process' auxiliary vector
* information is not needed anymore. */
#[cfg(any(target_os = "android", target_os = "linux"))]
pub struct UnregisterAuxvInfo {
pub id: GeckoChildId,
}
#[cfg(any(target_os = "android", target_os = "linux"))]
impl UnregisterAuxvInfo {
pub fn new(id: GeckoChildId) -> UnregisterAuxvInfo {
UnregisterAuxvInfo { id }
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
impl Message for UnregisterAuxvInfo {
fn kind() -> Kind {
Kind::UnregisterAuxvInfo
}
fn payload_size(&self) -> usize {
size_of::<GeckoChildId>()
}
fn ancillary_data_len(&self) -> usize {
0
}
fn encode(self) -> (Bytes, Bytes, Vec<AncillaryData>) {
let header = Header::encode(Self::kind(), self.payload_size());
let mut payload = BytesMut::with_capacity(self.payload_size());
payload.put_i32_ne(self.id);
(header, payload.freeze(), vec![])
}
fn decode(
data: Vec<u8>,
ancillary_data: Vec<AncillaryData>,
) -> Result<UnregisterAuxvInfo, MessageError> {
if !ancillary_data.is_empty() {
return Err(MessageError::UnexpectedAncillaryData);
}
let mut data = Bytes::from(data);
let id = data.try_get_i32_ne()?;
Ok(UnregisterAuxvInfo { id })
}
}
/* Message sent from the main process to the crash helper to register a new
* child process which is about to be spawned. This message contains the IPC
* endpoint which the crash helper will use to talk to the child. */
pub struct RegisterChildProcess {
pub ancillary_data: [AncillaryData; CONNECTOR_ANCILLARY_DATA_LEN],
}
impl RegisterChildProcess {
pub fn new(
ancillary_data: [AncillaryData; CONNECTOR_ANCILLARY_DATA_LEN],
) -> RegisterChildProcess {
RegisterChildProcess { ancillary_data }
}
}
impl Message for RegisterChildProcess {
fn kind() -> Kind {
Kind::RegisterChildProcess
}
fn payload_size(&self) -> usize {
0
}
fn ancillary_data_len(&self) -> usize {
self.ancillary_data.len()
}
fn encode(self) -> (Bytes, Bytes, Vec<AncillaryData>) {
let header = Header::encode(Self::kind(), self.payload_size());
let payload = Bytes::new();
(header, payload, self.ancillary_data.into())
}
fn decode(
_data: Vec<u8>,
ancillary_data: Vec<AncillaryData>,
) -> Result<RegisterChildProcess, MessageError> {
let mut iter = ancillary_data.into_iter();
#[cfg(any(target_os = "ios", target_os = "macos"))]
let ancillary_data: [AncillaryData; CONNECTOR_ANCILLARY_DATA_LEN] = {
let send_right = iter.next().ok_or(MessageError::MissingAncillary)?;
let receive_right = iter.next().ok_or(MessageError::MissingAncillary)?;
[send_right, receive_right]
};
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
let ancillary_data: [AncillaryData; CONNECTOR_ANCILLARY_DATA_LEN] =
[iter.next().ok_or(MessageError::MissingAncillary)?];
Ok(RegisterChildProcess { ancillary_data })
}
}
/* Message sent from the crash helper process to a newly registered child
* process. The child will prepare itself for being dumped by the crash helper
* after receiving this message, and then reply to inform the crash helper
* that it is now possible to dump it. */
pub struct ChildProcessRendezVous {
pub crash_helper_pid: Pid,
}
impl ChildProcessRendezVous {
pub fn new(pid: Pid) -> ChildProcessRendezVous {
ChildProcessRendezVous {
crash_helper_pid: pid,
}
}
}
impl Message for ChildProcessRendezVous {
fn kind() -> Kind {
Kind::ChildProcessRendezVous
}
fn payload_size(&self) -> usize {
size_of::<Pid>()
}
fn ancillary_data_len(&self) -> usize {
0
}
fn encode(self) -> (Bytes, Bytes, Vec<AncillaryData>) {
let header = Header::encode(Self::kind(), self.payload_size());
let mut payload = BytesMut::with_capacity(self.payload_size());
payload.put_pid_ne(self.crash_helper_pid);
(header, payload.freeze(), vec![])
}
fn decode(
data: Vec<u8>,
ancillary_data: Vec<AncillaryData>,
) -> Result<ChildProcessRendezVous, MessageError> {
if !ancillary_data.is_empty() {
return Err(MessageError::UnexpectedAncillaryData);
}
let mut data = Bytes::from(data);
let pid = data.try_get_pid_ne()?;
Ok(ChildProcessRendezVous {
crash_helper_pid: pid,
})
}
}
/* Reply sent by a child process to the crash helper process after receiving
* a ChildProcessRendezVous message. The message contains information on
* whether the child process managed to set itself up for being dumped, its
* PID plus platform-specific information that might be needed by the parent to
* dump it. */
pub struct ChildProcessRendezVousReply {
pub dumpable: bool,
pub child_pid: Pid,
pub id: GeckoChildId,
pub ancillary_data: [AncillaryData; CHILD_RENDEZVOUS_ANCILLARY_DATA_LEN],
}
impl ChildProcessRendezVousReply {
pub fn new(
dumpable: bool,
child_pid: Pid,
id: GeckoChildId,
ancillary_data: [AncillaryData; CHILD_RENDEZVOUS_ANCILLARY_DATA_LEN],
) -> ChildProcessRendezVousReply {
ChildProcessRendezVousReply {
dumpable,
child_pid,
id,
ancillary_data,
}
}
}
impl Message for ChildProcessRendezVousReply {
fn kind() -> Kind {
Kind::ChildProcessRendezVousReply
}
fn payload_size(&self) -> usize {
size_of::<u8>() + size_of::<Pid>() + size_of::<GeckoChildId>()
}
fn ancillary_data_len(&self) -> usize {
CHILD_RENDEZVOUS_ANCILLARY_DATA_LEN
}
fn encode(self) -> (Bytes, Bytes, Vec<AncillaryData>) {
let header = Header::encode(Self::kind(), self.payload_size());
let mut payload = BytesMut::with_capacity(self.payload_size());
payload.put_u8(self.dumpable.into());
payload.put_pid_ne(self.child_pid);
payload.put_i32_ne(self.id);
(header, payload.freeze(), self.ancillary_data.into())
}
fn decode(
data: Vec<u8>,
ancillary_data: Vec<AncillaryData>,
) -> Result<ChildProcessRendezVousReply, MessageError> {
#[allow(clippy::absurd_extreme_comparisons)]
if ancillary_data.len() < CHILD_RENDEZVOUS_ANCILLARY_DATA_LEN {
return Err(MessageError::MissingAncillary);
} else if ancillary_data.len() > CHILD_RENDEZVOUS_ANCILLARY_DATA_LEN {
return Err(MessageError::UnexpectedAncillaryData);
}
let mut data = Bytes::from(data);
let dumpable = data.try_get_u8()? != 0;
let child_pid = data.try_get_pid_ne()?;
let id = data.try_get_i32_ne()?;
let ancillary_data = ancillary_data.try_into().unwrap();
Ok(ChildProcessRendezVousReply {
dumpable,
child_pid,
id,
ancillary_data,
})
}
}