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