1 #![deny(unsafe_op_in_unsafe_fn)] 2 3 use crate::std::alloc::{GlobalAlloc, Layout, System}; 4 use crate::std::ffi::c_void; 5 use crate::std::ptr; 6 use crate::std::sync::atomic::{AtomicPtr, Ordering}; 7 use crate::std::sys::c; 8 use crate::std::sys::common::alloc::{realloc_fallback, MIN_ALIGN}; 9 10 #[cfg(test)] 11 mod tests; 12 13 // Heap memory management on Windows is done by using the system Heap API (heapapi.h) 14 // See https://docs.microsoft.com/windows/win32/api/heapapi/ 15 16 // Flag to indicate that the memory returned by `HeapAlloc` should be zeroed. 17 const HEAP_ZERO_MEMORY: c::DWORD = 0x00000008; 18 19 #[link(name = "kernel32")] 20 extern "system" { 21 // Get a handle to the default heap of the current process, or null if the operation fails. 22 // 23 // SAFETY: Successful calls to this function within the same process are assumed to 24 // always return the same handle, which remains valid for the entire lifetime of the process. 25 // 26 // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-getprocessheap 27 fn GetProcessHeap() -> c::HANDLE; 28 29 // Allocate a block of `dwBytes` bytes of memory from a given heap `hHeap`. 30 // The allocated memory may be uninitialized, or zeroed if `dwFlags` is 31 // set to `HEAP_ZERO_MEMORY`. 32 // 33 // Returns a pointer to the newly-allocated memory or null if the operation fails. 34 // The returned pointer will be aligned to at least `MIN_ALIGN`. 35 // 36 // SAFETY: 37 // - `hHeap` must be a non-null handle returned by `GetProcessHeap`. 38 // - `dwFlags` must be set to either zero or `HEAP_ZERO_MEMORY`. 39 // 40 // Note that `dwBytes` is allowed to be zero, contrary to some other allocators. 41 // 42 // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapalloc 43 fn HeapAlloc(hHeap: c::HANDLE, dwFlags: c::DWORD, dwBytes: c::SIZE_T) -> c::LPVOID; 44 45 // Reallocate a block of memory behind a given pointer `lpMem` from a given heap `hHeap`, 46 // to a block of at least `dwBytes` bytes, either shrinking the block in place, 47 // or allocating at a new location, copying memory, and freeing the original location. 48 // 49 // Returns a pointer to the reallocated memory or null if the operation fails. 50 // The returned pointer will be aligned to at least `MIN_ALIGN`. 51 // If the operation fails the given block will never have been freed. 52 // 53 // SAFETY: 54 // - `hHeap` must be a non-null handle returned by `GetProcessHeap`. 55 // - `dwFlags` must be set to zero. 56 // - `lpMem` must be a non-null pointer to an allocated block returned by `HeapAlloc` or 57 // `HeapReAlloc`, that has not already been freed. 58 // If the block was successfully reallocated at a new location, pointers pointing to 59 // the freed memory, such as `lpMem`, must not be dereferenced ever again. 60 // 61 // Note that `dwBytes` is allowed to be zero, contrary to some other allocators. 62 // 63 // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heaprealloc 64 fn HeapReAlloc( 65 hHeap: c::HANDLE, 66 dwFlags: c::DWORD, 67 lpMem: c::LPVOID, 68 dwBytes: c::SIZE_T, 69 ) -> c::LPVOID; 70 71 // Free a block of memory behind a given pointer `lpMem` from a given heap `hHeap`. 72 // Returns a nonzero value if the operation is successful, and zero if the operation fails. 73 // 74 // SAFETY: 75 // - `hHeap` must be a non-null handle returned by `GetProcessHeap`. 76 // - `dwFlags` must be set to zero. 77 // - `lpMem` must be a pointer to an allocated block returned by `HeapAlloc` or `HeapReAlloc`, 78 // that has not already been freed. 79 // If the block was successfully freed, pointers pointing to the freed memory, such as `lpMem`, 80 // must not be dereferenced ever again. 81 // 82 // Note that `lpMem` is allowed to be null, which will not cause the operation to fail. 83 // 84 // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapfree 85 fn HeapFree(hHeap: c::HANDLE, dwFlags: c::DWORD, lpMem: c::LPVOID) -> c::BOOL; 86 } 87 88 // Cached handle to the default heap of the current process. 89 // Either a non-null handle returned by `GetProcessHeap`, or null when not yet initialized or `GetProcessHeap` failed. 90 static HEAP: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut()); 91 92 // Get a handle to the default heap of the current process, or null if the operation fails. 93 // If this operation is successful, `HEAP` will be successfully initialized and contain 94 // a non-null handle returned by `GetProcessHeap`. 95 #[inline] 96 fn init_or_get_process_heap() -> c::HANDLE { 97 let heap = HEAP.load(Ordering::Relaxed); 98 if heap.is_null() { 99 // `HEAP` has not yet been successfully initialized 100 let heap = unsafe { GetProcessHeap() }; 101 if !heap.is_null() { 102 // SAFETY: No locking is needed because within the same process, 103 // successful calls to `GetProcessHeap` will always return the same value, even on different threads. 104 HEAP.store(heap, Ordering::Release); 105 106 // SAFETY: `HEAP` contains a non-null handle returned by `GetProcessHeap` 107 heap 108 } else { 109 // Could not get the current process heap. 110 ptr::null_mut() 111 } 112 } else { 113 // SAFETY: `HEAP` contains a non-null handle returned by `GetProcessHeap` 114 heap 115 } 116 } 117 118 // Get a non-null handle to the default heap of the current process. 119 // SAFETY: `HEAP` must have been successfully initialized. 120 #[inline] 121 unsafe fn get_process_heap() -> c::HANDLE { 122 HEAP.load(Ordering::Acquire) 123 } 124 125 // Header containing a pointer to the start of an allocated block. 126 // SAFETY: Size and alignment must be <= `MIN_ALIGN`. 127 #[repr(C)] 128 struct Header(*mut u8); 129 130 // Allocate a block of optionally zeroed memory for a given `layout`. 131 // SAFETY: Returns a pointer satisfying the guarantees of `System` about allocated pointers, 132 // or null if the operation fails. If this returns non-null `HEAP` will have been successfully 133 // initialized. 134 #[inline] 135 unsafe fn allocate(layout: Layout, zeroed: bool) -> *mut u8 { 136 let heap = init_or_get_process_heap(); 137 if heap.is_null() { 138 // Allocation has failed, could not get the current process heap. 139 return ptr::null_mut(); 140 } 141 142 // Allocated memory will be either zeroed or uninitialized. 143 let flags = if zeroed { HEAP_ZERO_MEMORY } else { 0 }; 144 145 if layout.align() <= MIN_ALIGN { 146 // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`. 147 // The returned pointer points to the start of an allocated block. 148 unsafe { HeapAlloc(heap, flags, layout.size()) as *mut u8 } 149 } else { 150 // Allocate extra padding in order to be able to satisfy the alignment. 151 let total = layout.align() + layout.size(); 152 153 // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`. 154 let ptr = unsafe { HeapAlloc(heap, flags, total) as *mut u8 }; 155 if ptr.is_null() { 156 // Allocation has failed. 157 return ptr::null_mut(); 158 } 159 160 // Create a correctly aligned pointer offset from the start of the allocated block, 161 // and write a header before it. 162 163 let offset = layout.align() - (ptr.addr() & (layout.align() - 1)); 164 // SAFETY: `MIN_ALIGN` <= `offset` <= `layout.align()` and the size of the allocated 165 // block is `layout.align() + layout.size()`. `aligned` will thus be a correctly aligned 166 // pointer inside the allocated block with at least `layout.size()` bytes after it and at 167 // least `MIN_ALIGN` bytes of padding before it. 168 let aligned = unsafe { ptr.add(offset) }; 169 // SAFETY: Because the size and alignment of a header is <= `MIN_ALIGN` and `aligned` 170 // is aligned to at least `MIN_ALIGN` and has at least `MIN_ALIGN` bytes of padding before 171 // it, it is safe to write a header directly before it. 172 unsafe { ptr::write((aligned as *mut Header).sub(1), Header(ptr)) }; 173 174 // SAFETY: The returned pointer does not point to the to the start of an allocated block, 175 // but there is a header readable directly before it containing the location of the start 176 // of the block. 177 aligned 178 } 179 } 180 181 // All pointers returned by this allocator have, in addition to the guarantees of `GlobalAlloc`, the 182 // following properties: 183 // 184 // If the pointer was allocated or reallocated with a `layout` specifying an alignment <= `MIN_ALIGN` 185 // the pointer will be aligned to at least `MIN_ALIGN` and point to the start of the allocated block. 186 // 187 // If the pointer was allocated or reallocated with a `layout` specifying an alignment > `MIN_ALIGN` 188 // the pointer will be aligned to the specified alignment and not point to the start of the allocated block. 189 // Instead there will be a header readable directly before the returned pointer, containing the actual 190 // location of the start of the block. 191 unsafe impl GlobalAlloc for System { 192 #[inline] 193 unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 194 // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System` 195 let zeroed = false; 196 unsafe { allocate(layout, zeroed) } 197 } 198 199 #[inline] 200 unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { 201 // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System` 202 let zeroed = true; 203 unsafe { allocate(layout, zeroed) } 204 } 205 206 #[inline] 207 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { 208 let block = { 209 if layout.align() <= MIN_ALIGN { 210 ptr 211 } else { 212 // The location of the start of the block is stored in the padding before `ptr`. 213 214 // SAFETY: Because of the contract of `System`, `ptr` is guaranteed to be non-null 215 // and have a header readable directly before it. 216 unsafe { ptr::read((ptr as *mut Header).sub(1)).0 } 217 } 218 }; 219 220 // SAFETY: because `ptr` has been successfully allocated with this allocator, 221 // `HEAP` must have been successfully initialized. 222 let heap = unsafe { get_process_heap() }; 223 224 // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`, 225 // `block` is a pointer to the start of an allocated block. 226 unsafe { HeapFree(heap, 0, block as c::LPVOID) }; 227 } 228 229 #[inline] 230 unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { 231 if layout.align() <= MIN_ALIGN { 232 // SAFETY: because `ptr` has been successfully allocated with this allocator, 233 // `HEAP` must have been successfully initialized. 234 let heap = unsafe { get_process_heap() }; 235 236 // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`, 237 // `ptr` is a pointer to the start of an allocated block. 238 // The returned pointer points to the start of an allocated block. 239 unsafe { HeapReAlloc(heap, 0, ptr as c::LPVOID, new_size) as *mut u8 } 240 } else { 241 // SAFETY: `realloc_fallback` is implemented using `dealloc` and `alloc`, which will 242 // correctly handle `ptr` and return a pointer satisfying the guarantees of `System` 243 unsafe { realloc_fallback(self, ptr, layout, new_size) } 244 } 245 } 246 } 247