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