Bug 1975979 - Remove the crash helper listener socket on Linux and macOS a=dmeehan

Original Revision: https://phabricator.services.mozilla.com/D262410

Differential Revision: https://phabricator.services.mozilla.com/D263340
This commit is contained in:
Gabriele Svelto
2025-09-19 02:54:04 +00:00
committed by dmeehan@mozilla.com
parent d6674b563e
commit a2e0149356
16 changed files with 92 additions and 279 deletions

View File

@@ -5,5 +5,5 @@
package org.mozilla.gecko.crashhelper;
interface ICrashHelper {
boolean start(in int mainProcessPid, in ParcelFileDescriptor breakpadFd, in String minidumpPath, in ParcelFileDescriptor listenFd, in ParcelFileDescriptor serverFd);
boolean start(in ParcelFileDescriptor breakpadFd, in String minidumpPath, in ParcelFileDescriptor serverFd);
}

View File

@@ -13,7 +13,6 @@ import android.os.Binder;
import android.os.DeadObjectException;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.system.ErrnoException;
import android.system.Os;
@@ -43,10 +42,8 @@ public final class CrashHelper extends Service {
private static class CrashHelperBinder extends ICrashHelper.Stub {
@Override
public boolean start(
final int clientPid,
final ParcelFileDescriptor breakpadFd,
final String minidumpPath,
final ParcelFileDescriptor listenFd,
final ParcelFileDescriptor serverFd) {
// Launch the crash helper code, this will spin out a thread which will
// handle the IPC with Firefox' other processes. When running junit tests
@@ -56,8 +53,7 @@ public final class CrashHelper extends Service {
// "steal" the crash report of another main process. We should add
// additional separation within the crash generation code to prevent this
// from happening even though it's very unlikely.
CrashHelper.crash_generator(
clientPid, breakpadFd.detachFd(), minidumpPath, listenFd.detachFd(), serverFd.detachFd());
CrashHelper.crash_generator(breakpadFd.detachFd(), minidumpPath, serverFd.detachFd());
return false;
}
@@ -71,17 +67,14 @@ public final class CrashHelper extends Service {
public static ServiceConnection createConnection(
final ParcelFileDescriptor breakpadFd,
final String minidumpPath,
final ParcelFileDescriptor listenFd,
final ParcelFileDescriptor serverFd) {
class CrashHelperConnection implements ServiceConnection {
public CrashHelperConnection(
final ParcelFileDescriptor breakpadFd,
final String minidumpPath,
final ParcelFileDescriptor listenFd,
final ParcelFileDescriptor serverFd) {
mBreakpadFd = breakpadFd;
mMinidumpPath = minidumpPath;
mListenFd = listenFd;
mServerFd = serverFd;
}
@@ -89,7 +82,7 @@ public final class CrashHelper extends Service {
public void onServiceConnected(final ComponentName name, final IBinder service) {
final ICrashHelper helper = ICrashHelper.Stub.asInterface(service);
try {
helper.start(Process.myPid(), mBreakpadFd, mMinidumpPath, mListenFd, mServerFd);
helper.start(mBreakpadFd, mMinidumpPath, mServerFd);
} catch (final DeadObjectException e) {
// The crash helper process died before we could start it, presumably
// because of an out-of-memory condition. We don't attempt to restart
@@ -107,24 +100,21 @@ public final class CrashHelper extends Service {
ParcelFileDescriptor mBreakpadFd;
String mMinidumpPath;
ParcelFileDescriptor mListenFd;
ParcelFileDescriptor mServerFd;
}
return new CrashHelperConnection(breakpadFd, minidumpPath, listenFd, serverFd);
return new CrashHelperConnection(breakpadFd, minidumpPath, serverFd);
}
public static final class Pipes {
public final ParcelFileDescriptor mBreakpadClient;
public final ParcelFileDescriptor mBreakpadServer;
public final ParcelFileDescriptor mListener;
public final ParcelFileDescriptor mClient;
public final ParcelFileDescriptor mServer;
public Pipes(
final FileDescriptor breakpadClientFd,
final FileDescriptor breakpadServerFd,
final FileDescriptor listenerFd,
final FileDescriptor clientFd,
final FileDescriptor serverFd)
throws IOException {
@@ -133,10 +123,6 @@ public final class CrashHelper extends Service {
if (!CrashHelper.set_breakpad_opts(mBreakpadServer.getFd())) {
throw new IOException("Could not set the proper options on the Breakpad socket");
}
mListener = ParcelFileDescriptor.dup(listenerFd);
if (!CrashHelper.bind_and_listen(mListener.getFd())) {
throw new IOException("Could not listen on incoming connections");
}
mClient = ParcelFileDescriptor.dup(clientFd);
mServer = ParcelFileDescriptor.dup(serverFd);
}
@@ -151,8 +137,8 @@ public final class CrashHelper extends Service {
public static Pipes createCrashHelperPipes(final Context context) {
try {
// We can't set the required socket options for the Breakpad server socket
// or our own listener from here, so we delegate those parts to native
// functions in crashhelper_android.cpp.
// so we delegate those parts to native functions in
// crashhelper_android.cpp.
GeckoLoader.doLoadLibrary(null, "crashhelper");
final FileDescriptor breakpad_client_fd = new FileDescriptor();
@@ -163,14 +149,11 @@ public final class CrashHelper extends Service {
0,
breakpad_client_fd,
breakpad_server_fd);
final FileDescriptor listener_fd =
Os.socket(OsConstants.AF_UNIX, OsConstants.SOCK_SEQPACKET, 0);
final FileDescriptor client_fd = new FileDescriptor();
final FileDescriptor server_fd = new FileDescriptor();
Os.socketpair(OsConstants.AF_UNIX, OsConstants.SOCK_SEQPACKET, 0, client_fd, server_fd);
final Pipes pipes =
new Pipes(breakpad_client_fd, breakpad_server_fd, listener_fd, client_fd, server_fd);
final Pipes pipes = new Pipes(breakpad_client_fd, breakpad_server_fd, client_fd, server_fd);
// Manually close all the file descriptors we created.
// ParcelFileDescriptor instances in the Pipes object will close their
@@ -178,7 +161,6 @@ public final class CrashHelper extends Service {
// not, leaving us the job to clean them up.
Os.close(breakpad_client_fd);
Os.close(breakpad_server_fd);
Os.close(listener_fd);
Os.close(client_fd);
Os.close(server_fd);
return pipes;
@@ -192,10 +174,7 @@ public final class CrashHelper extends Service {
// `stopWithTask` flag set in the manifest, so the service manager will
// tear it down for us.
protected static native void crash_generator(
int clientPid, int breakpadFd, String minidumpPath, int listenFd, int serverFd);
protected static native void crash_generator(int breakpadFd, String minidumpPath, int serverFd);
protected static native boolean set_breakpad_opts(int breakpadFd);
protected static native boolean bind_and_listen(int listenFd);
}

View File

@@ -459,8 +459,7 @@ public final class GeckoRuntime implements Parcelable {
final File minidumps = new File(context.getFilesDir(), "minidumps");
context.bindService(
i,
CrashHelper.createConnection(
pipes.mBreakpadServer, minidumps.getPath(), pipes.mListener, pipes.mServer),
CrashHelper.createConnection(pipes.mBreakpadServer, minidumps.getPath(), pipes.mServer),
Context.BIND_AUTO_CREATE);
} catch (final ClassNotFoundException e) {
Log.w(LOGTAG, "Couldn't find the crash helper class");

View File

@@ -49,16 +49,31 @@ static void free_breakpad_data(BreakpadRawData aData) {
#endif
}
#define GET_CLIENT_PID_ARG(arguments) ((arguments)[1])
#define GET_BREAKPAD_DATA_ARG(arguments) ((arguments)[2])
#define GET_MINIDUMP_PATH_ARG(arguments) ((arguments)[3])
#define GET_CONNECTOR_ARG(arguments) ((arguments)[4])
#ifdef XP_WIN
# define GET_LISTENER_ARG(arguments) ((arguments)[5])
# define ARG_NUM (6)
#else
static char sDummy[1] = "";
# define GET_LISTENER_ARG(arguments) (sDummy)
# define ARG_NUM (5)
#endif // XP_WIN
int main(int argc, char* argv[]) {
if (argc < 6) {
if (argc < ARG_NUM) {
exit(EXIT_FAILURE);
}
Pid client_pid = static_cast<Pid>(parse_int_or_exit(argv[1]));
BreakpadRawData breakpad_data = parse_breakpad_data(argv[2]);
char* minidump_path = argv[3];
char* listener = argv[4];
char* connector = argv[5];
Pid client_pid =
static_cast<Pid>(parse_int_or_exit(GET_CLIENT_PID_ARG(argv)));
BreakpadRawData breakpad_data =
parse_breakpad_data(GET_BREAKPAD_DATA_ARG(argv));
char* minidump_path = GET_MINIDUMP_PATH_ARG(argv);
char* connector = GET_CONNECTOR_ARG(argv);
char* listener = GET_LISTENER_ARG(argv);
int res = crash_generator_logic_desktop(client_pid, breakpad_data,
minidump_path, listener, connector);

View File

@@ -30,36 +30,10 @@ Java_org_mozilla_gecko_crashhelper_CrashHelper_set_1breakpad_1opts(
return true;
}
extern "C" JNIEXPORT jboolean JNICALL
Java_org_mozilla_gecko_crashhelper_CrashHelper_bind_1and_1listen(
JNIEnv* jenv, jclass, jint listen_fd) {
struct sockaddr_un addr = {
.sun_family = AF_UNIX,
.sun_path = {},
};
// The address' path deliberately starts with a null byte to inform the
// kernel that this is an abstract address and not an actual file path.
snprintf(addr.sun_path + 1, sizeof(addr.sun_path) - 2,
"gecko-crash-helper-pipe.%d", getpid());
int res = bind(listen_fd, (const struct sockaddr*)&addr, sizeof(addr));
if (res < 0) {
return false;
}
res = listen(listen_fd, 1);
if (res < 0) {
return false;
}
return true;
}
extern "C" JNIEXPORT void JNICALL
Java_org_mozilla_gecko_crashhelper_CrashHelper_crash_1generator(
JNIEnv* jenv, jclass, jint client_pid, jint breakpad_fd,
jstring minidump_path, jint listen_fd, jint server_fd) {
JNIEnv* jenv, jclass, jint breakpad_fd, jstring minidump_path,
jint server_fd) {
// The breakpad server socket needs to be put in non-blocking mode, we do it
// here as the Rust code that picks it up won't touch it anymore and just
// pass it along to Breakpad.
@@ -79,7 +53,6 @@ Java_org_mozilla_gecko_crashhelper_CrashHelper_crash_1generator(
const char* minidump_path_str =
jenv->GetStringUTFChars(minidump_path, nullptr);
crash_generator_logic_android(client_pid, breakpad_fd, minidump_path_str,
listen_fd, server_fd);
crash_generator_logic_android(breakpad_fd, minidump_path_str, server_fd);
jenv->ReleaseStringUTFChars(minidump_path, minidump_path_str);
}

View File

@@ -3,9 +3,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
use anyhow::Result;
use crash_helper_common::{
ignore_eintr, BreakpadChar, BreakpadData, IPCChannel, IPCConnector, IPCListener,
};
use crash_helper_common::{ignore_eintr, BreakpadChar, BreakpadData, IPCChannel, IPCConnector};
use nix::{
spawn::{posix_spawn, PosixSpawnAttr, PosixSpawnFileActions},
sys::wait::waitpid,
@@ -25,12 +23,11 @@ impl CrashHelperClient {
minidump_path: *const BreakpadChar,
) -> Result<CrashHelperClient> {
let channel = IPCChannel::new()?;
let (listener, server_endpoint, client_endpoint) = channel.deconstruct();
let (_listener, server_endpoint, client_endpoint) = channel.deconstruct();
CrashHelperClient::spawn_crash_helper(
program,
breakpad_data,
minidump_path,
listener,
server_endpoint,
)?;
@@ -45,7 +42,6 @@ impl CrashHelperClient {
program: *const BreakpadChar,
breakpad_data: BreakpadData,
minidump_path: *const BreakpadChar,
listener: IPCListener,
endpoint: IPCConnector,
) -> Result<()> {
let parent_pid = getpid().to_string();
@@ -54,7 +50,6 @@ impl CrashHelperClient {
let breakpad_data_arg =
unsafe { CString::from_vec_unchecked(breakpad_data.to_string().into_bytes()) };
let minidump_path = unsafe { CStr::from_ptr(minidump_path) };
let listener_arg = listener.serialize();
let endpoint_arg = endpoint.serialize();
let file_actions = PosixSpawnFileActions::init()?;
@@ -74,7 +69,6 @@ impl CrashHelperClient {
&parent_pid_arg,
&breakpad_data_arg,
minidump_path,
&listener_arg,
&endpoint_arg,
],
env.as_slice(),

View File

@@ -44,8 +44,8 @@ impl CrashHelperClient {
program,
breakpad_data,
minidump_path,
listener,
server_endpoint,
listener,
)
});
@@ -60,8 +60,8 @@ impl CrashHelperClient {
program: OsString,
breakpad_data: BreakpadData,
minidump_path: OsString,
listener: IPCListener,
endpoint: IPCConnector,
listener: IPCListener,
) -> Result<OwnedHandle> {
// SAFETY: `GetCurrentProcessId()` takes no arguments and should always work
let pid = OsString::from(unsafe { GetCurrentProcessId() }.to_string());
@@ -74,9 +74,9 @@ impl CrashHelperClient {
cmd_line.push(" ");
cmd_line.push(escape_cmd_line_arg(&minidump_path));
cmd_line.push(" ");
cmd_line.push(escape_cmd_line_arg(&listener.serialize()));
cmd_line.push(" ");
cmd_line.push(escape_cmd_line_arg(&endpoint.serialize()));
cmd_line.push(" ");
cmd_line.push(escape_cmd_line_arg(&listener.serialize()));
cmd_line.push("\0");
let mut cmd_line: Vec<u16> = cmd_line.encode_wide().collect();

View File

@@ -4,19 +4,19 @@
#[cfg(any(target_os = "android", target_os = "linux"))]
use crate::platform::linux::{
server_addr, set_socket_cloexec, set_socket_default_flags, unix_socket,
set_socket_cloexec, set_socket_default_flags,
};
#[cfg(target_os = "macos")]
use crate::platform::macos::{
server_addr, set_socket_cloexec, set_socket_default_flags, unix_socket,
set_socket_cloexec, set_socket_default_flags,
};
use crate::{ignore_eintr, Pid, ProcessHandle, IO_TIMEOUT};
use crate::{ignore_eintr, ProcessHandle, IO_TIMEOUT};
use nix::{
cmsg_space,
errno::Errno,
poll::{poll, PollFd, PollFlags, PollTimeout},
sys::socket::{connect, recvmsg, sendmsg, ControlMessage, ControlMessageOwned, MsgFlags},
sys::socket::{recvmsg, sendmsg, ControlMessage, ControlMessageOwned, MsgFlags},
};
use std::{
ffi::{CStr, CString},
@@ -61,40 +61,6 @@ impl IPCConnector {
IPCConnector::from_fd(unsafe { OwnedFd::from_raw_fd(ancillary_data) })
}
/// Create a new connector by connecting it to the process specified by
/// `pid`. The `FD_CLOEXEC` flag will be set on the underlying socket and
/// thus it will not be possible to inerhit this connector in a child
/// process.
pub fn connect(pid: Pid) -> Result<IPCConnector, IPCError> {
let socket = unix_socket().map_err(IPCError::ConnectionFailure)?;
set_socket_default_flags(socket.as_fd()).map_err(IPCError::ConnectionFailure)?;
set_socket_cloexec(socket.as_fd()).map_err(IPCError::ConnectionFailure)?;
let server_addr = server_addr(pid).map_err(IPCError::ConnectionFailure)?;
loop {
let timeout = PollTimeout::from(IO_TIMEOUT);
let res = ignore_eintr!(poll(
&mut [PollFd::new(socket.as_fd(), PollFlags::POLLOUT)],
timeout
));
match res {
Err(e) => return Err(IPCError::ConnectionFailure(e)),
Ok(_res @ 0) => return Err(IPCError::ConnectionFailure(Errno::ETIMEDOUT)),
Ok(_) => {}
}
let res = ignore_eintr!(connect(socket.as_raw_fd(), &server_addr));
match res {
Ok(_) => break,
Err(_e @ Errno::EAGAIN) => continue, // Retry, the helper might not be ready yet
Err(e) => return Err(IPCError::ConnectionFailure(e)),
}
}
Ok(IPCConnector { socket })
}
/// Serialize this connector into a string that can be passed on the
/// command-line to a child process. This only works for newly
/// created connectors because they are explicitly created as inheritable.
@@ -131,7 +97,7 @@ impl IPCConnector {
Ok(self.raw_fd())
}
pub fn as_raw_ref(&self) -> BorrowedFd {
pub fn as_raw_ref(&self) -> BorrowedFd<'_> {
self.socket.as_fd()
}

View File

@@ -2,77 +2,24 @@
* 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(any(target_os = "android", target_os = "linux"))]
use crate::platform::linux::{
server_addr, set_socket_cloexec, set_socket_default_flags, unix_socket,
};
#[cfg(target_os = "macos")]
use crate::platform::macos::{
server_addr, set_socket_cloexec, set_socket_default_flags, unix_socket,
};
use crate::{errors::IPCError, IPCConnector, Pid};
use std::ffi::CStr;
use nix::sys::socket::{accept, bind, listen, Backlog};
use std::{
ffi::{CStr, CString},
os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd},
str::FromStr,
};
use crate::{errors::IPCError, Pid};
pub struct IPCListener {
socket: OwnedFd,
}
pub struct IPCListener {}
impl IPCListener {
/// Create a new listener with an address based on `pid`. The underlying
/// socket will not have the `FD_CLOEXEC` flag set and thus can be
/// inherited by child processes.
pub fn new(pid: Pid) -> Result<IPCListener, IPCError> {
let socket = unix_socket().map_err(IPCError::System)?;
set_socket_default_flags(socket.as_fd()).map_err(IPCError::System)?;
let server_addr = server_addr(pid).map_err(IPCError::System)?;
bind(socket.as_fd().as_raw_fd(), &server_addr).map_err(IPCError::System)?;
listen(&socket, Backlog::new(1).unwrap()).map_err(IPCError::ListenFailed)?;
Ok(IPCListener { socket })
}
/// Create a new listener using an already prepared socket. The listener
/// must have been bound to the appropriate address and should already be
/// listening on incoming connections. This will set the `FD_CLOEXEC` flag
/// on the underlying socket and thus will make this litener not inheritable
/// by child processes.
pub fn from_fd(_pid: Pid, socket: OwnedFd) -> Result<IPCListener, IPCError> {
set_socket_cloexec(socket.as_fd()).map_err(IPCError::System)?;
Ok(IPCListener { socket })
}
/// Serialize this listener into a string that can be passed on the
/// command-line to a child process. This only works for newly
/// created listeners because they are explicitly created as inheritable.
pub fn serialize(&self) -> CString {
CString::new(self.socket.as_raw_fd().to_string()).unwrap()
/// Create a new dummy listener. This is not used on Linux and macOS but
/// we keep the type around so that the shared logic is the same as for
/// Windows where this type is used.
pub fn new(_pid: Pid) -> Result<IPCListener, IPCError> {
Ok(IPCListener {})
}
/// Deserialize a listener from an argument passed on the command-line.
/// The resulting listener is ready to accept new connections.
pub fn deserialize(string: &CStr, _pid: Pid) -> Result<IPCListener, IPCError> {
let string = string.to_str().map_err(|_e| IPCError::ParseError)?;
let fd = RawFd::from_str(string).map_err(|_e| IPCError::ParseError)?;
// SAFETY: This is a file descriptor we passed in ourselves.
let socket = unsafe { OwnedFd::from_raw_fd(fd) };
Ok(IPCListener { socket })
}
pub fn accept(&self) -> Result<IPCConnector, IPCError> {
let socket = accept(self.socket.as_fd().as_raw_fd()).map_err(IPCError::AcceptFailed)?;
// SAFETY: `socket` is guaranteed to be valid at this point.
IPCConnector::from_fd(unsafe { OwnedFd::from_raw_fd(socket) })
}
pub fn as_raw_ref(&self) -> BorrowedFd {
self.socket.as_fd()
/// This produces a dummy listener and is only kept to provide shared logic
/// with Windows.
pub fn deserialize(_string: &CStr, _pid: Pid) -> Result<IPCListener, IPCError> {
Ok(IPCListener {})
}
}

View File

@@ -20,7 +20,10 @@ pub use crate::breakpad::{BreakpadChar, BreakpadData, BreakpadRawData, Pid};
pub use crate::ipc_channel::{IPCChannel, IPCClientChannel};
pub use crate::ipc_connector::{AncillaryData, IPCConnector, IPCEvent, INVALID_ANCILLARY_DATA};
pub use crate::ipc_listener::IPCListener;
pub use crate::platform::{server_addr, ProcessHandle};
pub use crate::platform::ProcessHandle;
#[cfg(target_os = "windows")]
pub use crate::platform::server_addr;
/// OsString extensions to convert from/to C strings. The strings will be
/// regular nul-terminated byte strings on most platforms but will use wide

View File

@@ -9,13 +9,13 @@ pub use windows::{server_addr, ProcessHandle};
pub(crate) mod windows;
#[cfg(any(target_os = "android", target_os = "linux"))]
pub use linux::{server_addr, ProcessHandle};
pub use linux::ProcessHandle;
#[cfg(any(target_os = "android", target_os = "linux"))]
pub(crate) mod linux;
#[cfg(target_os = "macos")]
pub use macos::{server_addr, ProcessHandle};
pub use macos::ProcessHandle;
#[cfg(target_os = "macos")]
pub(crate) mod macos;

View File

@@ -2,33 +2,21 @@
* 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 crate::Pid;
use nix::{
fcntl::{
fcntl,
FcntlArg::{F_GETFL, F_SETFD, F_SETFL},
FdFlag, OFlag,
},
sys::socket::{socket, socketpair, AddressFamily, SockFlag, SockType, UnixAddr},
sys::socket::{socketpair, AddressFamily, SockFlag, SockType},
Result,
};
use std::{
env,
os::fd::{BorrowedFd, OwnedFd},
};
pub type ProcessHandle = ();
pub(crate) fn unix_socket() -> Result<OwnedFd> {
socket(
AddressFamily::Unix,
SockType::SeqPacket,
SockFlag::empty(),
None,
)
}
pub(crate) fn unix_socketpair() -> Result<(OwnedFd, OwnedFd)> {
socketpair(
AddressFamily::Unix,
@@ -47,12 +35,3 @@ pub(crate) fn set_socket_default_flags(socket: BorrowedFd) -> Result<()> {
pub(crate) fn set_socket_cloexec(socket: BorrowedFd) -> Result<()> {
fcntl(socket, F_SETFD(FdFlag::FD_CLOEXEC)).map(|_res| ())
}
pub fn server_addr(pid: Pid) -> Result<UnixAddr> {
let server_name = if let Ok(snap_instance_name) = env::var("SNAP_INSTANCE_NAME") {
format!("snap.{snap_instance_name:}.gecko-crash-helper-pipe.{pid:}")
} else {
format!("gecko-crash-helper-pipe.{pid:}")
};
UnixAddr::new_abstract(server_name.as_bytes())
}

View File

@@ -2,8 +2,6 @@
* 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 crate::Pid;
use nix::{
errno::Errno,
fcntl::{
@@ -12,27 +10,16 @@ use nix::{
FdFlag, OFlag,
},
libc::{setsockopt, SOL_SOCKET, SO_NOSIGPIPE},
sys::socket::{socket, socketpair, AddressFamily, SockFlag, SockType, UnixAddr},
sys::socket::{socketpair, AddressFamily, SockFlag, SockType},
Result,
};
use std::{
mem::size_of,
os::fd::{AsRawFd, BorrowedFd, OwnedFd},
path::PathBuf,
str::FromStr,
};
pub type ProcessHandle = ();
pub(crate) fn unix_socket() -> Result<OwnedFd> {
socket(
AddressFamily::Unix,
SockType::Stream,
SockFlag::empty(),
None,
)
}
pub(crate) fn unix_socketpair() -> Result<(OwnedFd, OwnedFd)> {
socketpair(
AddressFamily::Unix,
@@ -70,11 +57,3 @@ pub(crate) fn set_socket_default_flags(socket: BorrowedFd) -> Result<()> {
pub(crate) fn set_socket_cloexec(socket: BorrowedFd) -> Result<()> {
fcntl(socket, F_SETFD(FdFlag::FD_CLOEXEC)).map(|_res| ())
}
pub fn server_addr(pid: Pid) -> Result<UnixAddr> {
// macOS doesn't seem to support abstract paths as addresses for Unix
// protocol sockets, so this needs to be the path of an actual file.
let server_name = format!("/tmp/gecko-crash-helper-pipe.{pid:}");
let server_path = PathBuf::from_str(&server_name).unwrap();
UnixAddr::new(&server_path)
}

View File

@@ -32,6 +32,7 @@ struct IPCConnection {
}
pub(crate) struct IPCServer {
#[cfg_attr(unix, allow(dead_code))]
listener: IPCListener,
connections: Vec<IPCConnection>,
}

View File

@@ -3,17 +3,13 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
use crash_helper_common::{errors::IPCError, ignore_eintr, IPCEvent};
use nix::{
errno::Errno,
poll::{poll, PollFd, PollFlags, PollTimeout},
};
use nix::poll::{poll, PollFd, PollFlags, PollTimeout};
use super::IPCServer;
impl IPCServer {
pub fn wait_for_events(&mut self) -> Result<Vec<IPCEvent>, IPCError> {
let mut pollfds = Vec::with_capacity(1 + self.connections.len());
pollfds.push(PollFd::new(self.listener.as_raw_ref(), PollFlags::POLLIN));
let mut pollfds = Vec::with_capacity(self.connections.len());
pollfds.extend(
self.connections.iter().map(|connection| {
PollFd::new(connection.connector.as_raw_ref(), PollFlags::POLLIN)
@@ -31,8 +27,7 @@ impl IPCServer {
let revents = pollfd.revents().unwrap();
if revents.contains(PollFlags::POLLHUP) {
if index > 0 {
events.push(IPCEvent::Disconnect(index - 1));
events.push(IPCEvent::Disconnect(index));
// If a process was disconnected then skip all further
// processing of the socket. This wouldn't matter normally,
// but on macOS calling recvmsg() on a hung-up socket seems
@@ -40,30 +35,19 @@ impl IPCServer {
// in the past. Doing things this way avoids the panic
// while having no real downsides.
continue;
} else {
// This should never happen, unless the listener socket was
// not set up properly or a failure happened during setup.
return Err(IPCError::System(Errno::EFAULT));
}
}
if revents.contains(PollFlags::POLLIN) {
if index == 0 {
if let Ok(connector) = self.listener.accept() {
events.push(IPCEvent::Connect(connector));
}
} else {
// SAFETY: The index is guaranteed to be >0 and within
// the bounds of the connections array.
let connection = unsafe { self.connections.get_unchecked(index - 1) };
let connection = unsafe { self.connections.get_unchecked(index) };
let header = connection.connector.recv_header();
if let Ok(header) = header {
// Note that if we encounter a failure we don't propagate
// it, when the socket gets disconnected we'll get a
// POLLHUP event anyway so deal with disconnections there
// instead of here.
events.push(IPCEvent::Header(index - 1, header));
}
events.push(IPCEvent::Header(index, header));
}
}

View File

@@ -11,7 +11,9 @@ mod ipc_server;
mod logging;
mod phc;
use crash_helper_common::{BreakpadData, BreakpadRawData, IPCConnector, IPCListener, Pid};
#[cfg(not(target_os = "android"))]
use crash_helper_common::Pid;
use crash_helper_common::{BreakpadData, BreakpadRawData, IPCConnector, IPCListener};
use std::ffi::{c_char, CStr, OsString};
use crash_generation::CrashGenerator;
@@ -82,17 +84,14 @@ pub unsafe extern "C" fn crash_generator_logic_desktop(
///
/// # Safety
///
/// `minidump_data` must point to valid, nul-terminated C strings. `listener`
/// and `server_pipe` must be valid file descriptors and `breakpad_data` must
/// also be a valid file descriptor compatible with Breakpad's crash generation
/// server.
/// `minidump_data` must point to valid, nul-terminated C strings. `server_pipe`
/// must be a valid file descriptor and `breakpad_data` must also be a valid
/// file descriptor compatible with Breakpad's crash generation server.
#[cfg(target_os = "android")]
#[no_mangle]
pub unsafe extern "C" fn crash_generator_logic_android(
client_pid: Pid,
breakpad_data: BreakpadRawData,
minidump_path: *const c_char,
listener: RawFd,
pipe: RawFd,
) {
logging::init();
@@ -110,12 +109,7 @@ pub unsafe extern "C" fn crash_generator_logic_android(
})
.unwrap();
let listener = unsafe { OwnedFd::from_raw_fd(listener) };
let listener = IPCListener::from_fd(client_pid, listener)
.map_err(|error| {
log::error!("Could not use the listener (error: {error})");
})
.unwrap();
let listener = IPCListener::new(0).unwrap();
let pipe = unsafe { OwnedFd::from_raw_fd(pipe) };
let connector = IPCConnector::from_fd(pipe)
.map_err(|error| {