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