1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use std::convert::TryFrom;
6 use std::ffi::CString;
7 use std::fs::File;
8 use std::io::Stderr;
9 use std::io::Stdin;
10 use std::io::Stdout;
11 use std::marker::Send;
12 use std::marker::Sync;
13 use std::mem::MaybeUninit;
14 use std::ops::Drop;
15 use std::os::windows::io::AsRawHandle;
16 use std::os::windows::io::FromRawHandle;
17 use std::os::windows::io::IntoRawHandle;
18 use std::os::windows::io::RawHandle;
19 use std::sync::Once;
20
21 use win_util::duplicate_handle;
22 use win_util::win32_wide_string;
23 use winapi::shared::minwindef::BOOL;
24 use winapi::shared::minwindef::HMODULE;
25 use winapi::shared::minwindef::TRUE;
26 use winapi::um::handleapi::CloseHandle;
27 use winapi::um::handleapi::INVALID_HANDLE_VALUE;
28 use winapi::um::libloaderapi;
29
30 use super::Result;
31 use crate::descriptor::AsRawDescriptor;
32 use crate::descriptor::Descriptor;
33 use crate::descriptor::FromRawDescriptor;
34 use crate::descriptor::IntoRawDescriptor;
35 use crate::descriptor::SafeDescriptor;
36
37 pub type RawDescriptor = RawHandle;
38
39 pub const INVALID_DESCRIPTOR: RawDescriptor = INVALID_HANDLE_VALUE;
40
41 impl PartialEq for SafeDescriptor {
eq(&self, other: &Self) -> bool42 fn eq(&self, other: &Self) -> bool {
43 compare_object_handles(self.descriptor, other.as_raw_descriptor())
44 }
45 }
46
47 impl Drop for SafeDescriptor {
drop(&mut self)48 fn drop(&mut self) {
49 // SAFETY: trivially safe
50 unsafe { CloseHandle(self.descriptor) };
51 }
52 }
53
54 impl AsRawHandle for SafeDescriptor {
as_raw_handle(&self) -> RawHandle55 fn as_raw_handle(&self) -> RawHandle {
56 self.as_raw_descriptor()
57 }
58 }
59
60 static KERNELBASE_INIT: Once = Once::new();
61 static mut KERNELBASE_LIBRARY: MaybeUninit<HMODULE> = MaybeUninit::uninit();
62
compare_object_handles(first: RawHandle, second: RawHandle) -> bool63 fn compare_object_handles(first: RawHandle, second: RawHandle) -> bool {
64 KERNELBASE_INIT.call_once(|| {
65 // SAFETY: trivially safe
66 unsafe {
67 *KERNELBASE_LIBRARY.as_mut_ptr() =
68 libloaderapi::LoadLibraryW(win32_wide_string("Kernelbase").as_ptr());
69 };
70 });
71 // SAFETY: the return value is checked.
72 let handle = unsafe { KERNELBASE_LIBRARY.assume_init() };
73 if handle.is_null() {
74 return first == second;
75 }
76
77 let addr = CString::new("CompareObjectHandles").unwrap();
78 let addr_ptr = addr.as_ptr();
79 // SAFETY: the return value is checked.
80 let symbol = unsafe { libloaderapi::GetProcAddress(handle, addr_ptr) };
81 if symbol.is_null() {
82 return first == second;
83 }
84
85 // SAFETY: trivially safe
86 let func = unsafe {
87 std::mem::transmute::<
88 *mut winapi::shared::minwindef::__some_function,
89 extern "system" fn(RawHandle, RawHandle) -> BOOL,
90 >(symbol)
91 };
92 let ret = func(first, second);
93 ret == TRUE
94 }
95
96 impl TryFrom<&dyn AsRawHandle> for SafeDescriptor {
97 type Error = std::io::Error;
98
try_from(handle: &dyn AsRawHandle) -> std::result::Result<Self, Self::Error>99 fn try_from(handle: &dyn AsRawHandle) -> std::result::Result<Self, Self::Error> {
100 Ok(SafeDescriptor {
101 descriptor: duplicate_handle(handle.as_raw_handle())?,
102 })
103 }
104 }
105
106 impl SafeDescriptor {
107 /// Clones this descriptor, internally creating a new descriptor. The new SafeDescriptor will
108 /// share the same underlying count within the kernel.
try_clone(&self) -> Result<SafeDescriptor>109 pub fn try_clone(&self) -> Result<SafeDescriptor> {
110 // SAFETY:
111 // Safe because `duplicate_handle` will return a valid handle, or at the very least error
112 // out.
113 Ok(unsafe {
114 SafeDescriptor::from_raw_descriptor(win_util::duplicate_handle(self.descriptor)?)
115 })
116 }
117 }
118
119 // SAFETY:
120 // On Windows, RawHandles are represented by raw pointers but are not used as such in
121 // rust code, and are therefore safe to send between threads.
122 unsafe impl Send for SafeDescriptor {}
123 // SAFETY: See comments for impl Send
124 unsafe impl Sync for SafeDescriptor {}
125
126 // SAFETY:
127 // On Windows, RawHandles are represented by raw pointers but are opaque to the
128 // userspace and cannot be derefenced by rust code, and are therefore safe to
129 // send between threads.
130 unsafe impl Send for Descriptor {}
131 // SAFETY: See comments for impl Send
132 unsafe impl Sync for Descriptor {}
133
134 macro_rules! AsRawDescriptor {
135 ($name:ident) => {
136 impl AsRawDescriptor for $name {
137 fn as_raw_descriptor(&self) -> RawDescriptor {
138 return self.as_raw_handle();
139 }
140 }
141 };
142 }
143
144 macro_rules! FromRawDescriptor {
145 ($name:ident) => {
146 impl FromRawDescriptor for $name {
147 // SAFETY: It is caller's responsibility to ensure that the descriptor is valid.
148 unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self {
149 return $name::from_raw_handle(descriptor);
150 }
151 }
152 };
153 }
154
155 macro_rules! IntoRawDescriptor {
156 ($name:ident) => {
157 impl IntoRawDescriptor for $name {
158 fn into_raw_descriptor(self) -> RawDescriptor {
159 return self.into_raw_handle();
160 }
161 }
162 };
163 }
164
165 // Implementations for File. This enables the File-type to use the cross-platform
166 // RawDescriptor, but does not mean File should be used as a generic
167 // descriptor container. That should go to either SafeDescriptor or another more
168 // relevant container type.
169 // TODO(b/148971445): Ensure there are no usages of File that aren't actually files.
170 AsRawDescriptor!(File);
171 FromRawDescriptor!(File);
172 IntoRawDescriptor!(File);
173 AsRawDescriptor!(Stdin);
174 AsRawDescriptor!(Stdout);
175 AsRawDescriptor!(Stderr);
176
177 #[cfg_attr(all(target_os = "windows", target_env = "gnu"), ignore)]
178 #[test]
179 #[allow(clippy::eq_op)]
clone_equality()180 fn clone_equality() {
181 use crate::descriptor::IntoRawDescriptor;
182 use crate::Event;
183
184 let evt = Event::new().unwrap();
185 // SAFETY: Given evt is created above and is valid.
186 let descriptor = unsafe { SafeDescriptor::from_raw_descriptor(evt.into_raw_descriptor()) };
187
188 assert_eq!(descriptor, descriptor);
189
190 assert_eq!(
191 descriptor,
192 descriptor.try_clone().expect("failed to clone event")
193 );
194
195 let evt2 = Event::new().unwrap();
196 // SAFETY: Given evt2 is created above and is valid.
197 let another = unsafe { SafeDescriptor::from_raw_descriptor(evt2.into_raw_descriptor()) };
198
199 assert_ne!(descriptor, another);
200 }
201