main
cpp 384 lines 11.6 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 #include "precomp.h"
4
5
6 // Exit macros
7 #define MemExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__)
8 #define MemExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__)
9 #define MemExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__)
10 #define MemExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__)
11 #define MemExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__)
12 #define MemExitWithRootFailure(x, e, s, ...) ExitWithRootFailureSource(DUTIL_SOURCE_MEMUTIL, x, e, s, __VA_ARGS__)
13 #define MemExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__)
14 #define MemExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_MEMUTIL, p, x, e, s, __VA_ARGS__)
15 #define MemExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, p, x, s, __VA_ARGS__)
16 #define MemExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_MEMUTIL, p, x, e, s, __VA_ARGS__)
17 #define MemExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, p, x, s, __VA_ARGS__)
18 #define MemExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_MEMUTIL, e, x, s, __VA_ARGS__)
19 #define MemExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_MEMUTIL, g, x, s, __VA_ARGS__)
20
21
22 #if DEBUG
23 static BOOL vfMemInitialized = FALSE;
24 #endif
25
26 extern "C" HRESULT DAPI MemInitialize()
27 {
28 #if DEBUG
29 vfMemInitialized = TRUE;
30 #endif
31 return S_OK;
32 }
33
34 extern "C" void DAPI MemUninitialize()
35 {
36 #if DEBUG
37 vfMemInitialized = FALSE;
38 #endif
39 }
40
41 extern "C" LPVOID DAPI MemAlloc(
42 __in SIZE_T cbSize,
43 __in BOOL fZero
44 )
45 {
46 // AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash");
47 AssertSz(0 < cbSize, "MemAlloc() called with invalid size");
48 return ::HeapAlloc(::GetProcessHeap(), fZero ? HEAP_ZERO_MEMORY : 0, cbSize);
49 }
50
51
52 extern "C" LPVOID DAPI MemReAlloc(
53 __in LPVOID pv,
54 __in SIZE_T cbSize,
55 __in BOOL fZero
56 )
57 {
58 // AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash");
59 AssertSz(0 < cbSize, "MemReAlloc() called with invalid size");
60 return ::HeapReAlloc(::GetProcessHeap(), fZero ? HEAP_ZERO_MEMORY : 0, pv, cbSize);
61 }
62
63
64 extern "C" HRESULT DAPI MemReAllocSecure(
65 __in LPVOID pv,
66 __in SIZE_T cbSize,
67 __in BOOL fZero,
68 __deref_out LPVOID* ppvNew
69 )
70 {
71 // AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash");
72 AssertSz(ppvNew, "MemReAllocSecure() called with uninitialized pointer");
73 AssertSz(0 < cbSize, "MemReAllocSecure() called with invalid size");
74
75 HRESULT hr = S_OK;
76 DWORD dwFlags = HEAP_REALLOC_IN_PLACE_ONLY;
77 LPVOID pvNew = NULL;
78 SIZE_T cb = 0;
79
80 dwFlags |= fZero ? HEAP_ZERO_MEMORY : 0;
81 pvNew = ::HeapReAlloc(::GetProcessHeap(), dwFlags, pv, cbSize);
82 if (!pvNew)
83 {
84 pvNew = MemAlloc(cbSize, fZero);
85 if (pvNew)
86 {
87 hr = MemSizeChecked(pv, &cb);
88 MemExitOnFailure(hr, "Failed to get current memory size.");
89
90 const SIZE_T cbCurrent = cb;
91
92 // HeapReAlloc may allocate more memory than requested.
93 hr = MemSizeChecked(pvNew, &cb);
94 MemExitOnFailure(hr, "Failed to get new memory size.");
95
96 const SIZE_T cbNew = cb;
97
98 cbSize = cbNew;
99 if (cbSize > cbCurrent)
100 {
101 cbSize = cbCurrent;
102 }
103
104 memcpy_s(pvNew, cbNew, pv, cbSize);
105
106 SecureZeroMemory(pv, cbCurrent);
107 MemFree(pv);
108 }
109 }
110 MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to reallocate memory");
111
112 *ppvNew = pvNew;
113 pvNew = NULL;
114
115 LExit:
116 ReleaseMem(pvNew);
117
118 return hr;
119 }
120
121
122 extern "C" HRESULT DAPI MemAllocArray(
123 __inout LPVOID* ppvArray,
124 __in SIZE_T cbArrayType,
125 __in DWORD dwItemCount
126 )
127 {
128 return MemReAllocArray(ppvArray, 0, cbArrayType, dwItemCount);
129 }
130
131
132 extern "C" HRESULT DAPI MemReAllocArray(
133 __inout LPVOID* ppvArray,
134 __in DWORD cArray,
135 __in SIZE_T cbArrayType,
136 __in DWORD dwNewItemCount
137 )
138 {
139 HRESULT hr = S_OK;
140 DWORD cNew = 0;
141 LPVOID pvNew = NULL;
142 SIZE_T cbNew = 0;
143
144 hr = ::DWordAdd(cArray, dwNewItemCount, &cNew);
145 MemExitOnFailure(hr, "Integer overflow when calculating new element count.");
146
147 hr = ::SIZETMult(cNew, cbArrayType, &cbNew);
148 MemExitOnFailure(hr, "Integer overflow when calculating new block size.");
149
150 if (*ppvArray)
151 {
152 SIZE_T cbCurrent = 0;
153 hr = MemSizeChecked(*ppvArray, &cbCurrent);
154 MemExitOnFailure(hr, "Failed to get current memory size.");
155
156 if (cbCurrent < cbNew)
157 {
158 pvNew = MemReAlloc(*ppvArray, cbNew, TRUE);
159 MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate larger array.");
160
161 *ppvArray = pvNew;
162 }
163 }
164 else
165 {
166 pvNew = MemAlloc(cbNew, TRUE);
167 MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array.");
168
169 *ppvArray = pvNew;
170 }
171
172 LExit:
173 return hr;
174 }
175
176
177 extern "C" HRESULT DAPI MemEnsureArraySize(
178 __deref_inout_bcount(cArray * cbArrayType) LPVOID* ppvArray,
179 __in DWORD cArray,
180 __in SIZE_T cbArrayType,
181 __in DWORD dwGrowthCount
182 )
183 {
184 HRESULT hr = S_OK;
185 DWORD cNew = 0;
186 LPVOID pvNew = NULL;
187 SIZE_T cbNew = 0;
188
189 hr = ::DWordAdd(cArray, dwGrowthCount, &cNew);
190 MemExitOnFailure(hr, "Integer overflow when calculating new element count.");
191
192 hr = ::SIZETMult(cNew, cbArrayType, &cbNew);
193 MemExitOnFailure(hr, "Integer overflow when calculating new block size.");
194
195 if (*ppvArray)
196 {
197 SIZE_T cbUsed = cArray * cbArrayType;
198 SIZE_T cbCurrent = 0;
199
200 hr = MemSizeChecked(*ppvArray, &cbCurrent);
201 MemExitOnFailure(hr, "Failed to get current memory size.");
202
203 if (cbCurrent < cbUsed)
204 {
205 pvNew = MemReAlloc(*ppvArray, cbNew, TRUE);
206 MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate array larger.");
207
208 *ppvArray = pvNew;
209 }
210 }
211 else
212 {
213 pvNew = MemAlloc(cbNew, TRUE);
214 MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array.");
215
216 *ppvArray = pvNew;
217 }
218
219 LExit:
220 return hr;
221 }
222
223
224 extern "C" HRESULT DAPI MemEnsureArraySizeForNewItems(
225 __inout LPVOID* ppvArray,
226 __in DWORD cArray,
227 __in DWORD cNewItems,
228 __in SIZE_T cbArrayType,
229 __in DWORD dwGrowthCount
230 )
231 {
232 HRESULT hr = S_OK;
233 DWORD cNew = 0;
234
235 hr = ::DWordAdd(cArray, cNewItems, &cNew);
236 MemExitOnFailure(hr, "Integer overflow when calculating new element count.");
237
238 hr = MemEnsureArraySize(ppvArray, cNew, cbArrayType, dwGrowthCount);
239
240 LExit:
241 return hr;
242 }
243
244
245 extern "C" HRESULT DAPI MemInsertIntoArray(
246 __deref_inout_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray,
247 __in DWORD dwInsertIndex,
248 __in DWORD cInsertItems,
249 __in DWORD cExistingArray,
250 __in SIZE_T cbArrayType,
251 __in DWORD dwGrowthCount
252 )
253 {
254 HRESULT hr = S_OK;
255 DWORD i;
256 BYTE *pbArray = NULL;
257
258 if (0 == cInsertItems)
259 {
260 ExitFunction1(hr = S_OK);
261 }
262
263 hr = MemEnsureArraySizeForNewItems(ppvArray, cExistingArray, cInsertItems, cbArrayType, dwGrowthCount);
264 MemExitOnFailure(hr, "Failed to resize array while inserting items");
265
266 pbArray = reinterpret_cast<BYTE *>(*ppvArray);
267 for (i = cExistingArray + cInsertItems - 1; i > dwInsertIndex; --i)
268 {
269 memcpy_s(pbArray + i * cbArrayType, cbArrayType, pbArray + (i - 1) * cbArrayType, cbArrayType);
270 }
271
272 // Zero out the newly-inserted items
273 memset(pbArray + dwInsertIndex * cbArrayType, 0, cInsertItems * cbArrayType);
274
275 LExit:
276 return hr;
277 }
278
279 extern "C" void DAPI MemRemoveFromArray(
280 __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray,
281 __in DWORD dwRemoveIndex,
282 __in DWORD cRemoveItems,
283 __in DWORD cExistingArray,
284 __in SIZE_T cbArrayType,
285 __in BOOL fPreserveOrder
286 )
287 {
288 BYTE *pbArray = static_cast<BYTE *>(pvArray);
289 DWORD cItemsLeftAfterRemoveIndex = (cExistingArray - cRemoveItems - dwRemoveIndex);
290
291 if (fPreserveOrder)
292 {
293 memmove(pbArray + dwRemoveIndex * cbArrayType, pbArray + (dwRemoveIndex + cRemoveItems) * cbArrayType, cItemsLeftAfterRemoveIndex * cbArrayType);
294 }
295 else
296 {
297 DWORD cItemsToMove = (cRemoveItems > cItemsLeftAfterRemoveIndex ? cItemsLeftAfterRemoveIndex : cRemoveItems);
298 memmove(pbArray + dwRemoveIndex * cbArrayType, pbArray + (cExistingArray - cItemsToMove) * cbArrayType, cItemsToMove * cbArrayType);
299 }
300
301 ZeroMemory(pbArray + (cExistingArray - cRemoveItems) * cbArrayType, cRemoveItems * cbArrayType);
302 }
303
304 extern "C" void DAPI MemArraySwapItems(
305 __inout_bcount(cbArrayType) LPVOID pvArray,
306 __in DWORD dwIndex1,
307 __in DWORD dwIndex2,
308 __in SIZE_T cbArrayType
309 )
310 {
311 BYTE *pbArrayItem1 = static_cast<BYTE *>(pvArray) + dwIndex1 * cbArrayType;
312 BYTE *pbArrayItem2 = static_cast<BYTE *>(pvArray) + dwIndex2 * cbArrayType;
313 DWORD dwByteIndex = 0;
314
315 if (dwIndex1 == dwIndex2)
316 {
317 return;
318 }
319
320 // Use XOR swapping to avoid the need for a temporary item
321 while (dwByteIndex < cbArrayType)
322 {
323 // Try to do many bytes at a time in most cases
324 if (cbArrayType - dwByteIndex > sizeof(DWORD64))
325 {
326 // x: X xor Y
327 *(reinterpret_cast<DWORD64 *>(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast<DWORD64 *>(pbArrayItem2 + dwByteIndex));
328 // y: X xor Y
329 *(reinterpret_cast<DWORD64 *>(pbArrayItem2 + dwByteIndex)) = *(reinterpret_cast<DWORD64 *>(pbArrayItem1 + dwByteIndex)) ^ *(reinterpret_cast<DWORD64 *>(pbArrayItem2 + dwByteIndex));
330 // x: X xor Y
331 *(reinterpret_cast<DWORD64 *>(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast<DWORD64 *>(pbArrayItem2 + dwByteIndex));
332
333 dwByteIndex += sizeof(DWORD64);
334 }
335 else
336 {
337 // x: X xor Y
338 *(reinterpret_cast<unsigned char *>(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast<unsigned char *>(pbArrayItem2 + dwByteIndex));
339 // y: X xor Y
340 *(reinterpret_cast<unsigned char *>(pbArrayItem2 + dwByteIndex)) = *(reinterpret_cast<unsigned char *>(pbArrayItem1 + dwByteIndex)) ^ *(reinterpret_cast<unsigned char *>(pbArrayItem2 + dwByteIndex));
341 // x: X xor Y
342 *(reinterpret_cast<unsigned char *>(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast<unsigned char *>(pbArrayItem2 + dwByteIndex));
343
344 dwByteIndex += sizeof(unsigned char);
345 }
346 }
347 }
348
349 extern "C" HRESULT DAPI MemFree(
350 __in LPVOID pv
351 )
352 {
353 // AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash");
354 return ::HeapFree(::GetProcessHeap(), 0, pv) ? S_OK : HRESULT_FROM_WIN32(::GetLastError());
355 }
356
357
358 extern "C" SIZE_T DAPI MemSize(
359 __in LPCVOID pv
360 )
361 {
362 // AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash");
363 return ::HeapSize(::GetProcessHeap(), 0, pv);
364 }
365
366
367 extern "C" HRESULT DAPI MemSizeChecked(
368 __in LPCVOID pv,
369 __out SIZE_T* pcb
370 )
371 {
372 HRESULT hr = S_OK;
373
374 // AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash");
375 *pcb = MemSize(pv);
376
377 if (-1 == *pcb)
378 {
379 MemExitWithRootFailure(hr, E_INVALIDARG, "Failed to get memory size");
380 }
381
382 LExit:
383 return hr;
384 }