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 //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 = NULL;
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(), NULL, &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(NULL);
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 = NULL;
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 #pragma GCC diagnostic push
453 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
454     struct mallinfo info = mallinfo();
455 #pragma GCC diagnostic pop
456     // right now mallinfo initialized by zero
457     REQUIRE((!info.arena && !info.ordblks && !info.smblks && !info.hblks
458            && !info.hblkhd && !info.usmblks && !info.fsmblks
459            && !info.uordblks && !info.fordblks && !info.keepcost));
460 
461  #if !__ANDROID__
462     // These non-standard functions are exported by GLIBC, and might be used
463     // in conjunction with standard malloc/free. Test that we overload them as well.
464     // Bionic doesn't have them.
465     CheckStdFuncOverload(__libc_malloc, __libc_calloc, __libc_realloc, __libc_free);
466     CheckMemalignFuncOverload(__libc_memalign, __libc_free);
467     CheckVallocFuncOverload(__libc_valloc, __libc_free);
468     CheckPvalloc(__libc_pvalloc, __libc_free);
469  #endif
470 #endif // __unix__
471 
472 #else // MALLOC_WINDOWS_OVERLOAD_ENABLED
473 
474     ptr = _aligned_malloc(minLargeObjectSize, 16);
475     scalableMallocCheckSize(ptr, minLargeObjectSize);
476     REQUIRE(tbb::detail::is_aligned(ptr, 16));
477 
478     // Testing of workaround for vs "is power of 2 pow N" bug that accepts zeros
479     void* ptr1 = _aligned_malloc(minLargeObjectSize, 0);
480     scalableMallocCheckSize(ptr, minLargeObjectSize);
481     REQUIRE(tbb::detail::is_aligned(ptr, sizeof(void*)));
482     _aligned_free(ptr1);
483 
484     ptr1 = _aligned_realloc(ptr, minLargeObjectSize*10, 16);
485     scalableMallocCheckSize(ptr1, minLargeObjectSize*10);
486     REQUIRE(tbb::detail::is_aligned(ptr, 16));
487     _aligned_free(ptr1);
488 
489     FuncReplacementInfoCheck();
490 
491 #endif
492     CheckFreeAligned();
493 
494     CheckNewDeleteOverload();
495 
496 #if _WIN32
497     std::string stdstring = "dependency on msvcpXX.dll";
498     REQUIRE(strcmp(stdstring.c_str(), "dependency on msvcpXX.dll") == 0);
499 #endif
500     TestZoneOverload();
501     TestRuntimeRoutines();
502 }
503 
504 //! Test address range tracker in backend that could be
505 //! broken during remap because of incorrect order of
506 //! deallocation event and the mremap system call
507 //! \brief \ref regression
508 TEST_CASE("Address range tracker regression test") {
509     int numThreads = 16;
510     utils::NativeParallelFor(numThreads, [](int) {
511         void *ptr = nullptr;
512         for (int i = 0; i < 1000; ++i) {
513             for (int j = 0; j < 100; ++j) {
514                 ptr = realloc(ptr, 1024*1024 + 4096*j);
515             }
516         }
517         free(ptr);
518     });
519 }
520 #endif // !HARNESS_SKIP_TEST
521