windows_permissions/localheap.rs
1//! A specialized [`Box`] variation for items stored on the local heap.
2
3use crate::constants::LocalAllocFlags;
4use std::borrow::{Borrow, BorrowMut};
5use std::cmp::PartialEq;
6use std::fmt;
7use std::hash::Hash;
8use std::io;
9use std::ops::{Deref, DerefMut};
10use std::ptr::{null_mut, NonNull};
11
12/// A smart pointer to an object on the local heap.
13///
14/// Windows has several different options for allocation, and the local heap is
15/// no longer recommended. However, several of the
16/// WinAPI calls in this crate use the local heap, allocating with `LocalAlloc`
17/// and freeing with `LocalFree`. This type encapsulates that behavior,
18/// representing objects that reside on the local heap.
19///
20/// It is primarily created using `unsafe` code in the `wrappers` crate when
21/// the WinAPI allocates a data structure for the program (using `from_raw`).
22///
23/// However, allocations can be manually made with `allocate` or `try_allocate`.
24/// For example:
25///
26/// ```
27/// use std::mem::size_of;
28/// use windows_permissions::LocalBox;
29///
30/// let mut local_ptr1: LocalBox<u32> = unsafe { LocalBox::allocate() };
31///
32/// let mut local_ptr2: LocalBox<u32> = unsafe {
33/// LocalBox::try_allocate(true, size_of::<u32>()).unwrap()
34/// };
35///
36/// *local_ptr1 = 5u32;
37/// *local_ptr2 = 5u32;
38/// assert_eq!(local_ptr1, local_ptr2);
39/// ```
40///
41/// For details, see [MSDN](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localalloc#parameters).
42///
43/// # Exotically-sized types
44///
45/// This struct has not been tested with exotically-sized types. Use with
46/// extreme caution.
47pub struct LocalBox<T> {
48 ptr: NonNull<T>,
49}
50
51impl<T> LocalBox<T> {
52 /// Get a `LocalBox` from a `NonNull`
53 ///
54 /// # Safety
55 ///
56 /// - The `NonNull` pointer *must* have been allocated with
57 /// a Windows API call. When the resulting `NonNull<T>` is dropped, it
58 /// will be dropped with `LocalFree`
59 /// - The buffer pointed to by the pointer must be a valid `T`
60 pub unsafe fn from_raw(ptr: NonNull<T>) -> Self {
61 // Future maintainers:
62 // This function contains no unsafe code, but it requires that
63 // callers fulfil an un-checked promise that is relied on by other
64 // actually unsafe code. Do not remove the unsafe marker without
65 // fully understanding the implications.
66 Self { ptr }
67 }
68
69 /// Allocate enough zeroed memory to hold a `T` with `LocalAlloc`
70 ///
71 /// The memory will always come back zeroed, which has a modest performance
72 /// penalty but can reduce the impact of buffer overruns.
73 ///
74 /// # Panics
75 ///
76 /// Panics if the underlying `LocalAlloc` call fails.
77 ///
78 /// # Safety
79 ///
80 /// The allocated memory is zeroed, which may not be a valid representation
81 /// of a `T`.
82 pub unsafe fn allocate() -> Self {
83 Self::try_allocate(true, std::mem::size_of::<T>())
84 .expect("LocalAlloc failed to allocate memory")
85 }
86
87 /// Allocate memory with `LocalAlloc`
88 ///
89 /// If the allocation fails, returns the error code.
90 ///
91 /// # Safety
92 ///
93 /// The contents of the memory are not guaranteed to be a valid `T`. The
94 /// contents will either be zeroed or uninitialized depending on the `zeroed`
95 /// parameter.
96 ///
97 /// Additionally, `size` should be large enough to contain a `T`.
98 pub unsafe fn try_allocate(zeroed: bool, size: usize) -> io::Result<Self> {
99 let flags = match zeroed {
100 true => LocalAllocFlags::Fixed | LocalAllocFlags::ZeroInit,
101 false => LocalAllocFlags::Fixed,
102 };
103
104 let ptr = winapi::um::winbase::LocalAlloc(flags.bits(), size);
105
106 Ok(Self {
107 ptr: NonNull::new(ptr as *mut _).ok_or_else(io::Error::last_os_error)?,
108 })
109 }
110
111 /// Get a pointer to the underlying data structure
112 ///
113 /// Use this when interacting with FFI libraries that want pointers.
114 pub fn as_ptr(&self) -> *mut T {
115 self.ptr.as_ptr()
116 }
117}
118
119impl<T> Drop for LocalBox<T> {
120 fn drop(&mut self) {
121 let result = unsafe { winapi::um::winbase::LocalFree(self.as_ptr() as *mut _) };
122 debug_assert_eq!(result, null_mut());
123 }
124}
125
126impl<T> AsRef<T> for LocalBox<T> {
127 fn as_ref(&self) -> &T {
128 &*self
129 }
130}
131
132impl<T> Deref for LocalBox<T> {
133 type Target = T;
134
135 fn deref(&self) -> &Self::Target {
136 unsafe { self.ptr.as_ref() }
137 }
138}
139
140impl<T> DerefMut for LocalBox<T> {
141 fn deref_mut(&mut self) -> &mut Self::Target {
142 unsafe { self.ptr.as_mut() }
143 }
144}
145
146impl<T: fmt::Display> fmt::Display for LocalBox<T> {
147 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
148 self.deref().fmt(fmt)
149 }
150}
151
152impl<T: fmt::Debug> fmt::Debug for LocalBox<T> {
153 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
154 self.deref().fmt(fmt)
155 }
156}
157
158impl<T> Hash for LocalBox<T>
159where
160 T: Hash,
161{
162 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
163 self.deref().hash(state)
164 }
165}
166
167impl<T> Borrow<T> for LocalBox<T> {
168 fn borrow(&self) -> &T {
169 self.deref()
170 }
171}
172
173impl<T> BorrowMut<T> for LocalBox<T> {
174 fn borrow_mut(&mut self) -> &mut T {
175 self.deref_mut()
176 }
177}
178
179impl<T> Eq for LocalBox<T> where T: Eq {}
180impl<T, U> PartialEq<LocalBox<U>> for LocalBox<T>
181where
182 T: PartialEq<U>,
183{
184 fn eq(&self, other: &LocalBox<U>) -> bool {
185 self.deref().eq(other.deref())
186 }
187}
188
189// Safety: LocalAlloc/LocalFree are wrapper functions that call the
190// corresponding heap functions (HeapAlloc/HeapFree) using a handle to the
191// process default heap. The HeapAlloc documentation states "Serialization
192// ensures mutual exclusion when two or more threads attempt to simultaneously
193// allocate or free blocks from the same heap." Serialization may be disabled
194// with the HEAP_NO_SERIALIZE flag, but its documentation states "This value
195// should not be specified when accessing the process's default heap." because
196// the system may arbitrarily create threads that accesses that heap. Hence, it
197// is safe to assume that LocalAlloc/LocalFree are serialized, and so LocalBox
198// are safe to share across threads.
199unsafe impl<U: Send> Send for LocalBox<U> {}
200unsafe impl<U: Sync> Sync for LocalBox<U> {}