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 | /* 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 | #include <stdlib.h> |
26 | #include "harness_allocator_overload.h" |
27 | |
28 | // __TBB_malloc_safer_msize() returns 0 for unknown objects, |
29 | // thus we can detect ownership |
30 | #if _USRDLL |
31 | #if _WIN32||_WIN64 |
32 | extern __declspec(dllexport) |
33 | #endif |
34 | bool dll_isMallocOverloaded() |
35 | #else |
36 | bool exe_isMallocOverloaded() |
37 | #endif |
38 | { |
39 | const size_t reqSz = 8; |
40 | void *o = malloc(reqSz); |
41 | bool ret = __TBB_malloc_safer_msize(o, NULL) >= reqSz; |
42 | free(o); |
43 | return ret; |
44 | } |
45 | |
46 | #if _USRDLL |
47 | |
48 | #if MALLOC_UNIXLIKE_OVERLOAD_ENABLED || MALLOC_ZONE_OVERLOAD_ENABLED |
49 | |
50 | #define HARNESS_CUSTOM_MAIN 1 |
51 | #include "harness.h" |
52 | |
53 | #include <dlfcn.h> |
54 | #if __APPLE__ |
55 | #include <malloc/malloc.h> |
56 | #define malloc_usable_size(p) malloc_size(p) |
57 | #else |
58 | #include <malloc.h> |
59 | #endif |
60 | #include <signal.h> |
61 | |
62 | #if __linux__ && !__ANDROID__ |
63 | extern "C" { |
64 | void __libc_free(void *ptr); |
65 | void *__libc_realloc(void *ptr, size_t size); |
66 | |
67 | // check that such kind of free/realloc overload works correctly |
68 | void free(void *ptr) |
69 | { |
70 | __libc_free(ptr); |
71 | } |
72 | |
73 | void *realloc(void *ptr, size_t size) |
74 | { |
75 | return __libc_realloc(ptr, size); |
76 | } |
77 | } // extern "C" |
78 | #endif // __linux__ && !__ANDROID__ |
79 | |
80 | #endif // MALLOC_UNIXLIKE_OVERLOAD_ENABLED || MALLOC_ZONE_OVERLOAD_ENABLED |
81 | |
82 | // Even when the test is skipped, dll source must not be empty to generate .lib to link with. |
83 | |
84 | #ifndef _PGO_INSTRUMENT |
85 | void dummyFunction() {} |
86 | |
87 | // TODO: enable the check under Android |
88 | #if (MALLOC_UNIXLIKE_OVERLOAD_ENABLED || MALLOC_ZONE_OVERLOAD_ENABLED) && !__ANDROID__ |
89 | typedef void *(malloc_type)(size_t); |
90 | |
91 | static void SigSegv(int) |
92 | { |
93 | REPORT("Known issue: SIGSEGV during work with memory allocated by replaced allocator.\n" |
94 | "skip\n" ); |
95 | exit(0); |
96 | } |
97 | |
98 | // TODO: Using of SIGSEGV can be eliminated via parsing /proc/self/maps |
99 | // and series of system malloc calls. |
100 | void TestReplacedAllocFunc() |
101 | { |
102 | struct sigaction sa, sa_default; |
103 | malloc_type *orig_malloc = (malloc_type*)dlsym(RTLD_NEXT, "malloc" ); |
104 | void *p = (*orig_malloc)(16); |
105 | |
106 | // protect potentially unsafe actions |
107 | sigemptyset(&sa.sa_mask); |
108 | sa.sa_flags = 0; |
109 | sa.sa_handler = SigSegv; |
110 | if (sigaction(SIGSEGV, &sa, &sa_default)) |
111 | ASSERT(0, "sigaction failed" ); |
112 | |
113 | ASSERT(malloc_usable_size(p) >= 16, NULL); |
114 | free(p); |
115 | // no more unsafe actions, restore SIGSEGV |
116 | if (sigaction(SIGSEGV, &sa_default, NULL)) |
117 | ASSERT(0, "sigaction failed" ); |
118 | } |
119 | #else |
120 | void TestReplacedAllocFunc() { } |
121 | #endif |
122 | |
123 | class Foo { |
124 | public: |
125 | Foo() { |
126 | // add a lot of exit handlers to cause memory allocation |
127 | for (int i=0; i<1024; i++) |
128 | atexit(dummyFunction); |
129 | TestReplacedAllocFunc(); |
130 | } |
131 | }; |
132 | |
133 | static Foo f; |
134 | #endif |
135 | |
136 | #else // _USRDLL |
137 | #include "harness.h" |
138 | |
139 | #if _WIN32||_WIN64 |
140 | #include "tbb/tbbmalloc_proxy.h" |
141 | |
142 | extern __declspec(dllimport) |
143 | #endif |
144 | bool dll_isMallocOverloaded(); |
145 | |
146 | int TestMain () { |
147 | #ifdef _PGO_INSTRUMENT |
148 | REPORT("Known issue: test_malloc_atexit hangs if compiled with -prof-genx\n" ); |
149 | return Harness::Skipped; |
150 | #else |
151 | ASSERT( dll_isMallocOverloaded(), "malloc was not replaced" ); |
152 | ASSERT( exe_isMallocOverloaded(), "malloc was not replaced" ); |
153 | return Harness::Done; |
154 | #endif |
155 | } |
156 | |
157 | #endif // _USRDLL |
158 | |