1 //===-- tsan_fd.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 //===----------------------------------------------------------------------===//
14 #include <sanitizer_common/sanitizer_atomic.h>
18 const int kTableSizeL1
= 1024;
19 const int kTableSizeL2
= 1024;
20 const int kTableSize
= kTableSizeL1
* kTableSizeL2
;
33 atomic_uintptr_t tab
[kTableSizeL1
];
34 // Addresses used for synchronization.
41 static FdContext fdctx
;
43 static bool bogusfd(int fd
) {
44 // Apparently a bogus fd value.
45 return fd
< 0 || fd
>= kTableSize
;
48 static FdSync
*allocsync(ThreadState
*thr
, uptr pc
) {
49 FdSync
*s
= (FdSync
*)user_alloc_internal(thr
, pc
, sizeof(FdSync
),
50 kDefaultAlignment
, false);
51 atomic_store(&s
->rc
, 1, memory_order_relaxed
);
55 static FdSync
*ref(FdSync
*s
) {
56 if (s
&& atomic_load(&s
->rc
, memory_order_relaxed
) != (u64
)-1)
57 atomic_fetch_add(&s
->rc
, 1, memory_order_relaxed
);
61 static void unref(ThreadState
*thr
, uptr pc
, FdSync
*s
) {
62 if (s
&& atomic_load(&s
->rc
, memory_order_relaxed
) != (u64
)-1) {
63 if (atomic_fetch_sub(&s
->rc
, 1, memory_order_acq_rel
) == 1) {
64 CHECK_NE(s
, &fdctx
.globsync
);
65 CHECK_NE(s
, &fdctx
.filesync
);
66 CHECK_NE(s
, &fdctx
.socksync
);
67 user_free(thr
, pc
, s
, false);
72 static FdDesc
*fddesc(ThreadState
*thr
, uptr pc
, int fd
) {
74 CHECK_LT(fd
, kTableSize
);
75 atomic_uintptr_t
*pl1
= &fdctx
.tab
[fd
/ kTableSizeL2
];
76 uptr l1
= atomic_load(pl1
, memory_order_consume
);
78 uptr size
= kTableSizeL2
* sizeof(FdDesc
);
79 // We need this to reside in user memory to properly catch races on it.
80 void *p
= user_alloc_internal(thr
, pc
, size
, kDefaultAlignment
, false);
81 internal_memset(p
, 0, size
);
82 MemoryResetRange(thr
, (uptr
)&fddesc
, (uptr
)p
, size
);
83 if (atomic_compare_exchange_strong(pl1
, &l1
, (uptr
)p
, memory_order_acq_rel
))
86 user_free(thr
, pc
, p
, false);
88 return &((FdDesc
*)l1
)[fd
% kTableSizeL2
]; // NOLINT
91 // pd must be already ref'ed.
92 static void init(ThreadState
*thr
, uptr pc
, int fd
, FdSync
*s
,
94 FdDesc
*d
= fddesc(thr
, pc
, fd
);
95 // As a matter of fact, we don't intercept all close calls.
96 // See e.g. libc __res_iclose().
98 unref(thr
, pc
, d
->sync
);
101 if (flags()->io_sync
== 0) {
103 } else if (flags()->io_sync
== 1) {
105 } else if (flags()->io_sync
== 2) {
107 d
->sync
= &fdctx
.globsync
;
109 d
->creation_tid
= thr
->tid
;
110 d
->creation_stack
= CurrentStackId(thr
, pc
);
112 // To catch races between fd usage and open.
113 MemoryRangeImitateWrite(thr
, pc
, (uptr
)d
, 8);
115 // See the dup-related comment in FdClose.
116 MemoryRead(thr
, pc
, (uptr
)d
, kSizeLog8
);
121 atomic_store(&fdctx
.globsync
.rc
, (u64
)-1, memory_order_relaxed
);
122 atomic_store(&fdctx
.filesync
.rc
, (u64
)-1, memory_order_relaxed
);
123 atomic_store(&fdctx
.socksync
.rc
, (u64
)-1, memory_order_relaxed
);
126 void FdOnFork(ThreadState
*thr
, uptr pc
) {
127 // On fork() we need to reset all fd's, because the child is going
128 // close all them, and that will cause races between previous read/write
130 for (int l1
= 0; l1
< kTableSizeL1
; l1
++) {
131 FdDesc
*tab
= (FdDesc
*)atomic_load(&fdctx
.tab
[l1
], memory_order_relaxed
);
134 for (int l2
= 0; l2
< kTableSizeL2
; l2
++) {
135 FdDesc
*d
= &tab
[l2
];
136 MemoryResetRange(thr
, pc
, (uptr
)d
, 8);
141 bool FdLocation(uptr addr
, int *fd
, int *tid
, u32
*stack
) {
142 for (int l1
= 0; l1
< kTableSizeL1
; l1
++) {
143 FdDesc
*tab
= (FdDesc
*)atomic_load(&fdctx
.tab
[l1
], memory_order_relaxed
);
146 if (addr
>= (uptr
)tab
&& addr
< (uptr
)(tab
+ kTableSizeL2
)) {
147 int l2
= (addr
- (uptr
)tab
) / sizeof(FdDesc
);
148 FdDesc
*d
= &tab
[l2
];
149 *fd
= l1
* kTableSizeL1
+ l2
;
150 *tid
= d
->creation_tid
;
151 *stack
= d
->creation_stack
;
158 void FdAcquire(ThreadState
*thr
, uptr pc
, int fd
) {
161 FdDesc
*d
= fddesc(thr
, pc
, fd
);
163 DPrintf("#%d: FdAcquire(%d) -> %p\n", thr
->tid
, fd
, s
);
164 MemoryRead(thr
, pc
, (uptr
)d
, kSizeLog8
);
166 Acquire(thr
, pc
, (uptr
)s
);
169 void FdRelease(ThreadState
*thr
, uptr pc
, int fd
) {
172 FdDesc
*d
= fddesc(thr
, pc
, fd
);
174 DPrintf("#%d: FdRelease(%d) -> %p\n", thr
->tid
, fd
, s
);
175 MemoryRead(thr
, pc
, (uptr
)d
, kSizeLog8
);
177 Release(thr
, pc
, (uptr
)s
);
180 void FdAccess(ThreadState
*thr
, uptr pc
, int fd
) {
181 DPrintf("#%d: FdAccess(%d)\n", thr
->tid
, fd
);
184 FdDesc
*d
= fddesc(thr
, pc
, fd
);
185 MemoryRead(thr
, pc
, (uptr
)d
, kSizeLog8
);
188 void FdClose(ThreadState
*thr
, uptr pc
, int fd
, bool write
) {
189 DPrintf("#%d: FdClose(%d)\n", thr
->tid
, fd
);
192 FdDesc
*d
= fddesc(thr
, pc
, fd
);
194 // To catch races between fd usage and close.
195 MemoryWrite(thr
, pc
, (uptr
)d
, kSizeLog8
);
197 // This path is used only by dup2/dup3 calls.
198 // We do read instead of write because there is a number of legitimate
199 // cases where write would lead to false positives:
200 // 1. Some software dups a closed pipe in place of a socket before closing
201 // the socket (to prevent races actually).
202 // 2. Some daemons dup /dev/null in place of stdin/stdout.
203 // On the other hand we have not seen cases when write here catches real
205 MemoryRead(thr
, pc
, (uptr
)d
, kSizeLog8
);
207 // We need to clear it, because if we do not intercept any call out there
208 // that creates fd, we will hit false postives.
209 MemoryResetRange(thr
, pc
, (uptr
)d
, 8);
210 unref(thr
, pc
, d
->sync
);
213 d
->creation_stack
= 0;
216 void FdFileCreate(ThreadState
*thr
, uptr pc
, int fd
) {
217 DPrintf("#%d: FdFileCreate(%d)\n", thr
->tid
, fd
);
220 init(thr
, pc
, fd
, &fdctx
.filesync
);
223 void FdDup(ThreadState
*thr
, uptr pc
, int oldfd
, int newfd
, bool write
) {
224 DPrintf("#%d: FdDup(%d, %d)\n", thr
->tid
, oldfd
, newfd
);
225 if (bogusfd(oldfd
) || bogusfd(newfd
))
227 // Ignore the case when user dups not yet connected socket.
228 FdDesc
*od
= fddesc(thr
, pc
, oldfd
);
229 MemoryRead(thr
, pc
, (uptr
)od
, kSizeLog8
);
230 FdClose(thr
, pc
, newfd
, write
);
231 init(thr
, pc
, newfd
, ref(od
->sync
), write
);
234 void FdPipeCreate(ThreadState
*thr
, uptr pc
, int rfd
, int wfd
) {
235 DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr
->tid
, rfd
, wfd
);
236 FdSync
*s
= allocsync(thr
, pc
);
237 init(thr
, pc
, rfd
, ref(s
));
238 init(thr
, pc
, wfd
, ref(s
));
242 void FdEventCreate(ThreadState
*thr
, uptr pc
, int fd
) {
243 DPrintf("#%d: FdEventCreate(%d)\n", thr
->tid
, fd
);
246 init(thr
, pc
, fd
, allocsync(thr
, pc
));
249 void FdSignalCreate(ThreadState
*thr
, uptr pc
, int fd
) {
250 DPrintf("#%d: FdSignalCreate(%d)\n", thr
->tid
, fd
);
253 init(thr
, pc
, fd
, 0);
256 void FdInotifyCreate(ThreadState
*thr
, uptr pc
, int fd
) {
257 DPrintf("#%d: FdInotifyCreate(%d)\n", thr
->tid
, fd
);
260 init(thr
, pc
, fd
, 0);
263 void FdPollCreate(ThreadState
*thr
, uptr pc
, int fd
) {
264 DPrintf("#%d: FdPollCreate(%d)\n", thr
->tid
, fd
);
267 init(thr
, pc
, fd
, allocsync(thr
, pc
));
270 void FdSocketCreate(ThreadState
*thr
, uptr pc
, int fd
) {
271 DPrintf("#%d: FdSocketCreate(%d)\n", thr
->tid
, fd
);
274 // It can be a UDP socket.
275 init(thr
, pc
, fd
, &fdctx
.socksync
);
278 void FdSocketAccept(ThreadState
*thr
, uptr pc
, int fd
, int newfd
) {
279 DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr
->tid
, fd
, newfd
);
282 // Synchronize connect->accept.
283 Acquire(thr
, pc
, (uptr
)&fdctx
.connectsync
);
284 init(thr
, pc
, newfd
, &fdctx
.socksync
);
287 void FdSocketConnecting(ThreadState
*thr
, uptr pc
, int fd
) {
288 DPrintf("#%d: FdSocketConnecting(%d)\n", thr
->tid
, fd
);
291 // Synchronize connect->accept.
292 Release(thr
, pc
, (uptr
)&fdctx
.connectsync
);
295 void FdSocketConnect(ThreadState
*thr
, uptr pc
, int fd
) {
296 DPrintf("#%d: FdSocketConnect(%d)\n", thr
->tid
, fd
);
299 init(thr
, pc
, fd
, &fdctx
.socksync
);
302 uptr
File2addr(const char *path
) {
308 uptr
Dir2addr(const char *path
) {
314 } // namespace __tsan