1 /*
2     Copyright (c) 2005-2021 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 /* Regression test against a bug in TBB allocator manifested when
18    dynamic library calls atexit() or registers dtors of static objects.
19    If the allocator is not initialized yet, we can get deadlock,
20    because allocator library has static object dtors as well, they
21    registered during allocator initialization, and atexit() is protected
22    by non-recursive mutex in some versions of GLIBC.
23  */
24 
25 #define __TBB_NO_IMPLICIT_LINKAGE 1
26 
27 #include "common/allocator_overload.h"
28 #include "common/utils_assert.h"
29 #include <stdlib.h>
30 
31 // __TBB_malloc_safer_msize() returns 0 for unknown objects,
32 // thus we can detect ownership
33 #if _USRDLL
34 #if _WIN32||_WIN64
35 extern __declspec(dllexport)
36 #endif
37 bool dll_isMallocOverloaded()
38 #else
39 bool exe_isMallocOverloaded()
40 #endif
41 {
42     const size_t reqSz = 8;
43     void *o = malloc(reqSz);
44     bool ret = __TBB_malloc_safer_msize(o, NULL) >= reqSz;
45     free(o);
46     return ret;
47 }
48 
49 #if _USRDLL
50 #include "common/utils_report.h"
51 
52 #if MALLOC_UNIXLIKE_OVERLOAD_ENABLED || MALLOC_ZONE_OVERLOAD_ENABLED
53 
54 #include <dlfcn.h>
55 #if __APPLE__
56 #include <malloc/malloc.h>
57 #define malloc_usable_size(p) malloc_size(p)
58 #else
59 #include <malloc.h>
60 #endif
61 #include <signal.h>
62 
63 #if __linux__ && !__ANDROID__
64 extern "C" {
65 void __libc_free(void *ptr);
66 void *__libc_realloc(void *ptr, size_t size);
67 
68 // check that such kind of free/realloc overload works correctly
69 void free(void *ptr)
70 {
71     __libc_free(ptr);
72 }
73 
74 void *realloc(void *ptr, size_t size)
75 {
76     return __libc_realloc(ptr, size);
77 }
78 } // extern "C"
79 #endif // __linux__ && !__ANDROID__
80 
81 #endif // MALLOC_UNIXLIKE_OVERLOAD_ENABLED || MALLOC_ZONE_OVERLOAD_ENABLED
82 
83 // Even when the test is skipped, dll source must not be empty to generate .lib to link with.
84 
85 #ifndef _PGO_INSTRUMENT
86 void dummyFunction() {}
87 
88 // TODO: enable the check under Android
89 #if (MALLOC_UNIXLIKE_OVERLOAD_ENABLED || MALLOC_ZONE_OVERLOAD_ENABLED) && !__ANDROID__
90 typedef void *(malloc_type)(size_t);
91 
92 static void SigSegv(int)
93 {
94     REPORT("Known issue: SIGSEGV during work with memory allocated by replaced allocator.\n"
95            "skip\n");
96     exit(0);
97 }
98 
99 // TODO: Using of SIGSEGV can be eliminated via parsing /proc/self/maps
100 // and series of system malloc calls.
101 void TestReplacedAllocFunc()
102 {
103     struct sigaction sa, sa_default;
104     malloc_type *orig_malloc = (malloc_type*)dlsym(RTLD_NEXT, "malloc");
105     void *p = (*orig_malloc)(16);
106 
107     // protect potentially unsafe actions
108     sigemptyset(&sa.sa_mask);
109     sa.sa_flags = 0;
110     sa.sa_handler = SigSegv;
111     if (sigaction(SIGSEGV, &sa, &sa_default))
112         ASSERT(0, "sigaction failed");
113 
114     ASSERT(malloc_usable_size(p) >= 16, NULL);
115     free(p);
116     // no more unsafe actions, restore SIGSEGV
117     if (sigaction(SIGSEGV, &sa_default, NULL))
118         ASSERT(0, "sigaction failed");
119 }
120 #else
121 void TestReplacedAllocFunc() { }
122 #endif
123 
124 class Foo {
125 public:
126     Foo() {
127         // add a lot of exit handlers to cause memory allocation
128         for (int i=0; i<1024; i++)
129             atexit(dummyFunction);
130         TestReplacedAllocFunc();
131     }
132 };
133 
134 static Foo f;
135 #endif
136 
137 int main() {}
138 
139 #else // _USRDLL
140 #include "common/test.h"
141 
142 #if _WIN32||_WIN64
143 #include "tbb/tbbmalloc_proxy.h"
144 
145 extern __declspec(dllimport)
146 #endif
147 bool dll_isMallocOverloaded();
148 
149 #ifdef _PGO_INSTRUMENT
150 //! \brief \ref error_guessing
151 TEST_CASE("Known issue: test_malloc_atexit hangs if compiled with -prof-genx\n" * doctest::skip(true)) {}
152 #else
153 // Check common/allocator_overload.h for skip cases
154 #if !HARNESS_SKIP_TEST
155 //! \brief \ref error_guessing
156 TEST_CASE("test malloc atexit") {
157     REQUIRE_MESSAGE( dll_isMallocOverloaded(), "malloc was not replaced" );
158     REQUIRE_MESSAGE( exe_isMallocOverloaded(), "malloc was not replaced" );
159 }
160 #endif // HARNESS_SKIP_TEST
161 
162 #endif // _PGO_INSTRUMENT
163 
164 #endif // _USRDLL
165