Skip to content

Commit 3b97481

Browse files
committed
Fix assertion failures in OwnedHandle with windows_subsystem.
As discussed in rust-lang#88576, raw handle values in Windows can be null, such as in `windows_subsystem` mode, or when consoles are detached from a process. So, don't use `NonNull` to hold them, don't assert that they're not null, and remove `OwnedHandle`'s `repr(transparent)`. Introduce a new `HandleOrNull` type, similar to `HandleOrInvalid`, to cover the FFI use case.
1 parent 497ee32 commit 3b97481

File tree

1 file changed

+72
-40
lines changed

1 file changed

+72
-40
lines changed

library/std/src/os/windows/io/handle.rs

+72-40
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use crate::fmt;
99
use crate::fs;
1010
use crate::marker::PhantomData;
1111
use crate::mem::forget;
12-
use crate::ptr::NonNull;
1312
use crate::sys::c;
1413
use crate::sys_common::{AsInner, FromInner, IntoInner};
1514

@@ -20,32 +19,32 @@ use crate::sys_common::{AsInner, FromInner, IntoInner};
2019
///
2120
/// This uses `repr(transparent)` and has the representation of a host handle,
2221
/// so it can be used in FFI in places where a handle is passed as an argument,
23-
/// it is not captured or consumed, and it is never null.
22+
/// it is not captured or consumed.
2423
///
2524
/// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is
2625
/// sometimes a valid handle value. See [here] for the full story.
2726
///
27+
/// And, it *may* have the value `NULL` (0), which can occur when consoles are
28+
/// detached from processes, or when `windows_subsystem` is used.
29+
///
2830
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
2931
#[derive(Copy, Clone)]
3032
#[repr(transparent)]
3133
#[unstable(feature = "io_safety", issue = "87074")]
3234
pub struct BorrowedHandle<'handle> {
33-
handle: NonNull<c_void>,
35+
handle: RawHandle,
3436
_phantom: PhantomData<&'handle OwnedHandle>,
3537
}
3638

3739
/// An owned handle.
3840
///
3941
/// This closes the handle on drop.
4042
///
41-
/// This uses `repr(transparent)` and has the representation of a host handle,
42-
/// so it can be used in FFI in places where a handle is passed as a consumed
43-
/// argument or returned as an owned value, and is never null.
44-
///
4543
/// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is
46-
/// sometimes a valid handle value. See [here] for the full story. For APIs
47-
/// like `CreateFileW` which report errors with `INVALID_HANDLE_VALUE` instead
48-
/// of null, use [`HandleOrInvalid`] instead of `Option<OwnedHandle>`.
44+
/// sometimes a valid handle value. See [here] for the full story.
45+
///
46+
/// And, it *may* have the value `NULL` (0), which can occur when consoles are
47+
/// detached from processes, or when `windows_subsystem` is used.
4948
///
5049
/// `OwnedHandle` uses [`CloseHandle`] to close its handle on drop. As such,
5150
/// it must not be used with handles to open registry keys which need to be
@@ -55,12 +54,27 @@ pub struct BorrowedHandle<'handle> {
5554
/// [`RegCloseKey`]: https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regclosekey
5655
///
5756
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
58-
#[repr(transparent)]
5957
#[unstable(feature = "io_safety", issue = "87074")]
6058
pub struct OwnedHandle {
61-
handle: NonNull<c_void>,
59+
handle: RawHandle,
6260
}
6361

62+
/// FFI type for handles in return values or out parameters, where `NULL` is used
63+
/// as a sentry value to indicate errors, such as in the return value of `CreateThread`. This uses
64+
/// `repr(transparent)` and has the representation of a host handle, so that it can be used in such
65+
/// FFI declarations.
66+
///
67+
/// The only thing you can usefully do with a `HandleOrNull` is to convert it into an
68+
/// `OwnedHandle` using its [`TryFrom`] implementation; this conversion takes care of the check for
69+
/// `NULL`. This ensures that such FFI calls cannot start using the handle without
70+
/// checking for `NULL` first.
71+
///
72+
/// If this holds a valid handle, it will close the handle on drop.
73+
#[repr(transparent)]
74+
#[unstable(feature = "io_safety", issue = "87074")]
75+
#[derive(Debug)]
76+
pub struct HandleOrNull(OwnedHandle);
77+
6478
/// FFI type for handles in return values or out parameters, where `INVALID_HANDLE_VALUE` is used
6579
/// as a sentry value to indicate errors, such as in the return value of `CreateFileW`. This uses
6680
/// `repr(transparent)` and has the representation of a host handle, so that it can be used in such
@@ -75,17 +89,19 @@ pub struct OwnedHandle {
7589
#[repr(transparent)]
7690
#[unstable(feature = "io_safety", issue = "87074")]
7791
#[derive(Debug)]
78-
pub struct HandleOrInvalid(Option<OwnedHandle>);
92+
pub struct HandleOrInvalid(OwnedHandle);
7993

8094
// The Windows [`HANDLE`] type may be transferred across and shared between
8195
// thread boundaries (despite containing a `*mut void`, which in general isn't
8296
// `Send` or `Sync`).
8397
//
8498
// [`HANDLE`]: std::os::windows::raw::HANDLE
8599
unsafe impl Send for OwnedHandle {}
100+
unsafe impl Send for HandleOrNull {}
86101
unsafe impl Send for HandleOrInvalid {}
87102
unsafe impl Send for BorrowedHandle<'_> {}
88103
unsafe impl Sync for OwnedHandle {}
104+
unsafe impl Sync for HandleOrNull {}
89105
unsafe impl Sync for HandleOrInvalid {}
90106
unsafe impl Sync for BorrowedHandle<'_> {}
91107

@@ -95,18 +111,29 @@ impl BorrowedHandle<'_> {
95111
/// # Safety
96112
///
97113
/// The resource pointed to by `handle` must be a valid open handle, it
98-
/// must remain open for the duration of the returned `BorrowedHandle`, and
99-
/// it must not be null.
114+
/// must remain open for the duration of the returned `BorrowedHandle`.
100115
///
101116
/// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is
102117
/// sometimes a valid handle value. See [here] for the full story.
103118
///
119+
/// And, it *may* have the value `NULL` (0), which can occur when consoles are
120+
/// detached from processes, or when `windows_subsystem` is used.
121+
///
104122
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
105123
#[inline]
106124
#[unstable(feature = "io_safety", issue = "87074")]
107125
pub unsafe fn borrow_raw_handle(handle: RawHandle) -> Self {
108-
assert!(!handle.is_null());
109-
Self { handle: NonNull::new_unchecked(handle), _phantom: PhantomData }
126+
Self { handle, _phantom: PhantomData }
127+
}
128+
}
129+
130+
impl TryFrom<HandleOrNull> for OwnedHandle {
131+
type Error = ();
132+
133+
#[inline]
134+
fn try_from(handle_or_null: HandleOrNull) -> Result<Self, ()> {
135+
let owned_handle = handle_or_null.0;
136+
if owned_handle.handle.as_ptr().is_null() { Err(()) } else { Ok(owned_handle) }
110137
}
111138
}
112139

