1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
//! A specialized [`Box`] variation for items stored on the local heap.

use crate::constants::LocalAllocFlags;
use std::borrow::{Borrow, BorrowMut};
use std::cmp::PartialEq;
use std::fmt;
use std::hash::Hash;
use std::io;
use std::ops::{Deref, DerefMut};
use std::ptr::{null_mut, NonNull};

/// A smart pointer to an object on the local heap.
///
/// Windows has several different options for allocation, and the local heap is
/// no longer recommended. However, several of the
/// WinAPI calls in this crate use the local heap, allocating with `LocalAlloc`
/// and freeing with `LocalFree`. This type encapsulates that behavior,
/// representing objects that reside on the local heap.
///
/// It is primarily created using `unsafe` code in the `wrappers` crate when
/// the WinAPI allocates a data structure for the program (using `from_raw`).
///
/// However, allocations can be manually made with `allocate` or `try_allocate`.
/// For example:
///
/// ```
/// use std::mem::size_of;
/// use windows_permissions::LocalBox;
///
/// let mut local_ptr1: LocalBox<u32> = unsafe { LocalBox::allocate() };
///
/// let mut local_ptr2: LocalBox<u32> = unsafe {
///     LocalBox::try_allocate(true, size_of::<u32>()).unwrap()
/// };
///
/// *local_ptr1 = 5u32;
/// *local_ptr2 = 5u32;
/// assert_eq!(local_ptr1, local_ptr2);
/// ```
///
/// For details, see [MSDN](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localalloc#parameters).
///
/// # Exotically-sized types
///
/// This struct has not been tested with exotically-sized types. Use with
/// extreme caution.
pub struct LocalBox<T> {
    ptr: NonNull<T>,
}

impl<T> LocalBox<T> {
    /// Get a `LocalBox` from a `NonNull`
    ///
    /// # Safety
    ///
    /// - The `NonNull` pointer *must* have been allocated with
    /// a Windows API call. When the resulting `NonNull<T>` is dropped, it
    /// will be dropped with `LocalFree`
    /// - The buffer pointed to by the pointer must be a valid `T`
    pub unsafe fn from_raw(ptr: NonNull<T>) -> Self {
        // Future maintainers:
        // This function contains no unsafe code, but it requires that
        // callers fulfil an un-checked promise that is relied on by other
        // actually unsafe code. Do not remove the unsafe marker without
        // fully understanding the implications.
        Self { ptr }
    }

    /// Allocate enough zeroed memory to hold a `T` with `LocalAlloc`
    ///
    /// The memory will always come back zeroed, which has a modest performance
    /// penalty but can reduce the impact of buffer overruns.
    ///
    /// # Panics
    ///
    /// Panics if the underlying `LocalAlloc` call fails.
    ///
    /// # Safety
    ///
    /// The allocated memory is zeroed, which may not be a valid representation
    /// of a `T`.
    pub unsafe fn allocate() -> Self {
        Self::try_allocate(true, std::mem::size_of::<T>())
            .expect("LocalAlloc failed to allocate memory")
    }

    /// Allocate memory with `LocalAlloc`
    ///
    /// If the allocation fails, returns the error code.
    ///
    /// # Safety
    ///
    /// The contents of the memory are not guaranteed to be a valid `T`. The
    /// contents will either be zeroed or uninitialized depending on the `zeroed`
    /// parameter.
    ///
    /// Additionally, `size` should be large enough to contain a `T`.
    pub unsafe fn try_allocate(zeroed: bool, size: usize) -> io::Result<Self> {
        let flags = match zeroed {
            true => LocalAllocFlags::Fixed | LocalAllocFlags::ZeroInit,
            false => LocalAllocFlags::Fixed,
        };

        let ptr = winapi::um::winbase::LocalAlloc(flags.bits(), size);

        Ok(Self {
            ptr: NonNull::new(ptr as *mut _).ok_or_else(io::Error::last_os_error)?,
        })
    }

    /// Get a pointer to the underlying data structure
    ///
    /// Use this when interacting with FFI libraries that want pointers.
    pub fn as_ptr(&self) -> *mut T {
        self.ptr.as_ptr()
    }
}

impl<T> Drop for LocalBox<T> {
    fn drop(&mut self) {
        let result = unsafe { winapi::um::winbase::LocalFree(self.as_ptr() as *mut _) };
        debug_assert_eq!(result, null_mut());
    }
}

impl<T> AsRef<T> for LocalBox<T> {
    fn as_ref(&self) -> &T {
        &*self
    }
}

impl<T> Deref for LocalBox<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        unsafe { self.ptr.as_ref() }
    }
}

impl<T> DerefMut for LocalBox<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { self.ptr.as_mut() }
    }
}

impl<T: fmt::Display> fmt::Display for LocalBox<T> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        self.deref().fmt(fmt)
    }
}

impl<T: fmt::Debug> fmt::Debug for LocalBox<T> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        self.deref().fmt(fmt)
    }
}

impl<T> Hash for LocalBox<T>
where
    T: Hash,
{
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.deref().hash(state)
    }
}

impl<T> Borrow<T> for LocalBox<T> {
    fn borrow(&self) -> &T {
        self.deref()
    }
}

impl<T> BorrowMut<T> for LocalBox<T> {
    fn borrow_mut(&mut self) -> &mut T {
        self.deref_mut()
    }
}

impl<T> Eq for LocalBox<T> where T: Eq {}
impl<T, U> PartialEq<LocalBox<U>> for LocalBox<T>
where
    T: PartialEq<U>,
{
    fn eq(&self, other: &LocalBox<U>) -> bool {
        self.deref().eq(other.deref())
    }
}

// Safety: LocalAlloc/LocalFree are wrapper functions that call the
// corresponding heap functions (HeapAlloc/HeapFree) using a handle to the
// process default heap. The HeapAlloc documentation states "Serialization
// ensures mutual exclusion when two or more threads attempt to simultaneously
// allocate or free blocks from the same heap." Serialization may be disabled
// with the HEAP_NO_SERIALIZE flag, but its documentation states "This value
// should not be specified when accessing the process's default heap." because
// the system may arbitrarily create threads that accesses that heap. Hence, it
// is safe to assume that LocalAlloc/LocalFree are serialized, and so LocalBox
// are safe to share across threads.
unsafe impl<U: Send> Send for LocalBox<U> {}
unsafe impl<U: Sync> Sync for LocalBox<U> {}