Skip to content

Commit d71ba74

Browse files
committed
Auto merge of rust-lang#88798 - sunfishcode:sunfishcode/windows-null-handles, r=joshtriplett
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. r? `@joshtriplett`
2 parents 62efba8 + 5d79870 commit d71ba74

File tree

1 file changed

+85
-50
lines changed

1 file changed

+85
-50
lines changed

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

+85-50
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@
44

55
use super::raw::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
66
use crate::convert::TryFrom;
7-
use crate::ffi::c_void;
87
use crate::fmt;
98
use crate::fs;
109
use crate::marker::PhantomData;
1110
use crate::mem::forget;
12-
use crate::ptr::NonNull;
1311
use crate::sys::c;
1412
use crate::sys_common::{AsInner, FromInner, IntoInner};
1513

@@ -20,32 +18,32 @@ use crate::sys_common::{AsInner, FromInner, IntoInner};
2018
///
2119
/// This uses `repr(transparent)` and has the representation of a host handle,
2220
/// 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.
21+
/// it is not captured or consumed.
2422
///
2523
/// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is
2624
/// sometimes a valid handle value. See [here] for the full story.
2725
///
26+
/// And, it *may* have the value `NULL` (0), which can occur when consoles are
27+
/// detached from processes, or when `windows_subsystem` is used.
28+
///
2829
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
2930
#[derive(Copy, Clone)]
3031
#[repr(transparent)]
3132
#[unstable(feature = "io_safety", issue = "87074")]
3233
pub struct BorrowedHandle<'handle> {
33-
handle: NonNull<c_void>,
34+
handle: RawHandle,
3435
_phantom: PhantomData<&'handle OwnedHandle>,
3536
}
3637

3738
/// An owned handle.
3839
///
3940
/// This closes the handle on drop.
4041
///
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-
///
4542
/// 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>`.
43+
/// sometimes a valid handle value. See [here] for the full story.
44+
///
45+
/// And, it *may* have the value `NULL` (0), which can occur when consoles are
46+
/// detached from processes, or when `windows_subsystem` is used.
4947
///
5048
/// `OwnedHandle` uses [`CloseHandle`] to close its handle on drop. As such,
5149
/// it must not be used with handles to open registry keys which need to be
@@ -55,12 +53,31 @@ pub struct BorrowedHandle<'handle> {
5553
/// [`RegCloseKey`]: https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regclosekey
5654
///
5755
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
58-
#[repr(transparent)]
5956
#[unstable(feature = "io_safety", issue = "87074")]
6057
pub struct OwnedHandle {
61-
handle: NonNull<c_void>,
58+
handle: RawHandle,
6259
}
6360

61+
/// FFI type for handles in return values or out parameters, where `NULL` is used
62+
/// as a sentry value to indicate errors, such as in the return value of `CreateThread`. This uses
63+
/// `repr(transparent)` and has the representation of a host handle, so that it can be used in such
64+
/// FFI declarations.
65+
///
66+
/// The only thing you can usefully do with a `HandleOrNull` is to convert it into an
67+
/// `OwnedHandle` using its [`TryFrom`] implementation; this conversion takes care of the check for
68+
/// `NULL`. This ensures that such FFI calls cannot start using the handle without
69+
/// checking for `NULL` first.
70+
///
71+
/// This type concerns any value other than `NULL` to be valid, including `INVALID_HANDLE_VALUE`.
72+
/// This is because APIs that use `NULL` as their sentry value don't treat `INVALID_HANDLE_VALUE`
73+
/// as special.
74+
///
75+
/// If this holds a valid handle, it will close the handle on drop.
76+
#[repr(transparent)]
77+
#[unstable(feature = "io_safety", issue = "87074")]
78+
#[derive(Debug)]
79+
pub struct HandleOrNull(OwnedHandle);
80+
6481
/// FFI type for handles in return values or out parameters, where `INVALID_HANDLE_VALUE` is used
6582
/// as a sentry value to indicate errors, such as in the return value of `CreateFileW`. This uses
6683
/// `repr(transparent)` and has the representation of a host handle, so that it can be used in such
@@ -71,21 +88,27 @@ pub struct OwnedHandle {
7188
/// `INVALID_HANDLE_VALUE`. This ensures that such FFI calls cannot start using the handle without
7289
/// checking for `INVALID_HANDLE_VALUE` first.
7390
///
91+
/// This type concerns any value other than `INVALID_HANDLE_VALUE` to be valid, including `NULL`.
92+
/// This is because APIs that use `INVALID_HANDLE_VALUE` as their sentry value may return `NULL`
93+
/// under `windows_subsystem = "windows"` or other situations where I/O devices are detached.
94+
///
7495
/// If this holds a valid handle, it will close the handle on drop.
7596
#[repr(transparent)]
7697
#[unstable(feature = "io_safety", issue = "87074")]
7798
#[derive(Debug)]
78-
pub struct HandleOrInvalid(Option<OwnedHandle>);
99+
pub struct HandleOrInvalid(OwnedHandle);
79100

80101
// The Windows [`HANDLE`] type may be transferred across and shared between
81102
// thread boundaries (despite containing a `*mut void`, which in general isn't
82103
// `Send` or `Sync`).
83104
//
84105
// [`HANDLE`]: std::os::windows::raw::HANDLE
85106
unsafe impl Send for OwnedHandle {}
107+
unsafe impl Send for HandleOrNull {}
86108
unsafe impl Send for HandleOrInvalid {}
87109
unsafe impl Send for BorrowedHandle<'_> {}
88110
unsafe impl Sync for OwnedHandle {}
111+
unsafe impl Sync for HandleOrNull {}
89112
unsafe impl Sync for HandleOrInvalid {}
90113
unsafe impl Sync for BorrowedHandle<'_> {}
91114

@@ -95,18 +118,29 @@ impl BorrowedHandle<'_> {
95118
/// # Safety
96119
///
97120
/// 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.
121+
/// must remain open for the duration of the returned `BorrowedHandle`.
100122
///
101123
/// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is
102124
/// sometimes a valid handle value. See [here] for the full story.
103125
///
126+
/// And, it *may* have the value `NULL` (0), which can occur when consoles are
127+
/// detached from processes, or when `windows_subsystem` is used.
128+
///
104129
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
105130
#[inline]
106131
#[unstable(feature = "io_safety", issue = "87074")]
107132
pub unsafe fn borrow_raw_handle(handle: RawHandle) -> Self {
108-
assert!(!handle.is_null());
109-
Self { handle: NonNull::new_unchecked(handle), _phantom: PhantomData }
133+
Self { handle, _phantom: PhantomData }
134+
}
135+
}
136+
137+
impl TryFrom<HandleOrNull> for OwnedHandle {
138+
type Error = ();
139+
140+
#[inline]
141+
fn try_from(handle_or_null: HandleOrNull) -> Result<Self, ()> {
142+
let owned_handle = handle_or_null.0;
143+
if owned_handle.handle.is_null() { Err(()) } else { Ok(owned_handle) }
110144
}
111145
}
112146

