Skip to main content

std\sys\alloc/
windows.rs

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