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