@@ -115,44 +149,29 @@ impl TryFrom<HandleOrInvalid> for OwnedHandle {
115149

116150
#[inline]
117151
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!");
130-
if owned_handle.handle.as_ptr() == c::INVALID_HANDLE_VALUE {
131-
Err(())
132-
} else {
133-
Ok(owned_handle)
134-
}
152+
let owned_handle = handle_or_invalid.0;
153+
if owned_handle.handle == c::INVALID_HANDLE_VALUE { Err(()) } else { Ok(owned_handle) }
135154
}
136155
}
137156

138157
impl AsRawHandle for BorrowedHandle<'_> {
139158
#[inline]
140159
fn as_raw_handle(&self) -> RawHandle {
141-
self.handle.as_ptr()
160+
self.handle
142161
}
143162
}
144163

145164
impl AsRawHandle for OwnedHandle {
146165
#[inline]
147166
fn as_raw_handle(&self) -> RawHandle {
148-
self.handle.as_ptr()
167+
self.handle
149168
}
150169
}
151170

152171
impl IntoRawHandle for OwnedHandle {
153172
#[inline]
154173
fn into_raw_handle(self) -> RawHandle {
155-
let handle = self.handle.as_ptr();
174+
let handle = self.handle;
156175
forget(self);
157176
handle
158177
}
@@ -161,9 +180,6 @@ impl IntoRawHandle for OwnedHandle {
161180
impl FromRawHandle for OwnedHandle {
162181
/// Constructs a new instance of `Self` from the given raw handle.
163182
///
164-
/// Use `HandleOrInvalid` instead of `Option<OwnedHandle>` for APIs that
165-
/// use `INVALID_HANDLE_VALUE` to indicate failure.
166-
///
167183
/// # Safety
168184
///
169185
/// The resource pointed to by `handle` must be open and suitable for
@@ -180,8 +196,28 @@ impl FromRawHandle for OwnedHandle {
180196
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
181197
#[inline]
182198
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
183-
assert!(!handle.is_null());
184-
Self { handle: NonNull::new_unchecked(handle) }
199+
Self { handle }
200+
}
201+
}
202+
203+
impl FromRawHandle for HandleOrNull {
204+
/// Constructs a new instance of `Self` from the given `RawHandle` returned
205+
/// from a Windows API that uses null to indicate failure, such as
206+
/// `CreateThread`.
207+
///
208+
/// Use `HandleOrInvalid` instead of `HandleOrNull` for APIs that
209+
/// use `INVALID_HANDLE_VALUE` to indicate failure.
210+
///
211+
/// # Safety
212+
///
213+
/// The resource pointed to by `handle` must be either open and otherwise
214+
/// unowned, or null. Note that not all Windows APIs use null for errors;
215+
/// see [here] for the full story.
216+
///
217+
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
218+
#[inline]
219+
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
220+
Self(OwnedHandle::from_raw_handle(handle))
185221
}
186222
}
187223

@@ -190,29 +226,28 @@ impl FromRawHandle for HandleOrInvalid {
190226
/// from a Windows API that uses `INVALID_HANDLE_VALUE` to indicate
191227
/// failure, such as `CreateFileW`.
192228
///
193-
/// Use `Option<OwnedHandle>` instead of `HandleOrInvalid` for APIs that
229+
/// Use `HandleOrNull` instead of `HandleOrInvalid` for APIs that
194230
/// use null to indicate failure.
195231
///
196232
/// # Safety
197233
///
198234
/// 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.
235+
/// unowned, null, or equal to `INVALID_HANDLE_VALUE` (-1). Note that not
236+
/// all Windows APIs use `INVALID_HANDLE_VALUE` for errors; see [here] for
237+
/// the full story.
202238
///
203239
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
204240
#[inline]
205241
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)))
242+
Self(OwnedHandle::from_raw_handle(handle))
208243
}
209244
}
210245

211246
impl Drop for OwnedHandle {
212247
#[inline]
213248
fn drop(&mut self) {
214249
unsafe {
215-
let _ = c::CloseHandle(self.handle.as_ptr());
250+
let _ = c::CloseHandle(self.handle);
216251
}
217252
}
218253
}

0 commit comments

Comments
 (0)