1 //===-- tsan_fd.cpp -------------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file is a part of ThreadSanitizer (TSan), a race detector.
11 //===----------------------------------------------------------------------===//
15 #include <sanitizer_common/sanitizer_atomic.h>
19 const int kTableSizeL1
= 1024;
20 const int kTableSizeL2
= 1024;
21 const int kTableSize
= kTableSizeL1
* kTableSizeL2
;
30 StackID creation_stack
;
34 atomic_uintptr_t tab
[kTableSizeL1
];
35 // Addresses used for synchronization.
42 static FdContext fdctx
;
44 static bool bogusfd(int fd
) {
45 // Apparently a bogus fd value.
46 return fd
< 0 || fd
>= kTableSize
;
49 static FdSync
*allocsync(ThreadState
*thr
, uptr pc
) {
50 FdSync
*s
= (FdSync
*)user_alloc_internal(thr
, pc
, sizeof(FdSync
),
51 kDefaultAlignment
, false);
52 atomic_store(&s
->rc
, 1, memory_order_relaxed
);
56 static FdSync
*ref(FdSync
*s
) {
57 if (s
&& atomic_load(&s
->rc
, memory_order_relaxed
) != (u64
)-1)
58 atomic_fetch_add(&s
->rc
, 1, memory_order_relaxed
);
62 static void unref(ThreadState
*thr
, uptr pc
, FdSync
*s
) {
63 if (s
&& atomic_load(&s
->rc
, memory_order_relaxed
) != (u64
)-1) {
64 if (atomic_fetch_sub(&s
->rc
, 1, memory_order_acq_rel
) == 1) {
65 CHECK_NE(s
, &fdctx
.globsync
);
66 CHECK_NE(s
, &fdctx
.filesync
);
67 CHECK_NE(s
, &fdctx
.socksync
);
68 user_free(thr
, pc
, s
, false);
73 static FdDesc
*fddesc(ThreadState
*thr
, uptr pc
, int fd
) {
75 CHECK_LT(fd
, kTableSize
);
76 atomic_uintptr_t
*pl1
= &fdctx
.tab
[fd
/ kTableSizeL2
];
77 uptr l1
= atomic_load(pl1
, memory_order_consume
);
79 uptr size
= kTableSizeL2
* sizeof(FdDesc
);
80 // We need this to reside in user memory to properly catch races on it.
81 void *p
= user_alloc_internal(thr
, pc
, size
, kDefaultAlignment
, false);
82 internal_memset(p
, 0, size
);
83 MemoryResetRange(thr
, (uptr
)&fddesc
, (uptr
)p
, size
);
84 if (atomic_compare_exchange_strong(pl1
, &l1
, (uptr
)p
, memory_order_acq_rel
))
87 user_free(thr
, pc
, p
, false);
89 FdDesc
*fds
= reinterpret_cast<FdDesc
*>(l1
);
90 return &fds
[fd
% kTableSizeL2
];
93 // pd must be already ref'ed.
94 static void init(ThreadState
*thr
, uptr pc
, int fd
, FdSync
*s
,
96 FdDesc
*d
= fddesc(thr
, pc
, fd
);
97 // As a matter of fact, we don't intercept all close calls.
98 // See e.g. libc __res_iclose().
100 unref(thr
, pc
, d
->sync
);
103 if (flags()->io_sync
== 0) {
105 } else if (flags()->io_sync
== 1) {
107 } else if (flags()->io_sync
== 2) {
109 d
->sync
= &fdctx
.globsync
;
111 d
->creation_tid
= thr
->tid
;
112 d
->creation_stack
= CurrentStackId(thr
, pc
);
114 // To catch races between fd usage and open.
115 MemoryRangeImitateWrite(thr
, pc
, (uptr
)d
, 8);
117 // See the dup-related comment in FdClose.
118 MemoryAccess(thr
, pc
, (uptr
)d
, 8, kAccessRead
);
123 atomic_store(&fdctx
.globsync
.rc
, (u64
)-1, memory_order_relaxed
);
124 atomic_store(&fdctx
.filesync
.rc
, (u64
)-1, memory_order_relaxed
);
125 atomic_store(&fdctx
.socksync
.rc
, (u64
)-1, memory_order_relaxed
);
128 void FdOnFork(ThreadState
*thr
, uptr pc
) {
129 // On fork() we need to reset all fd's, because the child is going
130 // close all them, and that will cause races between previous read/write
132 for (int l1
= 0; l1
< kTableSizeL1
; l1
++) {
133 FdDesc
*tab
= (FdDesc
*)atomic_load(&fdctx
.tab
[l1
], memory_order_relaxed
);
136 for (int l2
= 0; l2
< kTableSizeL2
; l2
++) {
137 FdDesc
*d
= &tab
[l2
];
138 MemoryResetRange(thr
, pc
, (uptr
)d
, 8);
143 bool FdLocation(uptr addr
, int *fd
, Tid
*tid
, StackID
*stack
) {
144 for (int l1
= 0; l1
< kTableSizeL1
; l1
++) {
145 FdDesc
*tab
= (FdDesc
*)atomic_load(&fdctx
.tab
[l1
], memory_order_relaxed
);
148 if (addr
>= (uptr
)tab
&& addr
< (uptr
)(tab
+ kTableSizeL2
)) {
149 int l2
= (addr
- (uptr
)tab
) / sizeof(FdDesc
);
150 FdDesc
*d
= &tab
[l2
];
151 *fd
= l1
* kTableSizeL1
+ l2
;
152 *tid
= d
->creation_tid
;
153 *stack
= d
->creation_stack
;
160 void FdAcquire(ThreadState
*thr
, uptr pc
, int fd
) {
163 FdDesc
*d
= fddesc(thr
, pc
, fd
);
165 DPrintf("#%d: FdAcquire(%d) -> %p\n", thr
->tid
, fd
, s
);
166 MemoryAccess(thr
, pc
, (uptr
)d
, 8, kAccessRead
);
168 Acquire(thr
, pc
, (uptr
)s
);
171 void FdRelease(ThreadState
*thr
, uptr pc
, int fd
) {
174 FdDesc
*d
= fddesc(thr
, pc
, fd
);
176 DPrintf("#%d: FdRelease(%d) -> %p\n", thr
->tid
, fd
, s
);
177 MemoryAccess(thr
, pc
, (uptr
)d
, 8, kAccessRead
);
179 Release(thr
, pc
, (uptr
)s
);
182 void FdAccess(ThreadState
*thr
, uptr pc
, int fd
) {
183 DPrintf("#%d: FdAccess(%d)\n", thr
->tid
, fd
);
186 FdDesc
*d
= fddesc(thr
, pc
, fd
);
187 MemoryAccess(thr
, pc
, (uptr
)d
, 8, kAccessRead
);
190 void FdClose(ThreadState
*thr
, uptr pc
, int fd
, bool write
) {
191 DPrintf("#%d: FdClose(%d)\n", thr
->tid
, fd
);
194 FdDesc
*d
= fddesc(thr
, pc
, fd
);
196 // To catch races between fd usage and close.
197 MemoryAccess(thr
, pc
, (uptr
)d
, 8, kAccessWrite
);
199 // This path is used only by dup2/dup3 calls.
200 // We do read instead of write because there is a number of legitimate
201 // cases where write would lead to false positives:
202 // 1. Some software dups a closed pipe in place of a socket before closing
203 // the socket (to prevent races actually).
204 // 2. Some daemons dup /dev/null in place of stdin/stdout.
205 // On the other hand we have not seen cases when write here catches real
207 MemoryAccess(thr
, pc
, (uptr
)d
, 8, kAccessRead
);
209 // We need to clear it, because if we do not intercept any call out there
210 // that creates fd, we will hit false postives.
211 MemoryResetRange(thr
, pc
, (uptr
)d
, 8);
212 unref(thr
, pc
, d
->sync
);
214 d
->creation_tid
= kInvalidTid
;
215 d
->creation_stack
= kInvalidStackID
;
218 void FdFileCreate(ThreadState
*thr
, uptr pc
, int fd
) {
219 DPrintf("#%d: FdFileCreate(%d)\n", thr
->tid
, fd
);
222 init(thr
, pc
, fd
, &fdctx
.filesync
);
225 void FdDup(ThreadState
*thr
, uptr pc
, int oldfd
, int newfd
, bool write
) {
226 DPrintf("#%d: FdDup(%d, %d)\n", thr
->tid
, oldfd
, newfd
);
227 if (bogusfd(oldfd
) || bogusfd(newfd
))
229 // Ignore the case when user dups not yet connected socket.
230 FdDesc
*od
= fddesc(thr
, pc
, oldfd
);
231 MemoryAccess(thr
, pc
, (uptr
)od
, 8, kAccessRead
);
232 FdClose(thr
, pc
, newfd
, write
);
233 init(thr
, pc
, newfd
, ref(od
->sync
), write
);
236 void FdPipeCreate(ThreadState
*thr
, uptr pc
, int rfd
, int wfd
) {
237 DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr
->tid
, rfd
, wfd
);
238 FdSync
*s
= allocsync(thr
, pc
);
239 init(thr
, pc
, rfd
, ref(s
));
240 init(thr
, pc
, wfd
, ref(s
));
244 void FdEventCreate(ThreadState
*thr
, uptr pc
, int fd
) {
245 DPrintf("#%d: FdEventCreate(%d)\n", thr
->tid
, fd
);
248 init(thr
, pc
, fd
, allocsync(thr
, pc
));
251 void FdSignalCreate(ThreadState
*thr
, uptr pc
, int fd
) {
252 DPrintf("#%d: FdSignalCreate(%d)\n", thr
->tid
, fd
);
255 init(thr
, pc
, fd
, 0);
258 void FdInotifyCreate(ThreadState
*thr
, uptr pc
, int fd
) {
259 DPrintf("#%d: FdInotifyCreate(%d)\n", thr
->tid
, fd
);
262 init(thr
, pc
, fd
, 0);
265 void FdPollCreate(ThreadState
*thr
, uptr pc
, int fd
) {
266 DPrintf("#%d: FdPollCreate(%d)\n", thr
->tid
, fd
);
269 init(thr
, pc
, fd
, allocsync(thr
, pc
));
272 void FdSocketCreate(ThreadState
*thr
, uptr pc
, int fd
) {
273 DPrintf("#%d: FdSocketCreate(%d)\n", thr
->tid
, fd
);
276 // It can be a UDP socket.
277 init(thr
, pc
, fd
, &fdctx
.socksync
);
280 void FdSocketAccept(ThreadState
*thr
, uptr pc
, int fd
, int newfd
) {
281 DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr
->tid
, fd
, newfd
);
284 // Synchronize connect->accept.
285 Acquire(thr
, pc
, (uptr
)&fdctx
.connectsync
);
286 init(thr
, pc
, newfd
, &fdctx
.socksync
);
289 void FdSocketConnecting(ThreadState
*thr
, uptr pc
, int fd
) {
290 DPrintf("#%d: FdSocketConnecting(%d)\n", thr
->tid
, fd
);
293 // Synchronize connect->accept.
294 Release(thr
, pc
, (uptr
)&fdctx
.connectsync
);
297 void FdSocketConnect(ThreadState
*thr
, uptr pc
, int fd
) {
298 DPrintf("#%d: FdSocketConnect(%d)\n", thr
->tid
, fd
);
301 init(thr
, pc
, fd
, &fdctx
.socksync
);
304 uptr
File2addr(const char *path
) {
310 uptr
Dir2addr(const char *path
) {
316 } // namespace __tsan