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