1/*
2 Copyright (c) 2005-2019 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
76extern "C" {
77void *__libc_malloc(size_t size);
78void *__libc_realloc(void *ptr, size_t size);
79void *__libc_calloc(size_t num, size_t size);
80void __libc_free(void *ptr);
81void *__libc_memalign(size_t alignment, size_t size);
82void *__libc_pvalloc(size_t size);
83void *__libc_valloc(size_t size);
84#if __TBB_DLMALLOC_USABLE_SIZE_PRESENT
85#define malloc_usable_size(p) dlmalloc_usable_size(p)
86size_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
100typedef unsigned __int16 uint16_t;
101typedef unsigned __int32 uint32_t;
102typedef 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*/
116using 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
127class BackRefIdx { // composite index to backreference array
128private:
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
132public:
133 BackRefIdx() : master((uint16_t)-1) {}
134 bool isInvalid() { return master == (uint16_t)-1; }
135 bool isLargeObject() const { return largeObj; }
136 uint16_t getMaster() const { return master; }
137 uint16_t getOffset() const { return offset; }
138
139 // only newBackRef can modify BackRefIdx
140 static BackRefIdx newBackRef(bool largeObj);
141};
142
143class MemoryPool;
144class ExtMemoryPool;
145
146struct BlockI {
147 intptr_t blockState[2];
148};
149
150struct 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
165struct 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 */
176const uintptr_t blockSize = 16*1024;
177const uint32_t fittingAlignment = rml::internal::estimatedCacheLineSize;
178#define SET_FITTING_SIZE(N) ( (blockSize-2*rml::internal::estimatedCacheLineSize)/N ) & ~(fittingAlignment-1)
179const uint32_t fittingSize5 = SET_FITTING_SIZE(2); // 8128/8064
180#undef SET_FITTING_SIZE
181const uint32_t minLargeObjectSize = fittingSize5 + 1;
182
183/* end of code replicated from src/tbbmalloc */
184
185static 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
207void 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
223void 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
232void 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
240void 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
258void 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.
278char *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
293void 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.
312void TestRuntimeRoutines() {
313 system("rem should be a safe command to call");
314}
315#else
316#define TestRuntimeRoutines()
317#endif
318
319struct BigStruct {
320 char f[minLargeObjectSize];
321};
322
323void 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
344void 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
377int 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