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