1 //===-- tsan_platform_linux.cc --------------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 // This file is a part of ThreadSanitizer (TSan), a race detector.
10 // Linux-specific code.
11 //===----------------------------------------------------------------------===//
14 #include "sanitizer_common/sanitizer_platform.h"
17 #include "sanitizer_common/sanitizer_common.h"
18 #include "sanitizer_common/sanitizer_libc.h"
19 #include "sanitizer_common/sanitizer_procmaps.h"
20 #include "sanitizer_common/sanitizer_stoptheworld.h"
21 #include "tsan_platform.h"
23 #include "tsan_flags.h"
33 #include <sys/prctl.h>
34 #include <sys/syscall.h>
35 #include <sys/socket.h>
37 #include <sys/types.h>
38 #include <sys/resource.h>
44 #define __need_res_state
56 extern "C" struct mallinfo
__libc_mallinfo();
60 const uptr kPageSize
= 4096;
63 ScopedInRtl::ScopedInRtl()
64 : thr_(cur_thread()) {
65 in_rtl_
= thr_
->in_rtl
;
70 ScopedInRtl::~ScopedInRtl() {
73 CHECK_EQ(in_rtl_
, thr_
->in_rtl
);
76 ScopedInRtl::ScopedInRtl() {
79 ScopedInRtl::~ScopedInRtl() {
83 void FillProfileCallback(uptr start
, uptr rss
, bool file
,
84 uptr
*mem
, uptr stats_size
) {
85 CHECK_EQ(7, stats_size
);
86 mem
[6] += rss
; // total
88 if (start
< 0x10) // shadow
90 else if (start
>= 0x20 && start
< 0x30) // compat modules
91 mem
[file
? 1 : 2] += rss
;
92 else if (start
>= 0x7e) // modules
93 mem
[file
? 1 : 2] += rss
;
94 else if (start
>= 0x60 && start
< 0x62) // traces
96 else if (start
>= 0x7d && start
< 0x7e) // heap
102 void WriteMemoryProfile(char *buf
, uptr buf_size
) {
104 __sanitizer::GetMemoryProfile(FillProfileCallback
, mem
, 7);
106 char *buf_end
= buf
+ buf_size
;
107 buf_pos
+= internal_snprintf(buf_pos
, buf_end
- buf_pos
,
108 "RSS %zd MB: shadow:%zd file:%zd mmap:%zd trace:%zd heap:%zd other:%zd\n",
109 mem
[6] >> 20, mem
[0] >> 20, mem
[1] >> 20, mem
[2] >> 20,
110 mem
[3] >> 20, mem
[4] >> 20, mem
[5] >> 20);
111 struct mallinfo mi
= __libc_mallinfo();
112 buf_pos
+= internal_snprintf(buf_pos
, buf_end
- buf_pos
,
113 "mallinfo: arena=%d mmap=%d fordblks=%d keepcost=%d\n",
114 mi
.arena
>> 20, mi
.hblkhd
>> 20, mi
.fordblks
>> 20, mi
.keepcost
>> 20);
119 __sanitizer::GetMemoryProfile(FillProfileCallback
, mem
, 7);
124 void FlushShadowMemoryCallback(
125 const SuspendedThreadsList
&suspended_threads_list
,
127 FlushUnneededShadowMemory(kLinuxShadowBeg
, kLinuxShadowEnd
- kLinuxShadowBeg
);
130 void FlushShadowMemory() {
131 StopTheWorld(FlushShadowMemoryCallback
, 0);
135 static void ProtectRange(uptr beg
, uptr end
) {
140 if (beg
!= (uptr
)Mprotect(beg
, end
- beg
)) {
141 Printf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg
, end
);
142 Printf("FATAL: Make sure you are not using unlimited stack\n");
149 // Mark shadow for .rodata sections with the special kShadowRodata marker.
150 // Accesses to .rodata can't race, so this saves time, memory and trace space.
151 static void MapRodata() {
152 // First create temp file.
153 const char *tmpdir
= GetEnv("TMPDIR");
155 tmpdir
= GetEnv("TEST_TMPDIR");
163 internal_snprintf(filename
, sizeof(filename
), "%s/tsan.rodata.%d",
164 tmpdir
, (int)internal_getpid());
165 uptr openrv
= internal_open(filename
, O_RDWR
| O_CREAT
| O_EXCL
, 0600);
166 if (internal_iserror(openrv
))
169 // Fill the file with kShadowRodata.
170 const uptr kMarkerSize
= 512 * 1024 / sizeof(u64
);
171 InternalScopedBuffer
<u64
> marker(kMarkerSize
);
172 for (u64
*p
= marker
.data(); p
< marker
.data() + kMarkerSize
; p
++)
174 internal_write(fd
, marker
.data(), marker
.size());
175 // Map the file into memory.
176 uptr page
= internal_mmap(0, kPageSize
, PROT_READ
| PROT_WRITE
,
177 MAP_PRIVATE
| MAP_ANONYMOUS
, fd
, 0);
178 if (internal_iserror(page
)) {
180 internal_unlink(filename
);
183 // Map the file into shadow of .rodata sections.
184 MemoryMappingLayout
proc_maps(/*cache_enabled*/true);
185 uptr start
, end
, offset
, prot
;
187 while (proc_maps
.Next(&start
, &end
, &offset
, name
, ARRAY_SIZE(name
), &prot
)) {
188 if (name
[0] != 0 && name
[0] != '['
189 && (prot
& MemoryMappingLayout::kProtectionRead
)
190 && (prot
& MemoryMappingLayout::kProtectionExecute
)
191 && !(prot
& MemoryMappingLayout::kProtectionWrite
)
192 && IsAppMem(start
)) {
193 // Assume it's .rodata
194 char *shadow_start
= (char*)MemToShadow(start
);
195 char *shadow_end
= (char*)MemToShadow(end
);
196 for (char *p
= shadow_start
; p
< shadow_end
; p
+= marker
.size()) {
197 internal_mmap(p
, Min
<uptr
>(marker
.size(), shadow_end
- p
),
198 PROT_READ
, MAP_PRIVATE
| MAP_FIXED
, fd
, 0);
203 internal_unlink(filename
);
206 void InitializeShadowMemory() {
207 uptr shadow
= (uptr
)MmapFixedNoReserve(kLinuxShadowBeg
,
208 kLinuxShadowEnd
- kLinuxShadowBeg
);
209 if (shadow
!= kLinuxShadowBeg
) {
210 Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
211 Printf("FATAL: Make sure to compile with -fPIE and "
212 "to link with -pie (%p, %p).\n", shadow
, kLinuxShadowBeg
);
215 const uptr kClosedLowBeg
= 0x200000;
216 const uptr kClosedLowEnd
= kLinuxShadowBeg
- 1;
217 const uptr kClosedMidBeg
= kLinuxShadowEnd
+ 1;
218 const uptr kClosedMidEnd
= min(kLinuxAppMemBeg
, kTraceMemBegin
);
219 ProtectRange(kClosedLowBeg
, kClosedLowEnd
);
220 ProtectRange(kClosedMidBeg
, kClosedMidEnd
);
221 DPrintf("kClosedLow %zx-%zx (%zuGB)\n",
222 kClosedLowBeg
, kClosedLowEnd
, (kClosedLowEnd
- kClosedLowBeg
) >> 30);
223 DPrintf("kLinuxShadow %zx-%zx (%zuGB)\n",
224 kLinuxShadowBeg
, kLinuxShadowEnd
,
225 (kLinuxShadowEnd
- kLinuxShadowBeg
) >> 30);
226 DPrintf("kClosedMid %zx-%zx (%zuGB)\n",
227 kClosedMidBeg
, kClosedMidEnd
, (kClosedMidEnd
- kClosedMidBeg
) >> 30);
228 DPrintf("kLinuxAppMem %zx-%zx (%zuGB)\n",
229 kLinuxAppMemBeg
, kLinuxAppMemEnd
,
230 (kLinuxAppMemEnd
- kLinuxAppMemBeg
) >> 30);
231 DPrintf("stack %zx\n", (uptr
)&shadow
);
237 static uptr g_data_start
;
238 static uptr g_data_end
;
241 static void CheckPIE() {
242 // Ensure that the binary is indeed compiled with -pie.
243 MemoryMappingLayout
proc_maps(true);
245 if (proc_maps
.Next(&start
, &end
,
246 /*offset*/0, /*filename*/0, /*filename_size*/0,
248 if ((u64
)start
< kLinuxAppMemBeg
) {
249 Printf("FATAL: ThreadSanitizer can not mmap the shadow memory ("
250 "something is mapped at 0x%zx < 0x%zx)\n",
251 start
, kLinuxAppMemBeg
);
252 Printf("FATAL: Make sure to compile with -fPIE"
253 " and to link with -pie.\n");
259 static void InitDataSeg() {
260 MemoryMappingLayout
proc_maps(true);
261 uptr start
, end
, offset
;
263 bool prev_is_data
= false;
264 while (proc_maps
.Next(&start
, &end
, &offset
, name
, ARRAY_SIZE(name
),
266 DPrintf("%p-%p %p %s\n", start
, end
, offset
, name
);
267 bool is_data
= offset
!= 0 && name
[0] != 0;
268 // BSS may get merged with [heap] in /proc/self/maps. This is not very
270 bool is_bss
= offset
== 0 &&
271 (name
[0] == 0 || internal_strcmp(name
, "[heap]") == 0) && prev_is_data
;
272 if (g_data_start
== 0 && is_data
)
273 g_data_start
= start
;
276 prev_is_data
= is_data
;
278 DPrintf("guessed data_start=%p data_end=%p\n", g_data_start
, g_data_end
);
279 CHECK_LT(g_data_start
, g_data_end
);
280 CHECK_GE((uptr
)&g_data_start
, g_data_start
);
281 CHECK_LT((uptr
)&g_data_start
, g_data_end
);
284 #endif // #ifndef TSAN_GO
286 static rlim_t
getlim(int res
) {
288 CHECK_EQ(0, getrlimit(res
, &rlim
));
289 return rlim
.rlim_cur
;
292 static void setlim(int res
, rlim_t lim
) {
293 // The following magic is to prevent clang from replacing it with memset.
294 volatile rlimit rlim
;
297 setrlimit(res
, (rlimit
*)&rlim
);
300 const char *InitializePlatform() {
302 if (sizeof(p
) == 8) {
303 // Disable core dumps, dumping of 16TB usually takes a bit long.
304 setlim(RLIMIT_CORE
, 0);
307 // Go maps shadow memory lazily and works fine with limited address space.
308 // Unlimited stack is not a problem as well, because the executable
309 // is not compiled with -pie.
312 // TSan doesn't play well with unlimited stack size (as stack
313 // overlaps with shadow memory). If we detect unlimited stack size,
314 // we re-exec the program with limited stack size as a best effort.
315 if (getlim(RLIMIT_STACK
) == (rlim_t
)-1) {
316 const uptr kMaxStackSize
= 32 * 1024 * 1024;
317 Report("WARNING: Program is run with unlimited stack size, which "
318 "wouldn't work with ThreadSanitizer.\n");
319 Report("Re-execing with stack size limited to %zd bytes.\n",
321 SetStackSizeLimitInBytes(kMaxStackSize
);
325 if (getlim(RLIMIT_AS
) != (rlim_t
)-1) {
326 Report("WARNING: Program is run with limited virtual address space,"
327 " which wouldn't work with ThreadSanitizer.\n");
328 Report("Re-execing with unlimited virtual address space.\n");
329 setlim(RLIMIT_AS
, -1);
341 return GetEnv(kTsanOptionsEnv
);
344 bool IsGlobalVar(uptr addr
) {
345 return g_data_start
&& addr
>= g_data_start
&& addr
< g_data_end
;
349 // Extract file descriptors passed to glibc internal __res_iclose function.
350 // This is required to properly "close" the fds, because we do not see internal
351 // closes within glibc. The code is a pure hack.
352 int ExtractResolvFDs(void *state
, int *fds
, int nfd
) {
354 __res_state
*statp
= (__res_state
*)state
;
355 for (int i
= 0; i
< MAXNS
&& cnt
< nfd
; i
++) {
356 if (statp
->_u
._ext
.nsaddrs
[i
] && statp
->_u
._ext
.nssocks
[i
] != -1)
357 fds
[cnt
++] = statp
->_u
._ext
.nssocks
[i
];
362 // Extract file descriptors passed via UNIX domain sockets.
363 // This is requried to properly handle "open" of these fds.
364 // see 'man recvmsg' and 'man 3 cmsg'.
365 int ExtractRecvmsgFDs(void *msgp
, int *fds
, int nfd
) {
367 msghdr
*msg
= (msghdr
*)msgp
;
368 struct cmsghdr
*cmsg
= CMSG_FIRSTHDR(msg
);
369 for (; cmsg
; cmsg
= CMSG_NXTHDR(msg
, cmsg
)) {
370 if (cmsg
->cmsg_level
!= SOL_SOCKET
|| cmsg
->cmsg_type
!= SCM_RIGHTS
)
372 int n
= (cmsg
->cmsg_len
- CMSG_LEN(0)) / sizeof(fds
[0]);
373 for (int i
= 0; i
< n
; i
++) {
374 fds
[res
++] = ((int*)CMSG_DATA(cmsg
))[i
];
384 } // namespace __tsan
386 #endif // SANITIZER_LINUX