xref: /aosp_15_r20/external/crosvm/base/src/sys/windows/descriptor.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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