From 66faaa817ac28a64c05964cd0aa4999d793a1d1c Mon Sep 17 00:00:00 2001
From: Chris Denton <christophersdenton@gmail.com>
Date: Mon, 28 Mar 2022 08:42:59 +0100
Subject: [PATCH 1/5] Correct definition of `IO_STATUS_BLOCK`

---
 library/std/src/sys/windows/c.rs | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
index 0ecc2a5cfdad5..9e72db42dce6d 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -316,15 +316,21 @@ impl Default for OBJECT_ATTRIBUTES {
     }
 }
 #[repr(C)]
-pub struct IO_STATUS_BLOCK {
-    pub Pointer: *mut c_void,
-    pub Information: usize,
+union IO_STATUS_BLOCK_union {
+    Status: NTSTATUS,
+    Pointer: *mut c_void,
 }
-impl Default for IO_STATUS_BLOCK {
+impl Default for IO_STATUS_BLOCK_union {
     fn default() -> Self {
-        Self { Pointer: ptr::null_mut(), Information: 0 }
+        Self { Pointer: ptr::null_mut() }
     }
 }
+#[repr(C)]
+#[derive(Default)]
+pub struct IO_STATUS_BLOCK {
+    u: IO_STATUS_BLOCK_union,
+    pub Information: usize,
+}
 
 pub type LPOVERLAPPED_COMPLETION_ROUTINE = unsafe extern "system" fn(
     dwErrorCode: DWORD,

From 36aa75e44d5c6a83d9cb61eeaaadf82354307015 Mon Sep 17 00:00:00 2001
From: Chris Denton <christophersdenton@gmail.com>
Date: Wed, 30 Mar 2022 10:38:22 +0100
Subject: [PATCH 2/5] Complete reads and writes synchronously or abort

---
 library/std/src/sys/windows/c.rs      |  35 ++++++
 library/std/src/sys/windows/handle.rs | 168 ++++++++++++++++----------
 2 files changed, 137 insertions(+), 66 deletions(-)

diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
index 9e72db42dce6d..1528db7362e64 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -274,6 +274,9 @@ pub const STATUS_SUCCESS: NTSTATUS = 0x00000000;
 pub const STATUS_DELETE_PENDING: NTSTATUS = 0xc0000056_u32 as _;
 pub const STATUS_INVALID_PARAMETER: NTSTATUS = 0xc000000d_u32 as _;
 
+pub const STATUS_PENDING: NTSTATUS = 0x103 as _;
+pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011_u32 as _;
+
 // Equivalent to the `NT_SUCCESS` C preprocessor macro.
 // See: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/using-ntstatus-values
 pub fn nt_success(status: NTSTATUS) -> bool {
@@ -338,6 +341,12 @@ pub type LPOVERLAPPED_COMPLETION_ROUTINE = unsafe extern "system" fn(
     lpOverlapped: *mut OVERLAPPED,
 );
 
+type IO_APC_ROUTINE = unsafe extern "system" fn(
+    ApcContext: *mut c_void,
+    IoStatusBlock: *mut IO_STATUS_BLOCK,
+    Reserved: ULONG,
+);
+
 #[repr(C)]
 #[cfg(not(target_pointer_width = "64"))]
 pub struct WSADATA {
@@ -1274,6 +1283,32 @@ compat_fn! {
     ) -> NTSTATUS {
         panic!("`NtCreateFile` not available");
     }
+    pub fn NtReadFile(
+        FileHandle: BorrowedHandle<'_>,
+        Event: HANDLE,
+        ApcRoutine: Option<IO_APC_ROUTINE>,
+        ApcContext: *mut c_void,
+        IoStatusBlock: &mut IO_STATUS_BLOCK,
+        Buffer: *mut crate::mem::MaybeUninit<u8>,
+        Length: ULONG,
+        ByteOffset: Option<&LARGE_INTEGER>,
+        Key: Option<&ULONG>
+    ) -> NTSTATUS {
+        panic!("`NtReadFile` not available");
+    }
+    pub fn NtWriteFile(
+        FileHandle: BorrowedHandle<'_>,
+        Event: HANDLE,
+        ApcRoutine: Option<IO_APC_ROUTINE>,
+        ApcContext: *mut c_void,
+        IoStatusBlock: &mut IO_STATUS_BLOCK,
+        Buffer: *const u8,
+        Length: ULONG,
+        ByteOffset: Option<&LARGE_INTEGER>,
+        Key: Option<&ULONG>
+    ) -> NTSTATUS {
+        panic!("`NtWriteFile` not available");
+    }
     pub fn RtlNtStatusToDosError(
         Status: NTSTATUS
     ) -> ULONG {
diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs
index e5c9567957bc1..c3e1b9587a4a8 100644
--- a/library/std/src/sys/windows/handle.rs
+++ b/library/std/src/sys/windows/handle.rs
@@ -74,20 +74,10 @@ impl FromRawHandle for Handle {
 
 impl Handle {
     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
-        let mut read = 0;
-        let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
-        let res = cvt(unsafe {
-            c::ReadFile(
-                self.as_handle(),
-                buf.as_mut_ptr() as c::LPVOID,
-                len,
-                &mut read,
-                ptr::null_mut(),
-            )
-        });
+        let res = unsafe { self.synchronous_read(buf.as_mut_ptr().cast(), buf.len(), None) };
 
         match res {
-            Ok(_) => Ok(read as usize),
+            Ok(read) => Ok(read as usize),
 
             // The special treatment of BrokenPipe is to deal with Windows
             // pipe semantics, which yields this error when *reading* from
@@ -109,42 +99,23 @@ impl Handle {
     }
 
     pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
-        let mut read = 0;
-        let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
-        let res = unsafe {
-            let mut overlapped: c::OVERLAPPED = mem::zeroed();
-            overlapped.Offset = offset as u32;
-            overlapped.OffsetHigh = (offset >> 32) as u32;
-            cvt(c::ReadFile(
-                self.as_handle(),
-                buf.as_mut_ptr() as c::LPVOID,
-                len,
-                &mut read,
-                &mut overlapped,
-            ))
-        };
+        let res =
+            unsafe { self.synchronous_read(buf.as_mut_ptr().cast(), buf.len(), Some(offset)) };
+
         match res {
-            Ok(_) => Ok(read as usize),
+            Ok(read) => Ok(read as usize),
             Err(ref e) if e.raw_os_error() == Some(c::ERROR_HANDLE_EOF as i32) => Ok(0),
             Err(e) => Err(e),
         }
     }
 
     pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
-        let mut read = 0;
-        let len = cmp::min(buf.remaining(), <c::DWORD>::MAX as usize) as c::DWORD;
-        let res = cvt(unsafe {
-            c::ReadFile(
-                self.as_handle(),
-                buf.unfilled_mut().as_mut_ptr() as c::LPVOID,
-                len,
-                &mut read,
-                ptr::null_mut(),
-            )
-        });
+        let res = unsafe {
+            self.synchronous_read(buf.unfilled_mut().as_mut_ptr(), buf.remaining(), None)
+        };
 
         match res {
-            Ok(_) => {
+            Ok(read) => {
                 // Safety: `read` bytes were written to the initialized portion of the buffer
                 unsafe {
                     buf.assume_init(read as usize);
@@ -221,18 +192,7 @@ impl Handle {
     }
 
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
-        let mut amt = 0;
-        let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
-        cvt(unsafe {
-            c::WriteFile(
-                self.as_handle(),
-                buf.as_ptr() as c::LPVOID,
-                len,
-                &mut amt,
-                ptr::null_mut(),
-            )
-        })?;
-        Ok(amt as usize)
+        unsafe { self.synchronous_write(&buf, None) }
     }
 
     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
@@ -245,21 +205,7 @@ impl Handle {
     }
 
     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
-        let mut written = 0;
-        let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
-        unsafe {
-            let mut overlapped: c::OVERLAPPED = mem::zeroed();
-            overlapped.Offset = offset as u32;
-            overlapped.OffsetHigh = (offset >> 32) as u32;
-            cvt(c::WriteFile(
-                self.as_handle(),
-                buf.as_ptr() as c::LPVOID,
-                len,
-                &mut written,
-                &mut overlapped,
-            ))?;
-        }
-        Ok(written as usize)
+        unsafe { self.synchronous_write(&buf, Some(offset)) }
     }
 
     pub fn try_clone(&self) -> io::Result<Self> {
@@ -274,6 +220,96 @@ impl Handle {
     ) -> io::Result<Self> {
         Ok(Self(self.0.duplicate(access, inherit, options)?))
     }
+
+    /// Performs a synchronous read.
+    ///
+    /// If the handle is opened for asynchronous I/O then this abort the process.
+    /// See #81357.
+    ///
+    /// If `offset` is `None` then the current file position is used.
+    unsafe fn synchronous_read(
+        &self,
+        buf: *mut mem::MaybeUninit<u8>,
+        len: usize,
+        offset: Option<u64>,
+    ) -> io::Result<usize> {
+        let mut io_status = c::IO_STATUS_BLOCK::default();
+
+        // The length is clamped at u32::MAX.
+        let len = cmp::min(len, c::DWORD::MAX as usize) as c::DWORD;
+        let status = c::NtReadFile(
+            self.as_handle(),
+            ptr::null_mut(),
+            None,
+            ptr::null_mut(),
+            &mut io_status,
+            buf,
+            len,
+            offset.map(|n| n as _).as_ref(),
+            None,
+        );
+        match status {
+            // If the operation has not completed then abort the process.
+            // Doing otherwise means that the buffer and stack may be written to
+            // after this function returns.
+            c::STATUS_PENDING => {
+                eprintln!("I/O error: operation failed to complete synchronously");
+                crate::process::abort();
+            }
+
+            // Return `Ok(0)` when there's nothing more to read.
+            c::STATUS_END_OF_FILE => Ok(0),
+
+            // Success!
+            status if c::nt_success(status) => Ok(io_status.Information),
+
+            status => {
+                let error = c::RtlNtStatusToDosError(status);
+                Err(io::Error::from_raw_os_error(error as _))
+            }
+        }
+    }
+
+    /// Performs a synchronous write.
+    ///
+    /// If the handle is opened for asynchronous I/O then this abort the process.
+    /// See #81357.
+    ///
+    /// If `offset` is `None` then the current file position is used.
+    unsafe fn synchronous_write(&self, buf: &[u8], offset: Option<u64>) -> io::Result<usize> {
+        let mut io_status = c::IO_STATUS_BLOCK::default();
+
+        // The length is clamped at u32::MAX.
+        let len = cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD;
+        let status = c::NtWriteFile(
+            self.as_handle(),
+            ptr::null_mut(),
+            None,
+            ptr::null_mut(),
+            &mut io_status,
+            buf.as_ptr(),
+            len,
+            offset.map(|n| n as _).as_ref(),
+            None,
+        );
+        match status {
+            // If the operation has not completed then abort the process.
+            // Doing otherwise means that the buffer maybe read and the stack
+            // written to after this function returns.
+            c::STATUS_PENDING => {
+                eprintln!("I/O error: operation failed to complete synchronously");
+                crate::process::abort();
+            }
+
+            // Success!
+            status if c::nt_success(status) => Ok(io_status.Information),
+
+            status => {
+                let error = c::RtlNtStatusToDosError(status);
+                Err(io::Error::from_raw_os_error(error as _))
+            }
+        }
+    }
 }
 
 impl<'a> Read for &'a Handle {

From 084b71a54ff86db4d220845e7a8847b9928f89cf Mon Sep 17 00:00:00 2001
From: Chris Denton <christophersdenton@gmail.com>
Date: Wed, 30 Mar 2022 12:36:00 +0100
Subject: [PATCH 3/5] Document synchronicity

---
 library/std/src/fs.rs | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index 46acd0ee4c595..10c424269c7d1 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -85,6 +85,12 @@ use crate::time::SystemTime;
 /// by different processes. Avoid assuming that holding a `&File` means that the
 /// file will not change.
 ///
+/// # Platform-specific behavior
+///
+/// On Windows, the implementation of [`Read`] and [`Write`] traits for `File`
+/// perform synchronous I/O operations. Therefore the underlying file must not
+/// have been opened for asynchronous I/O (e.g. by using `FILE_FLAG_OVERLAPPED`).
+///
 /// [`BufReader<R>`]: io::BufReader
 /// [`sync_all`]: File::sync_all
 #[stable(feature = "rust1", since = "1.0.0")]

From 88c05edc9deb6def7ed4b1370cca6ddb79da8424 Mon Sep 17 00:00:00 2001
From: Chris Denton <christophersdenton@gmail.com>
Date: Mon, 4 Apr 2022 06:08:16 +0100
Subject: [PATCH 4/5] Make `synchronous_write` safe to call

---
 library/std/src/sys/windows/c.rs      |  7 ------
 library/std/src/sys/windows/handle.rs | 34 ++++++++++++++-------------
 2 files changed, 18 insertions(+), 23 deletions(-)

diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
index 1528db7362e64..336264d99f900 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -986,13 +986,6 @@ extern "system" {
         lpOverlapped: LPOVERLAPPED,
         lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE,
     ) -> BOOL;
-    pub fn WriteFile(
-        hFile: BorrowedHandle<'_>,
-        lpBuffer: LPVOID,
-        nNumberOfBytesToWrite: DWORD,
-        lpNumberOfBytesWritten: LPDWORD,
-        lpOverlapped: LPOVERLAPPED,
-    ) -> BOOL;
     pub fn WriteFileEx(
         hFile: BorrowedHandle<'_>,
         lpBuffer: LPVOID,
diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs
index c3e1b9587a4a8..2a0bffd6a8e99 100644
--- a/library/std/src/sys/windows/handle.rs
+++ b/library/std/src/sys/windows/handle.rs
@@ -192,7 +192,7 @@ impl Handle {
     }
 
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
-        unsafe { self.synchronous_write(&buf, None) }
+        self.synchronous_write(&buf, None)
     }
 
     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
@@ -205,7 +205,7 @@ impl Handle {
     }
 
     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
-        unsafe { self.synchronous_write(&buf, Some(offset)) }
+        self.synchronous_write(&buf, Some(offset))
     }
 
     pub fn try_clone(&self) -> io::Result<Self> {
@@ -276,25 +276,27 @@ impl Handle {
     /// See #81357.
     ///
     /// If `offset` is `None` then the current file position is used.
-    unsafe fn synchronous_write(&self, buf: &[u8], offset: Option<u64>) -> io::Result<usize> {
+    fn synchronous_write(&self, buf: &[u8], offset: Option<u64>) -> io::Result<usize> {
         let mut io_status = c::IO_STATUS_BLOCK::default();
 
         // The length is clamped at u32::MAX.
         let len = cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD;
-        let status = c::NtWriteFile(
-            self.as_handle(),
-            ptr::null_mut(),
-            None,
-            ptr::null_mut(),
-            &mut io_status,
-            buf.as_ptr(),
-            len,
-            offset.map(|n| n as _).as_ref(),
-            None,
-        );
+        let status = unsafe {
+            c::NtWriteFile(
+                self.as_handle(),
+                ptr::null_mut(),
+                None,
+                ptr::null_mut(),
+                &mut io_status,
+                buf.as_ptr(),
+                len,
+                offset.map(|n| n as _).as_ref(),
+                None,
+            )
+        };
         match status {
             // If the operation has not completed then abort the process.
-            // Doing otherwise means that the buffer maybe read and the stack
+            // Doing otherwise means that the buffer may be read and the stack
             // written to after this function returns.
             c::STATUS_PENDING => {
                 eprintln!("I/O error: operation failed to complete synchronously");
@@ -305,7 +307,7 @@ impl Handle {
             status if c::nt_success(status) => Ok(io_status.Information),
 
             status => {
-                let error = c::RtlNtStatusToDosError(status);
+                let error = unsafe { c::RtlNtStatusToDosError(status) };
                 Err(io::Error::from_raw_os_error(error as _))
             }
         }

From d2ce150c8c28b69bf406deb0920fcb4ca0d950ae Mon Sep 17 00:00:00 2001
From: Chris Denton <christophersdenton@gmail.com>
Date: Mon, 4 Apr 2022 06:08:41 +0100
Subject: [PATCH 5/5] Use rtabort

---
 library/std/src/sys/windows/handle.rs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs
index 2a0bffd6a8e99..ef9a8bd690031 100644
--- a/library/std/src/sys/windows/handle.rs
+++ b/library/std/src/sys/windows/handle.rs
@@ -299,8 +299,7 @@ impl Handle {
             // Doing otherwise means that the buffer may be read and the stack
             // written to after this function returns.
             c::STATUS_PENDING => {
-                eprintln!("I/O error: operation failed to complete synchronously");
-                crate::process::abort();
+                rtabort!("I/O error: operation failed to complete synchronously");
             }
 
             // Success!