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 /* 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
dll_isMallocOverloaded()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, nullptr) >= 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 __unix__ && !__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
free(void * ptr)69 void free(void *ptr)
70 {
71 __libc_free(ptr);
72 }
73
realloc(void * ptr,size_t size)74 void *realloc(void *ptr, size_t size)
75 {
76 return __libc_realloc(ptr, size);
77 }
78 } // extern "C"
79 #endif // __unix__ && !__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 #if !defined(_PGO_INSTRUMENT) && !__TBB_USE_ADDRESS_SANITIZER
dummyFunction()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
SigSegv(int)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.
TestReplacedAllocFunc()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, nullptr);
115 free(p);
116 // no more unsafe actions, restore SIGSEGV
117 if (sigaction(SIGSEGV, &sa_default, nullptr))
118 ASSERT(0, "sigaction failed");
119 }
120 #else
TestReplacedAllocFunc()121 void TestReplacedAllocFunc() { }
122 #endif
123
124 class Foo {
125 public:
Foo()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 // !defined(_PGO_INSTRUMENT) && !__TBB_USE_ADDRESS_SANITIZER
136
main()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
skip(true)151 TEST_CASE("Known issue: test_malloc_atexit hangs if compiled with -prof-genx\n" * doctest::skip(true)) {}
152 #elif __TBB_USE_ADDRESS_SANITIZER
153 //! \brief \ref error_guessing
skip(true)154 TEST_CASE("Known issue: test_malloc_atexit is not applicable under ASAN\n" * doctest::skip(true)) {}
155 #else
156 // Check common/allocator_overload.h for skip cases
157 #if !HARNESS_SKIP_TEST
158 //! \brief \ref error_guessing
159 TEST_CASE("test malloc atexit") {
160 REQUIRE_MESSAGE( dll_isMallocOverloaded(), "malloc was not replaced" );
161 REQUIRE_MESSAGE( exe_isMallocOverloaded(), "malloc was not replaced" );
162 }
163 #endif // HARNESS_SKIP_TEST
164
165 #endif // _PGO_INSTRUMENT
166
167 #endif // _USRDLL
168