1 // 2 // expand.cpp 3 // 4 // Copyright (c) Microsoft Corporation. All rights reserved. 5 // 6 // Implementation of _expand(). 7 // 8 #include <corecrt_internal.h> 9 #include <malloc.h> 10 11 12 13 // Enclaves have a different heap implementation. 14 15 #ifdef _UCRT_ENCLAVE_BUILD 16 17 // Tests whether the allocation contraction is possible 18 __inline static bool is_contraction_possible(size_t const) throw() 19 { 20 return FALSE; 21 } 22 23 #else /* ^^^ _UCRT_ENCLAVE_BUILD ^^^ // vvv !_UCRT_ENCLAVE_BUILD vvv */ 24 25 // Tests whether the allocation contraction is possible 26 __inline static bool is_contraction_possible(size_t const old_size) throw() 27 { 28 // Check if object allocated on low fragmentation heap. 29 //The LFH can only allocate blocks up to 16KB in size. 30 if (old_size <= 0x4000) 31 { 32 LONG heap_type = -1; 33 if (!HeapQueryInformation( 34 __acrt_heap, 35 HeapCompatibilityInformation, 36 &heap_type, 37 sizeof(heap_type), 38 nullptr)) 39 { 40 return FALSE; 41 } 42 return heap_type != 2; 43 } 44 // Contraction possible for objects not on the LFH 45 return TRUE; 46 } 47 48 #endif /* _UCRT_ENCLAVE_BUILD */ 49 50 51 // This function implements the logic of expand(). It is called directly by the 52 // _expand() function in the Release CRT, and called by the debug heap in the 53 // Debug CRT. 54 // 55 // This function must be marked noinline, otherwise _expand and 56 // _expand_base will have identical COMDATs, and the linker will fold 57 // them when calling one from the CRT. This is necessary because _expand 58 // needs to support users patching in custom implementations. 59 extern "C" __declspec(noinline) void* __cdecl _expand_base(void* const block, size_t const size) 60 { 61 // Validation section 62 _VALIDATE_RETURN (block != nullptr, EINVAL, nullptr); 63 _VALIDATE_RETURN_NOEXC(size <= _HEAP_MAXREQ, ENOMEM, nullptr); 64 65 size_t const old_size = static_cast<size_t>(HeapSize(__acrt_heap, 0, block)); 66 size_t const new_size = size == 0 ? 1 : size; 67 68 void* new_block = HeapReAlloc(__acrt_heap, HEAP_REALLOC_IN_PLACE_ONLY, block, new_size); 69 if (new_block != nullptr) 70 return new_block; 71 72 // If a failure to contract was caused by platform limitations, just 73 // return the original block. 74 if (new_size <= old_size && !is_contraction_possible(old_size)) 75 return block; 76 77 errno = __acrt_errno_from_os_error(GetLastError()); 78 return nullptr; 79 } 80 81 // Expands or contracts a block of memory in the heap. 82 // 83 // This function resizes a block of memory in the heap to 'size' bytes. The 84 // new size may be either greater (expansion) or less (contraction) than the 85 // original size of the block. This function never moves the block. In the 86 // case of expansion, if the block cannot be expanded to 'size', it is expanded 87 // as much as possible. 88 // 89 // This function supports patching and therefore must be marked noinline. 90 // Both _expand_dbg and _expand_base must also be marked noinline 91 // to prevent identical COMDAT folding from substituting calls to _expand 92 // with either other function or vice versa. 93 extern "C" _CRT_HYBRIDPATCHABLE __declspec(noinline) void* __cdecl _expand(void* const block, size_t const size) 94 { 95 #ifdef _DEBUG 96 return _expand_dbg(block, size, _NORMAL_BLOCK, nullptr, 0); 97 #else 98 return _expand_base(block, size); 99 #endif 100 } 101