Skip to main content

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> {}