Use HandleOrNull and HandleOrInvalid in the Windows FFI bindings.

Use the new `HandleOrNull` and `HandleOrInvalid` types that were introduced
as part of [I/O safety] in a few functions in the Windows FFI bindings.

This factors out an `unsafe` block and two `unsafe` function calls in the
Windows implementation code.

And, it helps test `HandleOrNull` and `HandleOrInvalid`, which indeed turned
up a bug: `OwnedHandle` also needs to be `#[repr(transparent)]`, as it's
used inside of `HandleOrNull` and `HandleOrInvalid` which are also
`#[repr(transparent)]`.

[I/O safety]: https://github.com/rust-lang/rust/issues/87074
This commit is contained in:
Dan Gohman 2022-03-03 11:07:23 -08:00
parent 32cbc7630b
commit 35606490ab
5 changed files with 22 additions and 17 deletions

View File

@ -59,6 +59,7 @@ pub struct BorrowedHandle<'handle> {
/// [`RegCloseKey`]: https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regclosekey
///
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
#[repr(transparent)]
#[unstable(feature = "io_safety", issue = "87074")]
pub struct OwnedHandle {
handle: RawHandle,

View File

@ -6,6 +6,7 @@
use crate::mem;
use crate::os::raw::{c_char, c_int, c_long, c_longlong, c_uint, c_ulong, c_ushort};
use crate::os::windows::io::{BorrowedHandle, HandleOrInvalid, HandleOrNull};
use crate::ptr;
use core::ffi::NonZero_c_ulong;
@ -886,7 +887,7 @@ pub fn CreateThread(
lpParameter: LPVOID,
dwCreationFlags: DWORD,
lpThreadId: LPDWORD,
) -> HANDLE;
) -> HandleOrNull;
pub fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
pub fn SwitchToThread() -> BOOL;
pub fn Sleep(dwMilliseconds: DWORD);
@ -950,14 +951,14 @@ pub fn DuplicateHandle(
dwOptions: DWORD,
) -> BOOL;
pub fn ReadFile(
hFile: HANDLE,
hFile: BorrowedHandle<'_>,
lpBuffer: LPVOID,
nNumberOfBytesToRead: DWORD,
lpNumberOfBytesRead: LPDWORD,
lpOverlapped: LPOVERLAPPED,
) -> BOOL;
pub fn WriteFile(
hFile: HANDLE,
hFile: BorrowedHandle<'_>,
lpBuffer: LPVOID,
nNumberOfBytesToWrite: DWORD,
lpNumberOfBytesWritten: LPDWORD,
@ -981,7 +982,7 @@ pub fn CreateFileW(
dwCreationDisposition: DWORD,
dwFlagsAndAttributes: DWORD,
hTemplateFile: HANDLE,
) -> HANDLE;
) -> HandleOrInvalid;
pub fn FindFirstFileW(fileName: LPCWSTR, findFileData: LPWIN32_FIND_DATAW) -> HANDLE;
pub fn FindNextFileW(findFile: HANDLE, findFileData: LPWIN32_FIND_DATAW) -> BOOL;

View File

@ -1,5 +1,6 @@
use crate::os::windows::prelude::*;
use crate::convert::TryInto;
use crate::ffi::OsString;
use crate::fmt;
use crate::io::{self, Error, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
@ -294,10 +295,10 @@ pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
ptr::null_mut(),
)
};
if handle == c::INVALID_HANDLE_VALUE {
Err(Error::last_os_error())
if let Ok(handle) = handle.try_into() {
Ok(File { handle: Handle::from_inner(handle) })
} else {
unsafe { Ok(File { handle: Handle::from_raw_handle(handle) }) }
Err(Error::last_os_error())
}
}

View File

@ -78,7 +78,7 @@ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
let res = cvt(unsafe {
c::ReadFile(
self.as_raw_handle(),
self.as_handle(),
buf.as_mut_ptr() as c::LPVOID,
len,
&mut read,
@ -116,7 +116,7 @@ pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
overlapped.Offset = offset as u32;
overlapped.OffsetHigh = (offset >> 32) as u32;
cvt(c::ReadFile(
self.as_raw_handle(),
self.as_handle(),
buf.as_mut_ptr() as c::LPVOID,
len,
&mut read,
@ -135,7 +135,7 @@ pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
let len = cmp::min(buf.remaining(), <c::DWORD>::MAX as usize) as c::DWORD;
let res = cvt(unsafe {
c::ReadFile(
self.as_raw_handle(),
self.as_handle(),
buf.unfilled_mut().as_mut_ptr() as c::LPVOID,
len,
&mut read,
@ -171,7 +171,7 @@ pub unsafe fn read_overlapped(
let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
let mut amt = 0;
let res = cvt(c::ReadFile(
self.as_raw_handle(),
self.as_handle(),
buf.as_ptr() as c::LPVOID,
len,
&mut amt,
@ -225,7 +225,7 @@ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
cvt(unsafe {
c::WriteFile(
self.as_raw_handle(),
self.as_handle(),
buf.as_ptr() as c::LPVOID,
len,
&mut amt,
@ -252,7 +252,7 @@ pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
overlapped.Offset = offset as u32;
overlapped.OffsetHigh = (offset >> 32) as u32;
cvt(c::WriteFile(
self.as_raw_handle(),
self.as_handle(),
buf.as_ptr() as c::LPVOID,
len,
&mut written,

View File

@ -1,11 +1,13 @@
use crate::convert::TryInto;
use crate::ffi::CStr;
use crate::io;
use crate::num::NonZeroUsize;
use crate::os::windows::io::{AsRawHandle, FromRawHandle};
use crate::os::windows::io::AsRawHandle;
use crate::ptr;
use crate::sys::c;
use crate::sys::handle::Handle;
use crate::sys::stack_overflow;
use crate::sys_common::FromInner;
use crate::time::Duration;
use libc::c_void;
@ -40,13 +42,13 @@ pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
ptr::null_mut(),
);
return if ret as usize == 0 {
return if let Ok(handle) = ret.try_into() {
Ok(Thread { handle: Handle::from_inner(handle) })
} else {
// The thread failed to start and as a result p was not consumed. Therefore, it is
// safe to reconstruct the box so that it gets deallocated.
drop(Box::from_raw(p));
Err(io::Error::last_os_error())
} else {
Ok(Thread { handle: Handle::from_raw_handle(ret) })
};
extern "system" fn thread_start(main: *mut c_void) -> c::DWORD {