Fix Overlapped I/O by boxing OVERLAPPED struct and accept overlapped arg positionally (#7198)

Two fixes in _winapi:

1. Box the OVERLAPPED struct in OverlappedInner to ensure it stays at
   a stable heap address. Previously, into_pyobject() moved the struct
   after ReadFile/WriteFile had given the OS a pointer to it, causing
   GetOverlappedResult to read stale data (returning err=0 instead of
   ERROR_MORE_DATA for zero-byte reads on message pipes).

2. Change ReadFile, WriteFile, ConnectNamedPipe overlapped parameter
   from #[pyarg(named)] to #[pyarg(any)] so it can be passed both
   positionally and as keyword argument.
This commit is contained in:
Jeong, YunWon authored and GitHub committed 2026-02-22 22:14:06 +09:00
1 parent 8a74956590
commit 9014ecc6cd
1 file changed
+19 -15
+19 -15
View File
@@ -800,7 +800,11 @@ mod _winapi {
}
struct OverlappedInner {
overlapped: windows_sys::Win32::System::IO::OVERLAPPED,
// Box ensures the OVERLAPPED struct stays at a stable heap address
// even when the containing Overlapped Python object is moved during
// into_pyobject(). The OS holds a pointer to this struct for pending
// I/O operations, so it must not be relocated.
overlapped: Box<windows_sys::Win32::System::IO::OVERLAPPED>,
handle: HANDLE,
pending: bool,
completed: bool,
@@ -833,7 +837,7 @@ mod _winapi {
Overlapped {
inner: PyMutex::new(OverlappedInner {
overlapped,
overlapped: Box::new(overlapped),
handle,
pending: false,
completed: false,
@@ -858,7 +862,7 @@ mod _winapi {
let ret = unsafe {
GetOverlappedResult(
inner.handle,
&inner.overlapped,
&*inner.overlapped,
&mut transferred,
if wait { 1 } else { 0 },
)
@@ -913,7 +917,7 @@ mod _winapi {
let mut inner = self.inner.lock();
let ret = if inner.pending {
unsafe { CancelIoEx(inner.handle, &inner.overlapped) }
unsafe { CancelIoEx(inner.handle, &*inner.overlapped) }
} else {
1
};
@@ -958,9 +962,9 @@ mod _winapi {
/// ConnectNamedPipe - Wait for a client to connect to a named pipe
#[derive(FromArgs)]
struct ConnectNamedPipeArgs {
#[pyarg(positional)]
#[pyarg(any)]
handle: WinHandle,
#[pyarg(named, optional)]
#[pyarg(any, optional)]
overlapped: OptionalArg<bool>,
}
@@ -982,7 +986,7 @@ mod _winapi {
unsafe {
windows_sys::Win32::System::Pipes::ConnectNamedPipe(
handle.0,
&mut inner.overlapped,
&mut *inner.overlapped,
)
}
};
@@ -1200,11 +1204,11 @@ mod _winapi {
#[derive(FromArgs)]
struct WriteFileArgs {
#[pyarg(positional)]
#[pyarg(any)]
handle: WinHandle,
#[pyarg(positional)]
#[pyarg(any)]
buffer: crate::function::ArgBytesLike,
#[pyarg(named, default = false)]
#[pyarg(any, default = false)]
overlapped: bool,
}
@@ -1233,7 +1237,7 @@ mod _winapi {
write_buf.as_ptr() as *const _,
len,
&mut written,
&mut inner.overlapped,
&mut *inner.overlapped,
)
};
@@ -1287,11 +1291,11 @@ mod _winapi {
#[derive(FromArgs)]
struct ReadFileArgs {
#[pyarg(positional)]
#[pyarg(any)]
handle: WinHandle,
#[pyarg(positional)]
#[pyarg(any)]
size: u32,
#[pyarg(named, default = false)]
#[pyarg(any, default = false)]
overlapped: bool,
}
@@ -1319,7 +1323,7 @@ mod _winapi {
read_buf.as_mut_ptr() as *mut _,
size,
&mut nread,
&mut inner.overlapped,
&mut *inner.overlapped,
)
};