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 #pragma GCC diagnostic push
452 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
453     struct mallinfo info = mallinfo();
454 #pragma GCC diagnostic pop
455     // right now mallinfo initialized by zero
456     REQUIRE((!info.arena && !info.ordblks && !info.smblks && !info.hblks
457            && !info.hblkhd && !info.usmblks && !info.fsmblks
458            && !info.uordblks && !info.fordblks && !info.keepcost));
459 
460  #if !__ANDROID__
461     // These non-standard functions are exported by GLIBC, and might be used
462     // in conjunction with standard malloc/free. Test that we overload them as well.
463     // Bionic doesn't have them.
464     CheckStdFuncOverload(__libc_malloc, __libc_calloc, __libc_realloc, __libc_free);
465     CheckMemalignFuncOverload(__libc_memalign, __libc_free);
466     CheckVallocFuncOverload(__libc_valloc, __libc_free);
467     CheckPvalloc(__libc_pvalloc, __libc_free);
468  #endif
469 #endif // __linux__
470 
471 #else // MALLOC_WINDOWS_OVERLOAD_ENABLED
472 
473     ptr = _aligned_malloc(minLargeObjectSize, 16);
474     scalableMallocCheckSize(ptr, minLargeObjectSize);
475     REQUIRE(tbb::detail::is_aligned(ptr, 16));
476 
477     // Testing of workaround for vs "is power of 2 pow N" bug that accepts zeros
478     void* ptr1 = _aligned_malloc(minLargeObjectSize, 0);
479     scalableMallocCheckSize(ptr, minLargeObjectSize);
480     REQUIRE(tbb::detail::is_aligned(ptr, sizeof(void*)));
481     _aligned_free(ptr1);
482 
483     ptr1 = _aligned_realloc(ptr, minLargeObjectSize*10, 16);
484     scalableMallocCheckSize(ptr1, minLargeObjectSize*10);
485     REQUIRE(tbb::detail::is_aligned(ptr, 16));
486     _aligned_free(ptr1);
487 
488     FuncReplacementInfoCheck();
489 
490 #endif
491     CheckFreeAligned();
492 
493     CheckNewDeleteOverload();
494 
495 #if _WIN32
496     std::string stdstring = "dependency on msvcpXX.dll";
497     REQUIRE(strcmp(stdstring.c_str(), "dependency on msvcpXX.dll") == 0);
498 #endif
499     TestZoneOverload();
500     TestRuntimeRoutines();
501 }
502 #endif // !HARNESS_SKIP_TEST
503