Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Fix Overlapped I/O by boxing OVERLAPPED struct and accept overlapped …
…arg positionally

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.
  • Loading branch information
youknowone committed Feb 22, 2026
commit cda8656d59e2c7da843daaa54868530d811cefa9
34 changes: 19 additions & 15 deletions crates/vm/src/stdlib/_winapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -833,7 +837,7 @@ mod _winapi {

Overlapped {
inner: PyMutex::new(OverlappedInner {
overlapped,
overlapped: Box::new(overlapped),
handle,
pending: false,
completed: false,
Expand All @@ -858,7 +862,7 @@ mod _winapi {
let ret = unsafe {
GetOverlappedResult(
inner.handle,
&inner.overlapped,
&*inner.overlapped,
&mut transferred,
if wait { 1 } else { 0 },
)
Expand Down Expand Up @@ -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
};
Expand Down Expand Up @@ -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>,
}

Expand All @@ -982,7 +986,7 @@ mod _winapi {
unsafe {
windows_sys::Win32::System::Pipes::ConnectNamedPipe(
handle.0,
&mut inner.overlapped,
&mut *inner.overlapped,
)
}
};
Expand Down Expand Up @@ -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,
}

Expand Down Expand Up @@ -1233,7 +1237,7 @@ mod _winapi {
write_buf.as_ptr() as *const _,
len,
&mut written,
&mut inner.overlapped,
&mut *inner.overlapped,
)
};

Expand Down Expand Up @@ -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,
}

Expand Down Expand Up @@ -1319,7 +1323,7 @@ mod _winapi {
read_buf.as_mut_ptr() as *mut _,
size,
&mut nread,
&mut inner.overlapped,
&mut *inner.overlapped,
)
};

Expand Down
Loading