1 /*
2     Copyright (c) 2005-2022 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_compliance.cpp
18 //! \brief Test for [memory_allocation.scalable_alloc_c_interface] functionality
19 
20 #define __TBB_NO_IMPLICIT_LINKAGE 1
21 
22 #define __STDC_LIMIT_MACROS 1 // to get SIZE_MAX from stdint.h
23 
24 #include "common/test.h"
25 
26 #include "common/utils.h"
27 #include "common/utils_report.h"
28 #include "common/spin_barrier.h"
29 #include "common/memory_usage.h"
30 
31 #include "oneapi/tbb/detail/_config.h"
32 
33 #define __TBB_NO_IMPLICIT_LINKAGE 1
34 #include "tbb/scalable_allocator.h"
35 
36 #include <vector>
37 
38 #if _WIN32 || _WIN64
39 /**
40  *  _WIN32_WINNT should be defined at the very beginning,
41  *  because other headers might include <windows.h>
42  **/
43 #undef _WIN32_WINNT
44 #define _WIN32_WINNT 0x0501
45 #include <windows.h>
46 #include <stdio.h>
47 
48 #if _MSC_VER && defined(_MT) && defined(_DLL)
49     #pragma comment(lib, "version.lib")  // to use GetFileVersionInfo*
50 #endif
51 
52 void limitMem( size_t limit )
53 {
54     static HANDLE hJob = nullptr;
55     JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo;
56 
57     jobInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_MEMORY;
58     jobInfo.ProcessMemoryLimit = limit? limit*MByte : 2*MByte*1024;
59     if (nullptr == hJob) {
60         if (nullptr == (hJob = CreateJobObject(nullptr, nullptr))) {
61             REPORT("Can't assign create job object: %ld\n", GetLastError());
62             exit(1);
63         }
64         if (0 == AssignProcessToJobObject(hJob, GetCurrentProcess())) {
65             REPORT("Can't assign process to job object: %ld\n", GetLastError());
66             exit(1);
67         }
68     }
69     if (0 == SetInformationJobObject(hJob, JobObjectExtendedLimitInformation,
70                                      &jobInfo, sizeof(jobInfo))) {
71         REPORT("Can't set limits: %ld\n", GetLastError());
72         exit(1);
73     }
74 }
75 // Do not test errno with static VC runtime
76 #else // _WIN32 || _WIN64
77 #include <sys/resource.h>
78 #include <stdlib.h>
79 #include <stdio.h>
80 #include <errno.h>
81 #include <sys/types.h>  // uint64_t on FreeBSD, needed for rlim_t
82 #include <stdint.h>     // SIZE_MAX
83 
84 void limitMem( size_t limit )
85 {
86     rlimit rlim;
87     int ret = getrlimit(RLIMIT_AS,&rlim);
88     if (0 != ret) {
89         REPORT("getrlimit() returned an error: errno %d\n", errno);
90         exit(1);
91     }
92     if (rlim.rlim_max==(rlim_t)RLIM_INFINITY)
93         rlim.rlim_cur = (limit > 0) ? limit*MByte : rlim.rlim_max;
94     else rlim.rlim_cur = (limit > 0 && static_cast<rlim_t>(limit)<rlim.rlim_max) ? limit*MByte : rlim.rlim_max;
95     ret = setrlimit(RLIMIT_AS,&rlim);
96     if (0 != ret) {
97         REPORT("Can't set limits: errno %d\n", errno);
98         exit(1);
99     }
100 }
101 #endif  // _WIN32 || _WIN64
102 
103 bool __tbb_test_errno = false;
104 
105 #define ASSERT_ERRNO(cond, msg)  REQUIRE_MESSAGE( (!__tbb_test_errno || (cond)), msg )
106 #define CHECK_ERRNO(cond) (__tbb_test_errno && (cond))
107 
108 static const int MinThread = 1;
109 static const int MaxThread = 4;
110 static bool Verbose = false;
111 
112 #include <time.h>
113 #include <errno.h>
114 #include <limits.h> // for CHAR_BIT
115 
116 #if __unix__
117 #include <stdint.h> // uintptr_t
118 #endif
119 #if _WIN32 || _WIN64
120 #include <malloc.h> // _aligned_(malloc|free|realloc)
121 #if __MINGW64__
122 // Workaround a bug in MinGW64 headers with _aligned_(malloc|free) not declared by default
123 extern "C" void __cdecl __declspec(dllimport) _aligned_free(void *);
124 extern "C" void *__cdecl __declspec(dllimport) _aligned_malloc(size_t,size_t);
125 #endif
126 #endif
127 
128 #include <vector>
129 
130 const int COUNT_ELEM = 25000;
131 const size_t MAX_SIZE = 1000;
132 const int COUNTEXPERIMENT = 10000;
133 
134 const char strError[]="failed";
135 const char strOk[]="done";
136 
137 typedef unsigned int UINT;
138 typedef unsigned char UCHAR;
139 typedef unsigned long DWORD;
140 typedef unsigned char BYTE;
141 
142 typedef void* TestMalloc(size_t size);
143 typedef void* TestCalloc(size_t num, size_t size);
144 typedef void* TestRealloc(void* memblock, size_t size);
145 typedef void  TestFree(void* memblock);
146 typedef int   TestPosixMemalign(void **memptr, size_t alignment, size_t size);
147 typedef void* TestAlignedMalloc(size_t size, size_t alignment);
148 typedef void* TestAlignedRealloc(void* memblock, size_t size, size_t alignment);
149 typedef void  TestAlignedFree(void* memblock);
150 
151 // pointers to tested functions
152 TestMalloc*  Rmalloc;
153 TestCalloc*  Rcalloc;
154 TestRealloc* Rrealloc;
155 TestFree*    Tfree;
156 TestPosixMemalign*  Rposix_memalign;
157 TestAlignedMalloc*  Raligned_malloc;
158 TestAlignedRealloc* Raligned_realloc;
159 TestAlignedFree* Taligned_free;
160 
161 // call functions via pointer and check result's alignment
162 void* Tmalloc(size_t size);
163 void* Tcalloc(size_t num, size_t size);
164 void* Trealloc(void* memblock, size_t size);
165 int   Tposix_memalign(void **memptr, size_t alignment, size_t size);
166 void* Taligned_malloc(size_t size, size_t alignment);
167 void* Taligned_realloc(void* memblock, size_t size, size_t alignment);
168 
169 std::atomic<bool> error_occurred{ false };
170 
171 #if __APPLE__
172 // Tests that use the variables are skipped on macOS*
173 #else
174 const size_t COUNT_ELEM_CALLOC = 2;
175 const int COUNT_TESTS = 1000;
176 static bool perProcessLimits = true;
177 #endif
178 
179 const size_t POWERS_OF_2 = 20;
180 
181 struct MemStruct
182 {
183     void* Pointer;
184     UINT Size;
185 
186     MemStruct() : Pointer(nullptr), Size(0) {}
187     MemStruct(void* ptr, UINT sz) : Pointer(ptr), Size(sz) {}
188 };
189 
190 class CMemTest: utils::NoAssign
191 {
192     bool FullLog;
193     utils::SpinBarrier *limitBarrier;
194     static bool firstTime;
195 
196 public:
197     CMemTest(utils::SpinBarrier *barrier, bool isVerbose=false) : limitBarrier(barrier)
198         {
199             srand((UINT)time(nullptr));
200             FullLog=isVerbose;
201         }
202     void NULLReturn(UINT MinSize, UINT MaxSize, int total_threads); // nullptr pointer + check errno
203     void UniquePointer(); // unique pointer - check with padding
204     void AddrArifm(); // unique pointer - check with pointer arithmetic
205     bool ShouldReportError();
206     void Free_NULL(); //
207     void Zerofilling(); // check if arrays are zero-filled
208     void TestAlignedParameters();
209     void RunAllTests(int total_threads);
210     ~CMemTest() {}
211 };
212 
213 class Limit {
214     size_t limit;
215 public:
216     Limit(size_t a_limit) : limit(a_limit) {}
217     void operator() () const {
218         limitMem(limit);
219     }
220 };
221 
222 int argC;
223 char** argV;
224 
225 struct RoundRobin: utils::NoAssign {
226     const long number_of_threads;
227     mutable CMemTest test;
228 
229     RoundRobin( long p, utils::SpinBarrier *limitBarrier, bool verbose ) :
230         number_of_threads(p), test(limitBarrier, verbose) {}
231     void operator()( int /*id*/ ) const
232         {
233             test.RunAllTests(number_of_threads);
234         }
235 };
236 
237 bool CMemTest::firstTime = true;
238 
239 inline size_t choose_random_alignment() {
240     return sizeof(void*)<<(rand() % POWERS_OF_2);
241 }
242 
243 #if TBB_REVAMP_TODO
244 // TODO: enable this test mode
245 static void setSystemAllocs()
246 {
247     Rmalloc=malloc;
248     Rrealloc=realloc;
249     Rcalloc=calloc;
250     Tfree=free;
251 #if _WIN32 || _WIN64
252     Raligned_malloc=_aligned_malloc;
253     Raligned_realloc=_aligned_realloc;
254     Taligned_free=_aligned_free;
255     Rposix_memalign=0;
256 #elif  __APPLE__ || __sun || __ANDROID__
257 // macOS, Solaris*, and Android* don't have posix_memalign
258     Raligned_malloc=0;
259     Raligned_realloc=0;
260     Taligned_free=0;
261     Rposix_memalign=0;
262 #else
263     Raligned_malloc=0;
264     Raligned_realloc=0;
265     Taligned_free=0;
266     Rposix_memalign=posix_memalign;
267 #endif
268 }
269 #endif
270 
271 // check that realloc works as free and as malloc
272 void ReallocParam()
273 {
274     const int ITERS = 1000;
275     int i;
276     void *bufs[ITERS];
277 
278     bufs[0] = Trealloc(nullptr, 30*MByte);
279     REQUIRE_MESSAGE(bufs[0], "Can't get memory to start the test.");
280 
281     for (i=1; i<ITERS; i++)
282     {
283         bufs[i] = Trealloc(nullptr, 30*MByte);
284         if (nullptr == bufs[i])
285             break;
286     }
287     REQUIRE_MESSAGE(i<ITERS, "Limits should be decreased for the test to work.");
288 
289     Trealloc(bufs[0], 0);
290     /* There is a race for the free space between different threads at
291        this point. So, have to run the test sequentially.
292     */
293     bufs[0] = Trealloc(nullptr, 30*MByte);
294     REQUIRE(bufs[0]);
295 
296     for (int j=0; j<i; j++)
297         Trealloc(bufs[j], 0);
298 }
299 
300 void CheckArgumentsOverflow()
301 {
302     void *p;
303     const size_t params[] = {SIZE_MAX, SIZE_MAX-16};
304 
305     for (unsigned i=0; i<utils::array_length(params); i++) {
306         p = Tmalloc(params[i]);
307         REQUIRE(!p);
308         ASSERT_ERRNO(errno==ENOMEM, nullptr);
309         p = Trealloc(nullptr, params[i]);
310         REQUIRE(!p);
311         ASSERT_ERRNO(errno==ENOMEM, nullptr);
312         p = Tcalloc(1, params[i]);
313         REQUIRE(!p);
314         ASSERT_ERRNO(errno==ENOMEM, nullptr);
315         p = Tcalloc(params[i], 1);
316         REQUIRE(!p);
317         ASSERT_ERRNO(errno==ENOMEM, nullptr);
318     }
319     const size_t max_alignment = size_t(1) << (sizeof(size_t)*CHAR_BIT - 1);
320     if (Rposix_memalign) {
321         int ret = Rposix_memalign(&p, max_alignment, ~max_alignment);
322         REQUIRE(ret == ENOMEM);
323         for (unsigned i=0; i<utils::array_length(params); i++) {
324             ret = Rposix_memalign(&p, max_alignment, params[i]);
325             REQUIRE(ret == ENOMEM);
326             ret = Rposix_memalign(&p, sizeof(void*), params[i]);
327             REQUIRE(ret == ENOMEM);
328         }
329     }
330     if (Raligned_malloc) {
331         p = Raligned_malloc(~max_alignment, max_alignment);
332         REQUIRE(!p);
333         for (unsigned i=0; i<utils::array_length(params); i++) {
334             p = Raligned_malloc(params[i], max_alignment);
335             REQUIRE(!p);
336             ASSERT_ERRNO(errno==ENOMEM, nullptr);
337             p = Raligned_malloc(params[i], sizeof(void*));
338             REQUIRE(!p);
339             ASSERT_ERRNO(errno==ENOMEM, nullptr);
340         }
341     }
342 
343     p = Tcalloc(SIZE_MAX/2-16, SIZE_MAX/2-16);
344     REQUIRE(!p);
345     ASSERT_ERRNO(errno==ENOMEM, nullptr);
346     p = Tcalloc(SIZE_MAX/2, SIZE_MAX/2);
347     REQUIRE(!p);
348     ASSERT_ERRNO(errno==ENOMEM, nullptr);
349 }
350 
351 void InvariantDataRealloc(bool aligned, size_t maxAllocSize, bool checkData)
352 {
353     utils::FastRandom<> fastRandom(1);
354     size_t size = 0, start = 0;
355     char *ptr = nullptr,
356         // external thread to create copies and compare ralloc result against it
357         *base = (char*)Tmalloc(2*maxAllocSize);
358 
359     REQUIRE(base);
360     REQUIRE_MESSAGE(!(2*maxAllocSize%sizeof(unsigned short)),
361            "The loop below expects that 2*maxAllocSize contains sizeof(unsigned short)");
362     for (size_t k = 0; k<2*maxAllocSize; k+=sizeof(unsigned short))
363         *(unsigned short*)(base+k) = fastRandom.get();
364 
365     for (int i=0; i<100; i++) {
366         // don't want sizeNew==0 here
367         const size_t sizeNew = fastRandom.get() % (maxAllocSize-1) + 1;
368         char *ptrNew = aligned?
369             (char*)Taligned_realloc(ptr, sizeNew, choose_random_alignment())
370             : (char*)Trealloc(ptr, sizeNew);
371         REQUIRE(ptrNew);
372         // check that old data not changed
373         if (checkData)
374             REQUIRE_MESSAGE(!memcmp(ptrNew, base+start, utils::min(size, sizeNew)), "broken data");
375 
376         // prepare fresh data, copying them from random position in external
377         size = sizeNew;
378         ptr = ptrNew;
379         if (checkData) {
380             start = fastRandom.get() % maxAllocSize;
381             memcpy(ptr, base+start, size);
382         }
383     }
384     if (aligned)
385         Taligned_realloc(ptr, 0, choose_random_alignment());
386     else
387         Trealloc(ptr, 0);
388     Tfree(base);
389 }
390 
391 void CheckReallocLeak()
392 {
393     int i;
394     const int ITER_TO_STABILITY = 10;
395     // do bootstrap
396     for (int k=0; k<3; k++)
397         InvariantDataRealloc(/*aligned=*/false, 128*MByte, /*checkData=*/false);
398     size_t prev = utils::GetMemoryUsage(utils::peakUsage);
399     // expect realloc to not increase peak memory consumption after ITER_TO_STABILITY-1 iterations
400     for (i=0; i<ITER_TO_STABILITY; i++) {
401         for (int k=0; k<3; k++)
402             InvariantDataRealloc(/*aligned=*/false, 128*MByte, /*checkData=*/false);
403         size_t curr = utils::GetMemoryUsage(utils::peakUsage);
404         if (prev == curr)
405             break;
406         prev = curr;
407     }
408     REQUIRE_MESSAGE(i < ITER_TO_STABILITY, "Can't stabilize memory consumption.");
409 }
410 
411 // if non-zero byte found, returns bad value address plus 1
412 size_t NonZero(void *ptr, size_t size)
413 {
414     size_t words = size / sizeof(intptr_t);
415     size_t tailSz = size % sizeof(intptr_t);
416     intptr_t *buf =(intptr_t*)ptr;
417     char *bufTail =(char*)(buf+words);
418 
419     for (size_t i=0; i<words; i++)
420         if (buf[i]) {
421             for (unsigned b=0; b<sizeof(intptr_t); b++)
422                 if (((char*)(buf+i))[b])
423                     return sizeof(intptr_t)*i + b + 1;
424         }
425     for (size_t i=0; i<tailSz; i++)
426         if (bufTail[i]) {
427             return words*sizeof(intptr_t)+i+1;
428         }
429     return 0;
430 }
431 
432 struct TestStruct
433 {
434     DWORD field1:2;
435     DWORD field2:6;
436     double field3;
437     UCHAR field4[100];
438     TestStruct* field5;
439     std::vector<int> field7;
440     double field8;
441 };
442 
443 void* Tmalloc(size_t size)
444 {
445     // For compatibility, on 64-bit systems malloc should align to 16 bytes
446     size_t alignment = (sizeof(intptr_t)>4 && size>8) ? 16 : 8;
447     void *ret = Rmalloc(size);
448     if (nullptr != ret)
449         CHECK_FAST_MESSAGE(0==((uintptr_t)ret & (alignment-1)),
450                "allocation result should be properly aligned");
451     return ret;
452 }
453 void* Tcalloc(size_t num, size_t size)
454 {
455     // For compatibility, on 64-bit systems calloc should align to 16 bytes
456     size_t alignment = (sizeof(intptr_t)>4 && num && size>8) ? 16 : 8;
457     void *ret = Rcalloc(num, size);
458     if (nullptr != ret)
459         CHECK_FAST_MESSAGE(0==((uintptr_t)ret & (alignment-1)),
460                "allocation result should be properly aligned");
461     return ret;
462 }
463 void* Trealloc(void* memblock, size_t size)
464 {
465     // For compatibility, on 64-bit systems realloc should align to 16 bytes
466     size_t alignment = (sizeof(intptr_t)>4 && size>8) ? 16 : 8;
467     void *ret = Rrealloc(memblock, size);
468     if (nullptr != ret)
469         CHECK_FAST_MESSAGE(0==((uintptr_t)ret & (alignment-1)),
470                "allocation result should be properly aligned");
471     return ret;
472 }
473 int Tposix_memalign(void **memptr, size_t alignment, size_t size)
474 {
475     int ret = Rposix_memalign(memptr, alignment, size);
476     if (0 == ret)
477         CHECK_FAST_MESSAGE(0==((uintptr_t)*memptr & (alignment-1)),
478                "allocation result should be aligned");
479     return ret;
480 }
481 void* Taligned_malloc(size_t size, size_t alignment)
482 {
483     void *ret = Raligned_malloc(size, alignment);
484     if (nullptr != ret)
485         CHECK_FAST_MESSAGE(0==((uintptr_t)ret & (alignment-1)),
486                "allocation result should be aligned");
487     return ret;
488 }
489 void* Taligned_realloc(void* memblock, size_t size, size_t alignment)
490 {
491     void *ret = Raligned_realloc(memblock, size, alignment);
492     if (nullptr != ret)
493         CHECK_FAST_MESSAGE(0==((uintptr_t)ret & (alignment-1)),
494                "allocation result should be aligned");
495     return ret;
496 }
497 
498 struct PtrSize {
499     void  *ptr;
500     size_t size;
501 };
502 
503 static int cmpAddrs(const void *p1, const void *p2)
504 {
505     const PtrSize *a = (const PtrSize *)p1;
506     const PtrSize *b = (const PtrSize *)p2;
507 
508     return a->ptr < b->ptr ? -1 : ( a->ptr == b->ptr ? 0 : 1);
509 }
510 
511 void CMemTest::AddrArifm()
512 {
513     PtrSize *arr = (PtrSize*)Tmalloc(COUNT_ELEM*sizeof(PtrSize));
514 
515     if (FullLog) REPORT("\nUnique pointer using Address arithmetic\n");
516     if (FullLog) REPORT("malloc....");
517     REQUIRE(arr);
518     for (int i=0; i<COUNT_ELEM; i++)
519     {
520         arr[i].size=rand()%MAX_SIZE;
521         arr[i].ptr=Tmalloc(arr[i].size);
522     }
523     qsort(arr, COUNT_ELEM, sizeof(PtrSize), cmpAddrs);
524 
525     for (int i=0; i<COUNT_ELEM-1; i++)
526     {
527         if (nullptr!=arr[i].ptr && nullptr!=arr[i+1].ptr)
528             REQUIRE_MESSAGE((uintptr_t)arr[i].ptr+arr[i].size <= (uintptr_t)arr[i+1].ptr,
529                    "intersection detected");
530     }
531     //----------------------------------------------------------------
532     if (FullLog) REPORT("realloc....");
533     for (int i=0; i<COUNT_ELEM; i++)
534     {
535         size_t count=arr[i].size*2;
536         void *tmpAddr=Trealloc(arr[i].ptr,count);
537         if (nullptr!=tmpAddr) {
538             arr[i].ptr = tmpAddr;
539             arr[i].size = count;
540         } else if (count==0) { // because realloc(..., 0) works as free
541             arr[i].ptr = nullptr;
542             arr[i].size = 0;
543         }
544     }
545     qsort(arr, COUNT_ELEM, sizeof(PtrSize), cmpAddrs);
546 
547     for (int i=0; i<COUNT_ELEM-1; i++)
548     {
549         if (nullptr!=arr[i].ptr && nullptr!=arr[i+1].ptr)
550             REQUIRE_MESSAGE((uintptr_t)arr[i].ptr+arr[i].size <= (uintptr_t)arr[i+1].ptr,
551                    "intersection detected");
552     }
553     for (int i=0; i<COUNT_ELEM; i++)
554     {
555         Tfree(arr[i].ptr);
556     }
557     //-------------------------------------------
558     if (FullLog) REPORT("calloc....");
559     for (int i=0; i<COUNT_ELEM; i++)
560     {
561         arr[i].size=rand()%MAX_SIZE;
562         arr[i].ptr=Tcalloc(arr[i].size,1);
563     }
564     qsort(arr, COUNT_ELEM, sizeof(PtrSize), cmpAddrs);
565 
566     for (int i=0; i<COUNT_ELEM-1; i++)
567     {
568         if (nullptr!=arr[i].ptr && nullptr!=arr[i+1].ptr)
569             REQUIRE_MESSAGE((uintptr_t)arr[i].ptr+arr[i].size <= (uintptr_t)arr[i+1].ptr,
570                    "intersection detected");
571     }
572     for (int i=0; i<COUNT_ELEM; i++)
573     {
574         Tfree(arr[i].ptr);
575     }
576     Tfree(arr);
577 }
578 
579 void CMemTest::Zerofilling()
580 {
581     TestStruct* TSMas;
582     size_t CountElement;
583     static std::atomic<int> CountErrors{0};
584     if (FullLog) REPORT("\nzeroings elements of array....");
585     //test struct
586     for (int i=0; i<COUNTEXPERIMENT; i++)
587     {
588         CountElement=rand()%MAX_SIZE;
589         TSMas=(TestStruct*)Tcalloc(CountElement,sizeof(TestStruct));
590         if (nullptr == TSMas)
591             continue;
592         for (size_t j=0; j<CountElement; j++)
593         {
594             if (NonZero(TSMas+j, sizeof(TestStruct)))
595             {
596                 CountErrors++;
597                 if (ShouldReportError()) REPORT("detect nonzero element at TestStruct\n");
598             }
599         }
600         Tfree(TSMas);
601     }
602     if (CountErrors) REPORT("%s\n",strError);
603     else if (FullLog) REPORT("%s\n",strOk);
604     if (CountErrors) error_occurred = true;
605 }
606 
607 #if !__APPLE__
608 
609 void myMemset(void *ptr, int c, size_t n)
610 {
611 #if  __unix__ &&  __i386__
612 // memset in Fedora 13 not always correctly sets memory to required values.
613     char *p = (char*)ptr;
614     for (size_t i=0; i<n; i++)
615         p[i] = c;
616 #else
617     memset(ptr, c, n);
618 #endif
619 }
620 
621 // This test requires more than TOTAL_MB_ALLOC MB of RAM.
622 #if __ANDROID__
623 // Android requires lower limit due to lack of virtual memory.
624 #define TOTAL_MB_ALLOC	200
625 #else
626 #define TOTAL_MB_ALLOC  800
627 #endif
628 void CMemTest::NULLReturn(UINT MinSize, UINT MaxSize, int total_threads)
629 {
630     const int MB_PER_THREAD = TOTAL_MB_ALLOC / total_threads;
631     // find size to guarantee getting nullptr for 1024 B allocations
632     const int MAXNUM_1024 = (MB_PER_THREAD + (MB_PER_THREAD>>2)) * 1024;
633 
634     std::vector<MemStruct> PointerList;
635     void *tmp;
636     static std::atomic<int> CountErrors{0};
637     int CountNULL, num_1024;
638     if (FullLog) REPORT("\nNULL return & check errno:\n");
639     UINT Size;
640     Limit limit_total(TOTAL_MB_ALLOC), no_limit(0);
641     void **buf_1024 = (void**)Tmalloc(MAXNUM_1024*sizeof(void*));
642 
643     REQUIRE(buf_1024);
644     /* We must have space for pointers when memory limit is hit.
645        Reserve enough for the worst case, taking into account race for
646        limited space between threads.
647     */
648     PointerList.reserve(TOTAL_MB_ALLOC*MByte/MinSize);
649 
650     /* There is a bug in the specific version of GLIBC (2.5-12) shipped
651        with RHEL5 that leads to erroneous working of the test
652        on Intel(R) 64 and Itanium(R) architecture when setrlimit-related part is enabled.
653        Switching to GLIBC 2.5-18 from RHEL5.1 resolved the issue.
654      */
655     if (perProcessLimits)
656         limitBarrier->wait(limit_total);
657     else
658         limitMem(MB_PER_THREAD);
659 
660     /* regression test against the bug in allocator when it dereference nullptr
661        while lack of memory
662     */
663     for (num_1024=0; num_1024<MAXNUM_1024; num_1024++) {
664         buf_1024[num_1024] = Tcalloc(1024, 1);
665         if (! buf_1024[num_1024]) {
666             ASSERT_ERRNO(errno == ENOMEM, nullptr);
667             break;
668         }
669     }
670     for (int i=0; i<num_1024; i++)
671         Tfree(buf_1024[i]);
672     Tfree(buf_1024);
673 
674     do {
675         Size=rand()%(MaxSize-MinSize)+MinSize;
676         tmp=Tmalloc(Size);
677         if (tmp != nullptr)
678         {
679             myMemset(tmp, 0, Size);
680             PointerList.push_back(MemStruct(tmp, Size));
681         }
682     } while(tmp != nullptr);
683     ASSERT_ERRNO(errno == ENOMEM, nullptr);
684     if (FullLog) REPORT("\n");
685 
686     // preparation complete, now running tests
687     // malloc
688     if (FullLog) REPORT("malloc....");
689     CountNULL = 0;
690     while (CountNULL==0)
691         for (int j=0; j<COUNT_TESTS; j++)
692         {
693             Size=rand()%(MaxSize-MinSize)+MinSize;
694             errno = ENOMEM+j+1;
695             tmp=Tmalloc(Size);
696             if (tmp == nullptr)
697             {
698                 CountNULL++;
699                 if ( CHECK_ERRNO(errno != ENOMEM) ) {
700                     CountErrors++;
701                     if (ShouldReportError()) REPORT("nullptr returned, error: errno (%d) != ENOMEM\n", errno);
702                 }
703             }
704             else
705             {
706                 // Technically, if malloc returns a non-null pointer, it is allowed to set errno anyway.
707                 // However, on most systems it does not set errno.
708                 bool known_issue = false;
709 #if __unix__ || __ANDROID__
710                 if( CHECK_ERRNO(errno==ENOMEM) ) known_issue = true;
711 #endif /* __unix__ */
712                 if ( CHECK_ERRNO(errno != ENOMEM+j+1) && !known_issue) {
713                     CountErrors++;
714                     if (ShouldReportError()) REPORT("error: errno changed to %d though valid pointer was returned\n", errno);
715                 }
716                 myMemset(tmp, 0, Size);
717                 PointerList.push_back(MemStruct(tmp, Size));
718             }
719         }
720     if (FullLog) REPORT("end malloc\n");
721     if (CountErrors) REPORT("%s\n",strError);
722     else if (FullLog) REPORT("%s\n",strOk);
723     if (CountErrors) error_occurred = true;
724 
725     //calloc
726     if (FullLog) REPORT("calloc....");
727     CountNULL = 0;
728     while (CountNULL==0)
729         for (int j=0; j<COUNT_TESTS; j++)
730         {
731             Size=rand()%(MaxSize-MinSize)+MinSize;
732             errno = ENOMEM+j+1;
733             tmp=Tcalloc(COUNT_ELEM_CALLOC,Size);
734             if (tmp == nullptr)
735             {
736                 CountNULL++;
737                 if ( CHECK_ERRNO(errno != ENOMEM) ){
738                     CountErrors++;
739                     if (ShouldReportError()) REPORT("nullptr returned, error: errno(%d) != ENOMEM\n", errno);
740                 }
741             }
742             else
743             {
744                 // Technically, if calloc returns a non-null pointer, it is allowed to set errno anyway.
745                 // However, on most systems it does not set errno.
746                 bool known_issue = false;
747 #if __unix__
748                 if( CHECK_ERRNO(errno==ENOMEM) ) known_issue = true;
749 #endif /* __unix__ */
750                 if ( CHECK_ERRNO(errno != ENOMEM+j+1) && !known_issue ) {
751                     CountErrors++;
752                     if (ShouldReportError()) REPORT("error: errno changed to %d though valid pointer was returned\n", errno);
753                 }
754                 PointerList.push_back(MemStruct(tmp, Size));
755             }
756         }
757     if (FullLog) REPORT("end calloc\n");
758     if (CountErrors) REPORT("%s\n",strError);
759     else if (FullLog) REPORT("%s\n",strOk);
760     if (CountErrors) error_occurred = true;
761     if (FullLog) REPORT("realloc....");
762     CountNULL = 0;
763     if (PointerList.size() > 0)
764         while (CountNULL==0)
765             for (size_t i=0; i<(size_t)COUNT_TESTS && i<PointerList.size(); i++)
766             {
767                 errno = 0;
768                 tmp=Trealloc(PointerList[i].Pointer,PointerList[i].Size*2);
769                 if (tmp != nullptr) // same or another place
770                 {
771                     bool known_issue = false;
772 #if __unix__
773                     if( errno==ENOMEM ) known_issue = true;
774 #endif /* __unix__ */
775                     if (errno != 0 && !known_issue) {
776                         CountErrors++;
777                         if (ShouldReportError()) REPORT("valid pointer returned, error: errno not kept\n");
778                     }
779                     // newly allocated area have to be zeroed
780                     myMemset((char*)tmp + PointerList[i].Size, 0, PointerList[i].Size);
781                     PointerList[i].Pointer = tmp;
782                     PointerList[i].Size *= 2;
783                 } else {
784                     CountNULL++;
785                     if ( CHECK_ERRNO(errno != ENOMEM) )
786                     {
787                         CountErrors++;
788                         if (ShouldReportError()) REPORT("nullptr returned, error: errno(%d) != ENOMEM\n", errno);
789                     }
790                     // check data integrity
791                     if (NonZero(PointerList[i].Pointer, PointerList[i].Size)) {
792                         CountErrors++;
793                         if (ShouldReportError()) REPORT("nullptr returned, error: data changed\n");
794                     }
795                 }
796             }
797     if (FullLog) REPORT("realloc end\n");
798     if (CountErrors) REPORT("%s\n",strError);
799     else if (FullLog) REPORT("%s\n",strOk);
800     if (CountErrors) error_occurred = true;
801     for (UINT i=0; i<PointerList.size(); i++)
802     {
803         Tfree(PointerList[i].Pointer);
804     }
805 
806     if (perProcessLimits)
807         limitBarrier->wait(no_limit);
808     else
809         limitMem(0);
810 }
811 #endif /* #if !__APPLE__ */
812 
813 void CMemTest::UniquePointer()
814 {
815     static std::atomic<int> CountErrors{0};
816     int **MasPointer = (int **)Tmalloc(sizeof(int*)*COUNT_ELEM);
817     size_t *MasCountElem = (size_t*)Tmalloc(sizeof(size_t)*COUNT_ELEM);
818     if (FullLog) REPORT("\nUnique pointer using 0\n");
819     REQUIRE((MasCountElem && MasPointer));
820     //
821     //-------------------------------------------------------
822     //malloc
823     for (int i=0; i<COUNT_ELEM; i++)
824     {
825         MasCountElem[i]=rand()%MAX_SIZE;
826         MasPointer[i]=(int*)Tmalloc(MasCountElem[i]*sizeof(int));
827         if (nullptr == MasPointer[i])
828             MasCountElem[i]=0;
829         memset(MasPointer[i], 0, sizeof(int)*MasCountElem[i]);
830     }
831     if (FullLog) REPORT("malloc....");
832     for (UINT i=0; i<COUNT_ELEM-1; i++)
833     {
834         if (size_t badOff = NonZero(MasPointer[i], sizeof(int)*MasCountElem[i])) {
835             CountErrors++;
836             if (ShouldReportError())
837                 REPORT("error, detect non-zero at %p\n", (char*)MasPointer[i]+badOff-1);
838         }
839         memset(MasPointer[i], 1, sizeof(int)*MasCountElem[i]);
840     }
841     if (CountErrors) REPORT("%s\n",strError);
842     else if (FullLog) REPORT("%s\n",strOk);
843     if (CountErrors) error_occurred = true;
844     //----------------------------------------------------------
845     //calloc
846     for (int i=0; i<COUNT_ELEM; i++)
847         Tfree(MasPointer[i]);
848     for (long i=0; i<COUNT_ELEM; i++)
849     {
850         MasPointer[i]=(int*)Tcalloc(MasCountElem[i]*sizeof(int),2);
851         if (nullptr == MasPointer[i])
852             MasCountElem[i]=0;
853     }
854     if (FullLog) REPORT("calloc....");
855     for (int i=0; i<COUNT_ELEM-1; i++)
856     {
857         if (size_t badOff = NonZero(MasPointer[i], sizeof(int)*MasCountElem[i])) {
858             CountErrors++;
859             if (ShouldReportError())
860                 REPORT("error, detect non-zero at %p\n", (char*)MasPointer[i]+badOff-1);
861         }
862         memset(MasPointer[i], 1, sizeof(int)*MasCountElem[i]);
863     }
864     if (CountErrors) REPORT("%s\n",strError);
865     else if (FullLog) REPORT("%s\n",strOk);
866     if (CountErrors) error_occurred = true;
867     //---------------------------------------------------------
868     //realloc
869     for (int i=0; i<COUNT_ELEM; i++)
870     {
871         MasCountElem[i]*=2;
872         *(MasPointer+i)=
873             (int*)Trealloc(*(MasPointer+i),MasCountElem[i]*sizeof(int));
874         if (nullptr == MasPointer[i])
875             MasCountElem[i]=0;
876         memset(MasPointer[i], 0, sizeof(int)*MasCountElem[i]);
877     }
878     if (FullLog) REPORT("realloc....");
879     for (int i=0; i<COUNT_ELEM-1; i++)
880     {
881         if (NonZero(MasPointer[i], sizeof(int)*MasCountElem[i]))
882             CountErrors++;
883         memset(MasPointer[i], 1, sizeof(int)*MasCountElem[i]);
884     }
885     if (CountErrors) REPORT("%s\n",strError);
886     else if (FullLog) REPORT("%s\n",strOk);
887     if (CountErrors) error_occurred = true;
888     for (int i=0; i<COUNT_ELEM; i++)
889         Tfree(MasPointer[i]);
890     Tfree(MasCountElem);
891     Tfree(MasPointer);
892 }
893 
894 bool CMemTest::ShouldReportError()
895 {
896     if (FullLog)
897         return true;
898     else
899         if (firstTime) {
900             firstTime = false;
901             return true;
902         } else
903             return false;
904 }
905 
906 void CMemTest::Free_NULL()
907 {
908     static std::atomic<int> CountErrors{0};
909     if (FullLog) REPORT("\ncall free with parameter nullptr....");
910     errno = 0;
911     for (int i=0; i<COUNTEXPERIMENT; i++)
912     {
913         Tfree(nullptr);
914         if (CHECK_ERRNO(errno))
915         {
916             CountErrors++;
917             if (ShouldReportError()) REPORT("error is found by a call free with parameter nullptr\n");
918         }
919     }
920     if (CountErrors) REPORT("%s\n",strError);
921     else if (FullLog) REPORT("%s\n",strOk);
922     if (CountErrors) error_occurred = true;
923 }
924 
925 void CMemTest::TestAlignedParameters()
926 {
927     void *memptr;
928     int ret;
929 
930     if (Rposix_memalign) {
931         // alignment isn't power of 2
932         for (int bad_align=3; bad_align<16; bad_align++)
933             if (bad_align&(bad_align-1)) {
934                 ret = Tposix_memalign(nullptr, bad_align, 100);
935                 REQUIRE(EINVAL==ret);
936             }
937 
938         memptr = &ret;
939         ret = Tposix_memalign(&memptr, 5*sizeof(void*), 100);
940         REQUIRE_MESSAGE(memptr == &ret,
941                "memptr should not be changed after unsuccessful call");
942         REQUIRE(EINVAL==ret);
943 
944         // alignment is power of 2, but not a multiple of sizeof(void *),
945         // we expect that sizeof(void*) > 2
946         ret = Tposix_memalign(nullptr, 2, 100);
947         REQUIRE(EINVAL==ret);
948     }
949     if (Raligned_malloc) {
950         // alignment isn't power of 2
951         for (int bad_align=3; bad_align<16; bad_align++)
952             if (bad_align&(bad_align-1)) {
953                 memptr = Taligned_malloc(100, bad_align);
954                 REQUIRE(memptr == nullptr);
955                 ASSERT_ERRNO(EINVAL==errno, nullptr);
956             }
957 
958         // size is zero
959         memptr = Taligned_malloc(0, 16);
960         REQUIRE_MESSAGE(memptr == nullptr, "size is zero, so must return nullptr");
961         ASSERT_ERRNO(EINVAL==errno, nullptr);
962     }
963     if (Taligned_free) {
964         // nullptr pointer is OK to free
965         errno = 0;
966         Taligned_free(nullptr);
967         /* As there is no return value for free, strictly speaking we can't
968            check errno here. But checked implementations obey the assertion.
969         */
970         ASSERT_ERRNO(0==errno, nullptr);
971     }
972     if (Raligned_realloc) {
973         for (int i=1; i<20; i++) {
974             // checks that calls work correctly in presence of non-zero errno
975             errno = i;
976             void *ptr = Taligned_malloc(i*10, 128);
977             REQUIRE(ptr != nullptr);
978             ASSERT_ERRNO(0!=errno, nullptr);
979             // if size is zero and pointer is not nullptr, works like free
980             memptr = Taligned_realloc(ptr, 0, 64);
981             REQUIRE(memptr == nullptr);
982             ASSERT_ERRNO(0!=errno, nullptr);
983         }
984         // alignment isn't power of 2
985         for (int bad_align=3; bad_align<16; bad_align++)
986             if (bad_align&(bad_align-1)) {
987                 void *ptr = &bad_align;
988                 memptr = Taligned_realloc(&ptr, 100, bad_align);
989                 REQUIRE(memptr == nullptr);
990                 REQUIRE(&bad_align==ptr);
991                 ASSERT_ERRNO(EINVAL==errno, nullptr);
992             }
993     }
994 }
995 
996 void CMemTest::RunAllTests(int total_threads)
997 {
998     Zerofilling();
999     Free_NULL();
1000     InvariantDataRealloc(/*aligned=*/false, 8*MByte, /*checkData=*/true);
1001     if (Raligned_realloc)
1002         InvariantDataRealloc(/*aligned=*/true, 8*MByte, /*checkData=*/true);
1003     TestAlignedParameters();
1004     UniquePointer();
1005     AddrArifm();
1006 #if __APPLE__ || __TBB_USE_THREAD_SANITIZER
1007     REPORT("Known issue: some tests are skipped on macOS\n");
1008 #else
1009     // TODO: enable
1010     NULLReturn(1*MByte,100*MByte,total_threads);
1011 #endif
1012     if (FullLog) REPORT("Tests for %d threads ended\n", total_threads);
1013 }
1014 
1015 // TODO: fix the tests to support UWP mode
1016 #if !__TBB_WIN8UI_SUPPORT
1017 
1018 TEST_CASE("MAIN TEST") {
1019     Rmalloc=scalable_malloc;
1020     Rrealloc=scalable_realloc;
1021     Rcalloc=scalable_calloc;
1022     Tfree=scalable_free;
1023     Rposix_memalign=scalable_posix_memalign;
1024     Raligned_malloc=scalable_aligned_malloc;
1025     Raligned_realloc=scalable_aligned_realloc;
1026     Taligned_free=scalable_aligned_free;
1027 
1028     // Check if we were called to test standard behavior
1029     // TODO: enable this mode
1030     // setSystemAllocs();
1031 #if __unix__
1032     /* According to man pthreads
1033        "NPTL threads do not share resource limits (fixed in kernel 2.6.10)".
1034        Use per-threads limits for affected systems.
1035      */
1036     if ( utils::LinuxKernelVersion() < 2*1000000 + 6*1000 + 10)
1037         perProcessLimits = false;
1038 #endif
1039     //-------------------------------------
1040 #if __APPLE__ || __TBB_USE_SANITIZERS
1041     /* Skip due to lack of memory limit enforcing under macOS. */
1042     //Skip this test under ASAN , as OOM condition breaks the ASAN as well
1043 #else
1044     limitMem(200);
1045     ReallocParam();
1046     limitMem(0);
1047 #endif
1048 
1049 //for linux and dynamic runtime errno is used to check allocator functions
1050 //check if library compiled with /MD(d) and we can use errno
1051 #if _MSC_VER
1052 #if defined(_MT) && defined(_DLL) //check errno if test itself compiled with /MD(d) only
1053     char*  version_info_block = nullptr;
1054     int version_info_block_size;
1055     LPVOID comments_block = nullptr;
1056     UINT comments_block_size;
1057 #ifdef _DEBUG
1058 #define __TBBMALLOCDLL "tbbmalloc_debug.dll"
1059 #else  //_DEBUG
1060 #define __TBBMALLOCDLL "tbbmalloc.dll"
1061 #endif //_DEBUG
1062     version_info_block_size = GetFileVersionInfoSize( __TBBMALLOCDLL, (LPDWORD)&version_info_block_size );
1063     if( version_info_block_size
1064         && ((version_info_block = (char*)malloc(version_info_block_size)) != nullptr)
1065         && GetFileVersionInfo(  __TBBMALLOCDLL, NULL, version_info_block_size, version_info_block )
1066         && VerQueryValue( version_info_block, "\\StringFileInfo\\000004b0\\Comments", &comments_block, &comments_block_size )
1067         && strstr( (char*)comments_block, "/MD" )
1068         ){
1069             __tbb_test_errno = true;
1070      }
1071      if( version_info_block ) free( version_info_block );
1072 #endif // defined(_MT) && defined(_DLL)
1073 #else  // _MSC_VER
1074     __tbb_test_errno = true;
1075 #endif // _MSC_VER
1076 
1077     CheckArgumentsOverflow();
1078     CheckReallocLeak();
1079     for( int p=MaxThread; p>=MinThread; --p ) {
1080         for (int limit=0; limit<2; limit++) {
1081             int ret = scalable_allocation_mode(TBBMALLOC_SET_SOFT_HEAP_LIMIT, 16*1024*limit);
1082             REQUIRE(ret==TBBMALLOC_OK);
1083             utils::SpinBarrier *barrier = new utils::SpinBarrier(p);
1084             utils::NativeParallelFor( p, RoundRobin(p, barrier, Verbose) );
1085             delete barrier;
1086         }
1087     }
1088     int ret = scalable_allocation_mode(TBBMALLOC_SET_SOFT_HEAP_LIMIT, 0);
1089     REQUIRE(ret==TBBMALLOC_OK);
1090     REQUIRE(!error_occurred);
1091 }
1092 
1093 #endif /* __TBB_WIN8UI_SUPPORT	 */
1094