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), InvalidSize(#[from] TryFromIntError),
#[error("Error while parsing a file descriptor string")] #[error("Error while parsing a file descriptor string")]
ParseError, ParseError,
#[error("Failed to duplicate clone handle")]
CloneHandleFailed(#[source] std::io::Error),
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]

View File

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

View File

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

View File

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