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:
BackRefIdx()142 BackRefIdx() : main((uint16_t)-1) {}
isInvalid()143 bool isInvalid() { return main == (uint16_t)-1; }
isLargeObject() const144 bool isLargeObject() const { return largeObj; }
getMain() const145 uint16_t getMain() const { return main; }
getOffset() const146 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
scalableMallocCheckSize(void * object,size_t size)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
CheckStdFuncOverload(void * (* malloc_p)(size_t),void * (* calloc_p)(size_t,size_t),void * (* realloc_p)(void *,size_t),void (* free_p)(void *))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
CheckMemalignFuncOverload(void * (* memalign_p)(size_t,size_t),void (* free_p)(void *))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
CheckVallocFuncOverload(void * (* valloc_p)(size_t),void (* free_p)(void *))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
CheckPvalloc(void * (* pvalloc_p)(size_t),void (* free_p)(void *))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
CheckFreeAligned()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.
strdup(const char * str)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
TestZoneOverload()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.
TestRuntimeRoutines()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
CheckNewDeleteOverload()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
FuncReplacementInfoCheck()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;
__anon9013b3550102(int) 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