• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // This code should move into the default Windows shim once the win-specific
6 // allocation shim has been removed, and the generic shim has becaome the
7 // default.
8 
9 #include "partition_alloc/shim/winheap_stubs_win.h"
10 
11 #include <limits.h>
12 #include <malloc.h>
13 #include <new.h>
14 #include <windows.h>
15 
16 #include <algorithm>
17 #include <bit>
18 #include <limits>
19 
20 #include "partition_alloc/partition_alloc_base/bits.h"
21 #include "partition_alloc/partition_alloc_base/numerics/safe_conversions.h"
22 #include "partition_alloc/partition_alloc_check.h"
23 
24 namespace allocator_shim {
25 
26 bool g_is_win_shim_layer_initialized = false;
27 
28 namespace {
29 
30 const size_t kWindowsPageSize = 4096;
31 const size_t kMaxWindowsAllocation = INT_MAX - kWindowsPageSize;
32 
get_heap_handle()33 inline HANDLE get_heap_handle() {
34   return reinterpret_cast<HANDLE>(_get_heap_handle());
35 }
36 
37 }  // namespace
38 
WinHeapMalloc(size_t size)39 void* WinHeapMalloc(size_t size) {
40   if (size < kMaxWindowsAllocation) {
41     return HeapAlloc(get_heap_handle(), 0, size);
42   }
43   return nullptr;
44 }
45 
WinHeapFree(void * ptr)46 void WinHeapFree(void* ptr) {
47   if (!ptr) {
48     return;
49   }
50 
51   HeapFree(get_heap_handle(), 0, ptr);
52 }
53 
WinHeapRealloc(void * ptr,size_t size)54 void* WinHeapRealloc(void* ptr, size_t size) {
55   if (!ptr) {
56     return WinHeapMalloc(size);
57   }
58   if (!size) {
59     WinHeapFree(ptr);
60     return nullptr;
61   }
62   if (size < kMaxWindowsAllocation) {
63     return HeapReAlloc(get_heap_handle(), 0, ptr, size);
64   }
65   return nullptr;
66 }
67 
WinHeapGetSizeEstimate(void * ptr)68 size_t WinHeapGetSizeEstimate(void* ptr) {
69   if (!ptr) {
70     return 0;
71   }
72 
73   return HeapSize(get_heap_handle(), 0, ptr);
74 }
75 
76 // Call the new handler, if one has been set.
77 // Returns true on successfully calling the handler, false otherwise.
WinCallNewHandler(size_t size)78 bool WinCallNewHandler(size_t size) {
79 #ifdef _CPPUNWIND
80 #error "Exceptions in allocator shim are not supported!"
81 #endif  // _CPPUNWIND
82   // Get the current new handler.
83   _PNH nh = _query_new_handler();
84   if (!nh) {
85     return false;
86   }
87   // Since exceptions are disabled, we don't really know if new_handler
88   // failed.  Assume it will abort if it fails.
89   return nh(size) ? true : false;
90 }
91 
92 // The Windows _aligned_* functions are implemented by creating an allocation
93 // with enough space to create an aligned allocation internally. The offset to
94 // the original allocation is prefixed to the aligned allocation so that it can
95 // be correctly freed.
96 
97 namespace {
98 
99 struct AlignedPrefix {
100   // Offset to the original allocation point.
101   unsigned int original_allocation_offset;
102   // Make sure an unsigned int is enough to store the offset
103   static_assert(
104       kMaxWindowsAllocation < std::numeric_limits<unsigned int>::max(),
105       "original_allocation_offset must be able to fit into an unsigned int");
106 #if BUILDFLAG(PA_DCHECK_IS_ON)
107   // Magic value used to check that _aligned_free() and _aligned_realloc() are
108   // only ever called on an aligned allocated chunk.
109   static constexpr unsigned int kMagic = 0x12003400;
110   unsigned int magic;
111 #endif  // BUILDFLAG(PA_DCHECK_IS_ON)
112 };
113 
114 // Compute how large an allocation we need to fit an allocation with the given
115 // size and alignment and space for a prefix pointer.
AdjustedSize(size_t size,size_t alignment)116 size_t AdjustedSize(size_t size, size_t alignment) {
117   // Minimal alignment is the prefix size so the prefix is properly aligned.
118   alignment = std::max(alignment, alignof(AlignedPrefix));
119   return size + sizeof(AlignedPrefix) + alignment - 1;
120 }
121 
122 // Align the allocation and write the prefix.
AlignAllocation(void * ptr,size_t alignment)123 void* AlignAllocation(void* ptr, size_t alignment) {
124   // Minimal alignment is the prefix size so the prefix is properly aligned.
125   alignment = std::max(alignment, alignof(AlignedPrefix));
126 
127   uintptr_t address = reinterpret_cast<uintptr_t>(ptr);
128   address = partition_alloc::internal::base::bits::AlignUp(
129       address + sizeof(AlignedPrefix), alignment);
130 
131   // Write the prefix.
132   AlignedPrefix* prefix = reinterpret_cast<AlignedPrefix*>(address) - 1;
133   prefix->original_allocation_offset =
134       partition_alloc::internal::base::checked_cast<unsigned int>(
135           address - reinterpret_cast<uintptr_t>(ptr));
136 #if BUILDFLAG(PA_DCHECK_IS_ON)
137   prefix->magic = AlignedPrefix::kMagic;
138 #endif  // BUILDFLAG(PA_DCHECK_IS_ON)
139   return reinterpret_cast<void*>(address);
140 }
141 
142 // Return the original allocation from an aligned allocation.
UnalignAllocation(void * ptr)143 void* UnalignAllocation(void* ptr) {
144   AlignedPrefix* prefix = reinterpret_cast<AlignedPrefix*>(ptr) - 1;
145 #if BUILDFLAG(PA_DCHECK_IS_ON)
146   PA_DCHECK(prefix->magic == AlignedPrefix::kMagic);
147 #endif  // BUILDFLAG(PA_DCHECK_IS_ON)
148   void* unaligned =
149       static_cast<uint8_t*>(ptr) - prefix->original_allocation_offset;
150   PA_CHECK(unaligned < ptr);
151   PA_CHECK(reinterpret_cast<uintptr_t>(ptr) -
152                reinterpret_cast<uintptr_t>(unaligned) <=
153            kMaxWindowsAllocation);
154   return unaligned;
155 }
156 
157 }  // namespace
158 
WinHeapAlignedMalloc(size_t size,size_t alignment)159 void* WinHeapAlignedMalloc(size_t size, size_t alignment) {
160   PA_CHECK(std::has_single_bit(alignment));
161 
162   size_t adjusted = AdjustedSize(size, alignment);
163   if (adjusted >= kMaxWindowsAllocation) {
164     return nullptr;
165   }
166 
167   void* ptr = WinHeapMalloc(adjusted);
168   if (!ptr) {
169     return nullptr;
170   }
171 
172   return AlignAllocation(ptr, alignment);
173 }
174 
WinHeapAlignedRealloc(void * ptr,size_t size,size_t alignment)175 void* WinHeapAlignedRealloc(void* ptr, size_t size, size_t alignment) {
176   PA_CHECK(std::has_single_bit(alignment));
177 
178   if (!ptr) {
179     return WinHeapAlignedMalloc(size, alignment);
180   }
181   if (!size) {
182     WinHeapAlignedFree(ptr);
183     return nullptr;
184   }
185 
186   size_t adjusted = AdjustedSize(size, alignment);
187   if (adjusted >= kMaxWindowsAllocation) {
188     return nullptr;
189   }
190 
191   // Try to resize the allocation in place first.
192   void* unaligned = UnalignAllocation(ptr);
193   if (HeapReAlloc(get_heap_handle(), HEAP_REALLOC_IN_PLACE_ONLY, unaligned,
194                   adjusted)) {
195     return ptr;
196   }
197 
198   // Otherwise manually perform an _aligned_malloc() and copy since an
199   // unaligned allocation from HeapReAlloc() would force us to copy the
200   // allocation twice.
201   void* new_ptr = WinHeapAlignedMalloc(size, alignment);
202   if (!new_ptr) {
203     return nullptr;
204   }
205 
206   size_t gap =
207       reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(unaligned);
208   size_t old_size = WinHeapGetSizeEstimate(unaligned) - gap;
209   memcpy(new_ptr, ptr, std::min(size, old_size));
210   WinHeapAlignedFree(ptr);
211   return new_ptr;
212 }
213 
WinHeapAlignedFree(void * ptr)214 void WinHeapAlignedFree(void* ptr) {
215   if (!ptr) {
216     return;
217   }
218 
219   void* original_allocation = UnalignAllocation(ptr);
220   WinHeapFree(original_allocation);
221 }
222 
223 }  // namespace allocator_shim
224