Bug 1974259 - Hold an OwnedHandle in an OverlappedOperation a=dmeehan

This avoids cancelling a potentially closed handle.

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

Differential Revision: https://phabricator.services.mozilla.com/D255385
This commit is contained in:
Alex Franchuk
2025-06-27 20:37:05 +00:00
committed by dmeehan@mozilla.com
parent e60e2adff4
commit 79a08e6f66
4 changed files with 36 additions and 16 deletions

View File

@@ -38,6 +38,8 @@ pub enum IPCError {
InvalidSize(#[from] TryFromIntError),
#[error("Error while parsing a file descriptor string")]
ParseError,
#[error("Failed to duplicate clone handle")]
CloneHandleFailed(#[source] std::io::Error),
}
#[derive(Debug, Error)]

View File

@@ -200,7 +200,9 @@ impl IPCConnector {
}
self.overlapped = Some(OverlappedOperation::sched_recv(
self.as_raw(),
self.handle
.try_clone()
.map_err(IPCError::CloneHandleFailed)?,
self.event_raw_handle(),
messages::HEADER_SIZE,
)?);
@@ -216,14 +218,24 @@ impl IPCConnector {
}
pub fn send(&self, buff: &[u8]) -> Result<(), IPCError> {
let overlapped =
OverlappedOperation::sched_send(self.as_raw(), self.event_raw_handle(), buff.to_vec())?;
let overlapped = OverlappedOperation::sched_send(
self.handle
.try_clone()
.map_err(IPCError::CloneHandleFailed)?,
self.event_raw_handle(),
buff.to_vec(),
)?;
overlapped.complete_send(/* wait */ false)
}
pub fn recv(&self, expected_size: usize) -> Result<(Vec<u8>, Option<AncillaryData>), IPCError> {
let overlapped =
OverlappedOperation::sched_recv(self.as_raw(), self.event_raw_handle(), expected_size)?;
let overlapped = OverlappedOperation::sched_recv(
self.handle
.try_clone()
.map_err(IPCError::CloneHandleFailed)?,
self.event_raw_handle(),
expected_size,
)?;
let buffer = overlapped.collect_recv(/* wait */ true)?;
Ok((buffer, None))
}

View File

@@ -53,7 +53,9 @@ impl IPCListener {
pub fn listen(&mut self) -> Result<(), IPCError> {
self.overlapped = Some(OverlappedOperation::listen(
self.handle.as_raw_handle() as HANDLE,
self.handle
.try_clone()
.map_err(IPCError::CloneHandleFailed)?,
self.event_raw_handle(),
)?);
Ok(())

View File

@@ -8,7 +8,7 @@ use crate::{
};
use std::{
mem::zeroed,
os::windows::io::{FromRawHandle, OwnedHandle, RawHandle},
os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle, RawHandle},
ptr::{null, null_mut},
};
use windows_sys::Win32::{
@@ -93,7 +93,7 @@ fn cancel_overlapped_io(handle: HANDLE, overlapped: &mut OVERLAPPED) -> bool {
}
pub(crate) struct OverlappedOperation {
handle: HANDLE,
handle: OwnedHandle,
overlapped: Option<Box<OVERLAPPED>>,
buffer: Option<Vec<u8>>,
}
@@ -104,12 +104,16 @@ enum OverlappedOperationType {
}
impl OverlappedOperation {
pub(crate) fn listen(handle: HANDLE, event: HANDLE) -> Result<OverlappedOperation, IPCError> {
pub(crate) fn listen(
handle: OwnedHandle,
event: HANDLE,
) -> Result<OverlappedOperation, IPCError> {
let mut overlapped = Self::overlapped_with_event(event)?;
// SAFETY: We guarantee that the handle and OVERLAPPED object are both
// valid and remain so while used by this function.
let res = unsafe { ConnectNamedPipe(handle, overlapped.as_mut()) };
let res =
unsafe { ConnectNamedPipe(handle.as_raw_handle() as HANDLE, overlapped.as_mut()) };
let error = get_last_error();
if res != FALSE {
@@ -173,7 +177,7 @@ impl OverlappedOperation {
// and thus guaranteed to be valid.
let res = unsafe {
GetOverlappedResultEx(
self.handle,
self.handle.as_raw_handle() as HANDLE,
overlapped.as_ref(),
&mut number_of_bytes_transferred,
if wait { IO_TIMEOUT as u32 } else { 0 },
@@ -202,7 +206,7 @@ impl OverlappedOperation {
}
pub(crate) fn sched_recv(
handle: HANDLE,
handle: OwnedHandle,
event: HANDLE,
expected_size: usize,
) -> Result<OverlappedOperation, IPCError> {
@@ -214,7 +218,7 @@ impl OverlappedOperation {
// duration of the asynchronous operation.
let res = unsafe {
ReadFile(
handle,
handle.as_raw_handle() as HANDLE,
buffer.as_mut_ptr(),
number_of_bytes_to_read,
null_mut(),
@@ -243,7 +247,7 @@ impl OverlappedOperation {
}
pub(crate) fn sched_send(
handle: HANDLE,
handle: OwnedHandle,
event: HANDLE,
mut buffer: Vec<u8>,
) -> Result<OverlappedOperation, IPCError> {
@@ -254,7 +258,7 @@ impl OverlappedOperation {
// duration of the asynchronous operation.
let res = unsafe {
WriteFile(
handle,
handle.as_raw_handle() as HANDLE,
buffer.as_mut_ptr(),
number_of_bytes_to_write,
null_mut(),
@@ -296,7 +300,7 @@ impl OverlappedOperation {
}
fn cancel_or_leak(&self, mut overlapped: Box<OVERLAPPED>, buffer: Option<Vec<u8>>) {
if !cancel_overlapped_io(self.handle, overlapped.as_mut()) {
if !cancel_overlapped_io(self.handle.as_raw_handle() as HANDLE, overlapped.as_mut()) {
// If we cannot cancel the operation we must leak the
// associated buffers so that they're available in case it
// ever completes.