@@ -115,18 +142,7 @@ impl TryFrom<HandleOrInvalid> for OwnedHandle {
115142

116143
#[inline]
117144
fn try_from(handle_or_invalid: HandleOrInvalid) -> Result<Self, ()> {
118-
// In theory, we ought to be able to assume that the pointer here is
119-
// never null, use `OwnedHandle` rather than `Option<OwnedHandle>`, and
120-
// obviate the the panic path here. Unfortunately, Win32 documentation
121-
// doesn't explicitly guarantee this anywhere.
122-
//
123-
// APIs like [`CreateFileW`] itself have `HANDLE` arguments where a
124-
// null handle indicates an absent value, which wouldn't work if null
125-
// were a valid handle value, so it seems very unlikely that it could
126-
// ever return null. But who knows?
127-
//
128-
// [`CreateFileW`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
129-
let owned_handle = handle_or_invalid.0.expect("A `HandleOrInvalid` was null!");
145+
let owned_handle = handle_or_invalid.0;
130146
if owned_handle.handle.as_ptr() == c::INVALID_HANDLE_VALUE {
131147
Err(())
132148
} else {
@@ -161,9 +177,6 @@ impl IntoRawHandle for OwnedHandle {
161177
impl FromRawHandle for OwnedHandle {
162178
/// Constructs a new instance of `Self` from the given raw handle.
163179
///
164-
/// Use `HandleOrInvalid` instead of `Option<OwnedHandle>` for APIs that
165-
/// use `INVALID_HANDLE_VALUE` to indicate failure.
166-
///
167180
/// # Safety
168181
///
169182
/// The resource pointed to by `handle` must be open and suitable for
@@ -180,8 +193,28 @@ impl FromRawHandle for OwnedHandle {
180193
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
181194
#[inline]
182195
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
183-
assert!(!handle.is_null());
184-
Self { handle: NonNull::new_unchecked(handle) }
196+
Self { handle }
197+
}
198+
}
199+
200+
impl FromRawHandle for HandleOrNull {
201+
/// Constructs a new instance of `Self` from the given `RawHandle` returned
202+
/// from a Windows API that uses null to indicate failure, such as
203+
/// `CreateThread`.
204+
///
205+
/// Use `HandleOrInvalid` instead of `HandleOrNull` for APIs that
206+
/// use `INVALID_HANDLE_VALUE` to indicate failure.
207+
///
208+
/// # Safety
209+
///
210+
/// The resource pointed to by `handle` must be either open and otherwise
211+
/// unowned, or null. Note that not all Windows APIs use null for errors;
212+
/// see [here] for the full story.
213+
///
214+
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
215+
#[inline]
216+
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
217+
Self(OwnedHandle::from_raw_handle(handle))
185218
}
186219
}
187220

@@ -190,21 +223,20 @@ impl FromRawHandle for HandleOrInvalid {
190223
/// from a Windows API that uses `INVALID_HANDLE_VALUE` to indicate
191224
/// failure, such as `CreateFileW`.
192225
///
193-
/// Use `Option<OwnedHandle>` instead of `HandleOrInvalid` for APIs that
226+
/// Use `HandleOrNull` instead of `HandleOrInvalid` for APIs that
194227
/// use null to indicate failure.
195228
///
196229
/// # Safety
197230
///
198231
/// The resource pointed to by `handle` must be either open and otherwise
199-
/// unowned, or equal to `INVALID_HANDLE_VALUE` (-1). It must not be null.
200-
/// Note that not all Windows APIs use `INVALID_HANDLE_VALUE` for errors;
201-
/// see [here] for the full story.
232+
/// unowned, null, or equal to `INVALID_HANDLE_VALUE` (-1). Note that not
233+
/// all Windows APIs use `INVALID_HANDLE_VALUE` for errors; see [here] for
234+
/// the full story.
202235
///
203236
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
204237
#[inline]
205238
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
206-
// We require non-null here to catch errors earlier.
207-
Self(Some(OwnedHandle::from_raw_handle(handle)))
239+
Self(OwnedHandle::from_raw_handle(handle))
208240
}
209241
}
210242

0 commit comments

Comments
 (0)