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