1 /* 2 Copyright (c) 2005-2020 Intel Corporation 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 //! \file test_malloc_overload.cpp 18 //! \brief Test for [memory_allocation] functionality 19 20 #define __TBB_NO_IMPLICIT_LINKAGE 1 21 22 #if (_WIN32 || _WIN64) 23 // As the test is intentionally build with /EHs-, suppress multiple VS2005's 24 // warnings like C4530: C++ exception handler used, but unwind semantics are not enabled 25 #if defined(_MSC_VER) && !__INTEL_COMPILER 26 /* ICC 10.1 and 11.0 generates code that uses std::_Raise_handler, 27 but it's only defined in libcpmt(d), which the test doesn't linked with. 28 */ 29 #undef _HAS_EXCEPTIONS 30 #define _HAS_EXCEPTIONS _CPPUNWIND 31 #endif 32 // to use strdup w/o warnings 33 #define _CRT_NONSTDC_NO_DEPRECATE 1 34 #endif // _WIN32 || _WIN64 35 36 #define _ISOC11_SOURCE 1 // to get C11 declarations for GLIBC 37 38 #include "common/allocator_overload.h" 39 40 #if MALLOC_WINDOWS_OVERLOAD_ENABLED 41 #include "tbb/tbbmalloc_proxy.h" 42 #endif 43 44 #include "common/test.h" 45 46 #if !HARNESS_SKIP_TEST 47 48 #if __ANDROID__ 49 #include <android/api-level.h> // for __ANDROID_API__ 50 #endif 51 52 #define __TBB_POSIX_MEMALIGN_PRESENT (__linux__ && !__ANDROID__) || __APPLE__ 53 #define __TBB_PVALLOC_PRESENT __linux__ && !__ANDROID__ 54 #if __GLIBC__ 55 // aligned_alloc available since GLIBC 2.16 56 #define __TBB_ALIGNED_ALLOC_PRESENT __GLIBC_PREREQ(2, 16) 57 #endif // __GLIBC__ 58 // later Android doesn't have valloc or dlmalloc_usable_size 59 #define __TBB_VALLOC_PRESENT (__linux__ && __ANDROID_API__<21) || __APPLE__ 60 #define __TBB_DLMALLOC_USABLE_SIZE_PRESENT __ANDROID__ && __ANDROID_API__<21 61 62 #include "common/utils.h" 63 #include "common/utils_report.h" 64 #include "common/utils_assert.h" 65 #include "common/utils_env.h" 66 67 #include <stdlib.h> 68 #include <string.h> 69 #if !__APPLE__ 70 #include <malloc.h> 71 #endif 72 #include <stdio.h> 73 #include <new> 74 #if MALLOC_UNIXLIKE_OVERLOAD_ENABLED || MALLOC_ZONE_OVERLOAD_ENABLED 75 #include <unistd.h> // for sysconf 76 #include <dlfcn.h> 77 #endif 78 79 #if __linux__ 80 #include <stdint.h> // for uintptr_t 81 82 extern "C" { 83 void *__libc_malloc(size_t size); 84 void *__libc_realloc(void *ptr, size_t size); 85 void *__libc_calloc(size_t num, size_t size); 86 void __libc_free(void *ptr); 87 void *__libc_memalign(size_t alignment, size_t size); 88 void *__libc_pvalloc(size_t size); 89 void *__libc_valloc(size_t size); 90 #if __TBB_DLMALLOC_USABLE_SIZE_PRESENT 91 #define malloc_usable_size(p) dlmalloc_usable_size(p) 92 size_t dlmalloc_usable_size(const void *ptr); 93 #endif 94 } 95 96 #elif __APPLE__ 97 98 #include <malloc/malloc.h> 99 #define malloc_usable_size(p) malloc_size(p) 100 101 #elif _WIN32 102 #include <stddef.h> 103 #if __MINGW32__ 104 #include <unistd.h> 105 #else 106 typedef unsigned __int16 uint16_t; 107 typedef unsigned __int32 uint32_t; 108 typedef unsigned __int64 uint64_t; 109 #endif 110 111 #endif /* OS selection */ 112 113 #if _WIN32 114 // On Windows, the trick with string "dependency on msvcpXX.dll" is necessary to create 115 // dependency on msvcpXX.dll, for sake of a regression test. 116 // On Linux, C++ RTL headers are undesirable because of breaking strict ANSI mode. 117 #if defined(_MSC_VER) && _MSC_VER >= 1300 && _MSC_VER <= 1310 && !defined(__INTEL_COMPILER) 118 /* Fixing compilation error reported by VS2003 for exception class 119 when _HAS_EXCEPTIONS is 0: 120 bad_cast that inherited from exception is not in std namespace. 121 */ 122 using namespace std; 123 #endif 124 #include <string> 125 #include <set> 126 #include <sstream> 127 #endif 128 129 #include "oneapi/tbb/detail/_utils.h" // tbb::detail::is_aligned 130 #include "src/tbbmalloc/shared_utils.h" // alignDown, alignUp, estimatedCacheLineSize 131 132 /* start of code replicated from src/tbbmalloc */ 133 134 class BackRefIdx { // composite index to backreference array 135 private: 136 uint16_t master; // index in BackRefMaster 137 uint16_t largeObj:1; // is this object "large"? 138 uint16_t offset :15; // offset from beginning of BackRefBlock 139 public: 140 BackRefIdx() : master((uint16_t)-1) {} 141 bool isInvalid() { return master == (uint16_t)-1; } 142 bool isLargeObject() const { return largeObj; } 143 uint16_t getMaster() const { return master; } 144 uint16_t getOffset() const { return offset; } 145 146 // only newBackRef can modify BackRefIdx 147 static BackRefIdx newBackRef(bool largeObj); 148 }; 149 150 class MemoryPool; 151 class ExtMemoryPool; 152 153 struct BlockI { 154 intptr_t blockState[2]; 155 }; 156 157 struct LargeMemoryBlock : public BlockI { 158 MemoryPool *pool; // owner pool 159 LargeMemoryBlock *next, // ptrs in list of cached blocks 160 *prev, 161 *gPrev, // in pool's global list 162 *gNext; 163 uintptr_t age; // age of block while in cache 164 size_t objectSize; // the size requested by a client 165 size_t unalignedSize; // the size requested from getMemory 166 bool fromMapMemory; 167 BackRefIdx backRefIdx; // cached here, used copy is in LargeObjectHdr 168 void registerInPool(ExtMemoryPool *extMemPool); 169 void unregisterFromPool(ExtMemoryPool *extMemPool); 170 }; 171 172 struct LargeObjectHdr { 173 LargeMemoryBlock *memoryBlock; 174 /* Have to duplicate it here from CachedObjectHdr, 175 as backreference must be checked without further pointer dereference. 176 Points to LargeObjectHdr. */ 177 BackRefIdx backRefIdx; 178 }; 179 180 /* 181 * Objects of size minLargeObjectSize and larger are considered large objects. 182 */ 183 const uintptr_t blockSize = 16*1024; 184 const uint32_t fittingAlignment = rml::internal::estimatedCacheLineSize; 185 #define SET_FITTING_SIZE(N) ( (blockSize-2*rml::internal::estimatedCacheLineSize)/N ) & ~(fittingAlignment-1) 186 const uint32_t fittingSize5 = SET_FITTING_SIZE(2); // 8128/8064 187 #undef SET_FITTING_SIZE 188 const uint32_t minLargeObjectSize = fittingSize5 + 1; 189 190 /* end of code replicated from src/tbbmalloc */ 191 192 static void scalableMallocCheckSize(void *object, size_t size) 193 { 194 #if __clang__ 195 // This prevents Clang from throwing out the calls to new & delete in CheckNewDeleteOverload(). 196 static void *v = object; 197 utils::suppress_unused_warning(v); 198 #endif 199 REQUIRE(object); 200 if (size >= minLargeObjectSize) { 201 LargeMemoryBlock *lmb = ((LargeObjectHdr*)object-1)->memoryBlock; 202 REQUIRE((uintptr_t(lmb)<uintptr_t(((LargeObjectHdr*)object-1)) && lmb->objectSize >= size)); 203 } 204 #if MALLOC_UNIXLIKE_OVERLOAD_ENABLED || MALLOC_ZONE_OVERLOAD_ENABLED 205 REQUIRE(malloc_usable_size(object) >= size); 206 #elif MALLOC_WINDOWS_OVERLOAD_ENABLED 207 // Check that _msize works correctly 208 REQUIRE(_msize(object) >= size); 209 REQUIRE((size < 8 || _aligned_msize(object,8,0) >= size)); 210 #endif 211 } 212 213 void CheckStdFuncOverload(void *(*malloc_p)(size_t), void *(*calloc_p)(size_t, size_t), 214 void *(*realloc_p)(void *, size_t), void (*free_p)(void *)) 215 { 216 void *ptr = malloc_p(minLargeObjectSize); 217 scalableMallocCheckSize(ptr, minLargeObjectSize); 218 free(ptr); 219 220 ptr = calloc_p(minLargeObjectSize, 2); 221 scalableMallocCheckSize(ptr, 2*minLargeObjectSize); 222 void *ptr1 = realloc_p(ptr, 10*minLargeObjectSize); 223 scalableMallocCheckSize(ptr1, 10*minLargeObjectSize); 224 free_p(ptr1); 225 } 226 227 #if MALLOC_UNIXLIKE_OVERLOAD_ENABLED || MALLOC_ZONE_OVERLOAD_ENABLED 228 229 void CheckMemalignFuncOverload(void *(*memalign_p)(size_t, size_t), 230 void (*free_p)(void*)) 231 { 232 void *ptr = memalign_p(128, 4*minLargeObjectSize); 233 scalableMallocCheckSize(ptr, 4*minLargeObjectSize); 234 REQUIRE(tbb::detail::is_aligned(ptr, 128)); 235 free_p(ptr); 236 } 237 238 void CheckVallocFuncOverload(void *(*valloc_p)(size_t), void (*free_p)(void*)) 239 { 240 void *ptr = valloc_p(minLargeObjectSize); 241 scalableMallocCheckSize(ptr, minLargeObjectSize); 242 REQUIRE(tbb::detail::is_aligned(ptr, sysconf(_SC_PAGESIZE))); 243 free_p(ptr); 244 } 245 246 void CheckPvalloc(void *(*pvalloc_p)(size_t), void (*free_p)(void*)) 247 { 248 const long memoryPageSize = sysconf(_SC_PAGESIZE); 249 // request large object with not power-of-2 size 250 const size_t largeSz = alignUp(minLargeObjectSize, 16*1024) + 1; 251 252 for (size_t sz = 0; sz<=largeSz; sz+=largeSz) { 253 void *ptr = pvalloc_p(sz); 254 scalableMallocCheckSize(ptr, sz? alignUp(sz, memoryPageSize) : memoryPageSize); 255 REQUIRE(tbb::detail::is_aligned(ptr, memoryPageSize)); 256 free_p(ptr); 257 } 258 } 259 260 #endif // MALLOC_UNIXLIKE_OVERLOAD_ENABLED || MALLOC_ZONE_OVERLOAD_ENABLED 261 262 // regression test: on macOS scalable_free() treated small aligned object, 263 // placed in large block, as small block 264 void CheckFreeAligned() { 265 size_t sz[] = {8, 4*1024, 16*1024, 0}; 266 size_t align[] = {8, 4*1024, 16*1024, 0}; 267 268 for (int s=0; sz[s]; s++) 269 for (int a=0; align[a]; a++) { 270 void *ptr = NULL; 271 #if __TBB_POSIX_MEMALIGN_PRESENT 272 int ret = posix_memalign(&ptr, align[a], sz[s]); 273 REQUIRE(!ret); 274 #elif MALLOC_WINDOWS_OVERLOAD_ENABLED 275 ptr = _aligned_malloc(sz[s], align[a]); 276 #endif 277 REQUIRE(tbb::detail::is_aligned(ptr, align[a])); 278 free(ptr); 279 } 280 } 281 282 #if __ANDROID__ 283 // Workaround for an issue with strdup somehow bypassing our malloc replacement on Android. 284 char *strdup(const char *str) { 285 REPORT( "Known issue: malloc replacement does not work for strdup on Android.\n" ); 286 size_t len = strlen(str)+1; 287 void *new_str = malloc(len); 288 return new_str ? reinterpret_cast<char *>(memcpy(new_str, str, len)) : 0; 289 } 290 #endif 291 292 #if __APPLE__ 293 #include <mach/mach.h> 294 295 // regression test: malloc_usable_size() that was passed to zone interface 296 // called system malloc_usable_size(), so for object that was not allocated 297 // by tbbmalloc non-zero was returned, so such objects were passed to 298 // tbbmalloc's free(), that is incorrect 299 void TestZoneOverload() { 300 vm_address_t *zones; 301 unsigned zones_num; 302 303 kern_return_t ret = malloc_get_all_zones(mach_task_self(), NULL, &zones, &zones_num); 304 REQUIRE((!ret && zones_num>1)); 305 malloc_zone_t *sys_zone = (malloc_zone_t*)zones[1]; 306 REQUIRE_MESSAGE(strcmp("tbbmalloc", malloc_get_zone_name(sys_zone)), "zone 1 expected to be not tbbmalloc"); 307 void *p = malloc_zone_malloc(sys_zone, 16); 308 free(p); 309 } 310 #else 311 #define TestZoneOverload() 312 #endif 313 314 #if _WIN32 315 // regression test: certain MSVC runtime functions use "public" allocation functions 316 // but internal free routines, causing crashes if tbbmalloc_proxy does not intercept the latter. 317 void TestRuntimeRoutines() { 318 system("rem should be a safe command to call"); 319 } 320 #else 321 #define TestRuntimeRoutines() 322 #endif 323 324 struct BigStruct { 325 char f[minLargeObjectSize]; 326 }; 327 328 void CheckNewDeleteOverload() { 329 BigStruct *s1, *s2, *s3, *s4; 330 331 s1 = new BigStruct; 332 scalableMallocCheckSize(s1, sizeof(BigStruct)); 333 delete s1; 334 335 s2 = new BigStruct[10]; 336 scalableMallocCheckSize(s2, 10*sizeof(BigStruct)); 337 delete []s2; 338 339 s3 = new(std::nothrow) BigStruct; 340 scalableMallocCheckSize(s3, sizeof(BigStruct)); 341 delete s3; 342 343 s4 = new(std::nothrow) BigStruct[2]; 344 scalableMallocCheckSize(s4, 2*sizeof(BigStruct)); 345 delete []s4; 346 } 347 348 #if MALLOC_WINDOWS_OVERLOAD_ENABLED 349 void FuncReplacementInfoCheck() { 350 char **func_replacement_log; 351 int func_replacement_status = TBB_malloc_replacement_log(&func_replacement_log); 352 353 std::set<std::string> functions; 354 functions.insert("free"); 355 functions.insert("_msize"); 356 functions.insert("_aligned_free"); 357 functions.insert("_aligned_msize"); 358 359 int status_check = 0; 360 for (char** log_string = func_replacement_log; *log_string != 0; log_string++) { 361 std::stringstream s(*log_string); 362 std::string status, function_name; 363 s >> status >> function_name; 364 365 if (status.find("Fail:") != status.npos) { 366 status_check = -1; 367 } 368 369 functions.erase(function_name); 370 } 371 372 REQUIRE_MESSAGE(functions.empty(), "Changed opcodes log must contain all required functions with \"Success\" changed status"); 373 REQUIRE_MESSAGE(func_replacement_status == status_check, "replacement_opcodes_log() function return wrong status"); 374 375 func_replacement_status = TBB_malloc_replacement_log(NULL); 376 REQUIRE_MESSAGE(func_replacement_status == status_check, "replacement_opcodes_log() function return wrong status"); 377 378 // TODO: was ASSERT_WARNING 379 if (func_replacement_status != 0) { 380 REPORT("Some standard allocation functions was not replaced to tbb_malloc functions.\n"); 381 } 382 } 383 #endif // MALLOC_WINDOWS_OVERLOAD_ENABLED 384 385 //! Testing tbbmalloc_proxy overload capabilities 386 //! \brief \ref error_guessing 387 TEST_CASE("Main set of tests") { 388 void *ptr = NULL; 389 utils::suppress_unused_warning(ptr); // for android 390 391 #if MALLOC_UNIXLIKE_OVERLOAD_ENABLED || MALLOC_ZONE_OVERLOAD_ENABLED 392 REQUIRE_MESSAGE(dlsym(RTLD_DEFAULT, "scalable_malloc"), "Lost dependency on malloc_proxy or LD_PRELOAD was not set?"); 393 #endif 394 395 /* On Windows, memory block size returned by _msize() is sometimes used 396 to calculate the size for an extended block. Substituting _msize, 397 scalable_msize initially returned 0 for regions not allocated by the scalable 398 allocator, which led to incorrect memory reallocation and subsequent crashes. 399 It was found that adding a new environment variable triggers the error. 400 */ 401 REQUIRE_MESSAGE(getenv("PATH"), "We assume that PATH is set everywhere."); 402 char *pathCopy = strdup(getenv("PATH")); 403 #if __ANDROID__ 404 REQUIRE_MESSAGE(strcmp(pathCopy,getenv("PATH")) == 0, "strdup workaround does not work as expected."); 405 #endif 406 const char *newEnvName = "__TBBMALLOC_OVERLOAD_REGRESSION_TEST_FOR_REALLOC_AND_MSIZE"; 407 REQUIRE_MESSAGE(!getenv(newEnvName), "Environment variable should not be used before."); 408 int r = utils::SetEnv(newEnvName,"1"); 409 REQUIRE(!r); 410 char *path = getenv("PATH"); 411 REQUIRE_MESSAGE((path && 0==strcmp(path, pathCopy)), "Environment was changed erroneously."); 412 free(pathCopy); 413 414 CheckStdFuncOverload(malloc, calloc, realloc, free); 415 #if MALLOC_UNIXLIKE_OVERLOAD_ENABLED || MALLOC_ZONE_OVERLOAD_ENABLED 416 417 #if __TBB_POSIX_MEMALIGN_PRESENT 418 int ret = posix_memalign(&ptr, 1024, 3*minLargeObjectSize); 419 REQUIRE(0 == ret); 420 scalableMallocCheckSize(ptr, 3*minLargeObjectSize); 421 REQUIRE(tbb::detail::is_aligned(ptr, 1024)); 422 free(ptr); 423 #endif 424 425 #if __TBB_VALLOC_PRESENT 426 CheckVallocFuncOverload(valloc, free); 427 #endif 428 #if __TBB_PVALLOC_PRESENT 429 CheckPvalloc(pvalloc, free); 430 #endif 431 #if __linux__ 432 CheckMemalignFuncOverload(memalign, free); 433 #if __TBB_ALIGNED_ALLOC_PRESENT 434 CheckMemalignFuncOverload(aligned_alloc, free); 435 #endif 436 437 struct mallinfo info = mallinfo(); 438 // right now mallinfo initialized by zero 439 REQUIRE((!info.arena && !info.ordblks && !info.smblks && !info.hblks 440 && !info.hblkhd && !info.usmblks && !info.fsmblks 441 && !info.uordblks && !info.fordblks && !info.keepcost)); 442 443 #if !__ANDROID__ 444 // These non-standard functions are exported by GLIBC, and might be used 445 // in conjunction with standard malloc/free. Test that we overload them as well. 446 // Bionic doesn't have them. 447 CheckStdFuncOverload(__libc_malloc, __libc_calloc, __libc_realloc, __libc_free); 448 CheckMemalignFuncOverload(__libc_memalign, __libc_free); 449 CheckVallocFuncOverload(__libc_valloc, __libc_free); 450 CheckPvalloc(__libc_pvalloc, __libc_free); 451 #endif 452 #endif // __linux__ 453 454 #else // MALLOC_WINDOWS_OVERLOAD_ENABLED 455 456 ptr = _aligned_malloc(minLargeObjectSize, 16); 457 scalableMallocCheckSize(ptr, minLargeObjectSize); 458 REQUIRE(tbb::detail::is_aligned(ptr, 16)); 459 460 // Testing of workaround for vs "is power of 2 pow N" bug that accepts zeros 461 void* ptr1 = _aligned_malloc(minLargeObjectSize, 0); 462 scalableMallocCheckSize(ptr, minLargeObjectSize); 463 REQUIRE(tbb::detail::is_aligned(ptr, sizeof(void*))); 464 _aligned_free(ptr1); 465 466 ptr1 = _aligned_realloc(ptr, minLargeObjectSize*10, 16); 467 scalableMallocCheckSize(ptr1, minLargeObjectSize*10); 468 REQUIRE(tbb::detail::is_aligned(ptr, 16)); 469 _aligned_free(ptr1); 470 471 FuncReplacementInfoCheck(); 472 473 #endif 474 CheckFreeAligned(); 475 476 CheckNewDeleteOverload(); 477 478 #if _WIN32 479 std::string stdstring = "dependency on msvcpXX.dll"; 480 REQUIRE(strcmp(stdstring.c_str(), "dependency on msvcpXX.dll") == 0); 481 #endif 482 TestZoneOverload(); 483 TestRuntimeRoutines(); 484 } 485 #endif // !HARNESS_SKIP_TEST 486