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(thr
, pc
, sizeof(FdSync
), kDefaultAlignment
,
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(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
) {
93 FdDesc
*d
= fddesc(thr
, pc
, fd
);
94 // As a matter of fact, we don't intercept all close calls.
95 // See e.g. libc __res_iclose().
97 unref(thr
, pc
, d
->sync
);
100 if (flags()->io_sync
== 0) {
102 } else if (flags()->io_sync
== 1) {
104 } else if (flags()->io_sync
== 2) {
106 d
->sync
= &fdctx
.globsync
;
108 d
->creation_tid
= thr
->tid
;
109 d
->creation_stack
= CurrentStackId(thr
, pc
);
110 // To catch races between fd usage and open.
111 MemoryRangeImitateWrite(thr
, pc
, (uptr
)d
, 8);
115 atomic_store(&fdctx
.globsync
.rc
, (u64
)-1, memory_order_relaxed
);
116 atomic_store(&fdctx
.filesync
.rc
, (u64
)-1, memory_order_relaxed
);
117 atomic_store(&fdctx
.socksync
.rc
, (u64
)-1, memory_order_relaxed
);
120 void FdOnFork(ThreadState
*thr
, uptr pc
) {
121 // On fork() we need to reset all fd's, because the child is going
122 // close all them, and that will cause races between previous read/write
124 for (int l1
= 0; l1
< kTableSizeL1
; l1
++) {
125 FdDesc
*tab
= (FdDesc
*)atomic_load(&fdctx
.tab
[l1
], memory_order_relaxed
);
128 for (int l2
= 0; l2
< kTableSizeL2
; l2
++) {
129 FdDesc
*d
= &tab
[l2
];
130 MemoryResetRange(thr
, pc
, (uptr
)d
, 8);
135 bool FdLocation(uptr addr
, int *fd
, int *tid
, u32
*stack
) {
136 for (int l1
= 0; l1
< kTableSizeL1
; l1
++) {
137 FdDesc
*tab
= (FdDesc
*)atomic_load(&fdctx
.tab
[l1
], memory_order_relaxed
);
140 if (addr
>= (uptr
)tab
&& addr
< (uptr
)(tab
+ kTableSizeL2
)) {
141 int l2
= (addr
- (uptr
)tab
) / sizeof(FdDesc
);
142 FdDesc
*d
= &tab
[l2
];
143 *fd
= l1
* kTableSizeL1
+ l2
;
144 *tid
= d
->creation_tid
;
145 *stack
= d
->creation_stack
;
152 void FdAcquire(ThreadState
*thr
, uptr pc
, int fd
) {
155 FdDesc
*d
= fddesc(thr
, pc
, fd
);
157 DPrintf("#%d: FdAcquire(%d) -> %p\n", thr
->tid
, fd
, s
);
158 MemoryRead(thr
, pc
, (uptr
)d
, kSizeLog8
);
160 Acquire(thr
, pc
, (uptr
)s
);
163 void FdRelease(ThreadState
*thr
, uptr pc
, int fd
) {
166 FdDesc
*d
= fddesc(thr
, pc
, fd
);
168 DPrintf("#%d: FdRelease(%d) -> %p\n", thr
->tid
, fd
, s
);
169 MemoryRead(thr
, pc
, (uptr
)d
, kSizeLog8
);
171 Release(thr
, pc
, (uptr
)s
);
174 void FdAccess(ThreadState
*thr
, uptr pc
, int fd
) {
175 DPrintf("#%d: FdAccess(%d)\n", thr
->tid
, fd
);
178 FdDesc
*d
= fddesc(thr
, pc
, fd
);
179 MemoryRead(thr
, pc
, (uptr
)d
, kSizeLog8
);
182 void FdClose(ThreadState
*thr
, uptr pc
, int fd
) {
183 DPrintf("#%d: FdClose(%d)\n", thr
->tid
, fd
);
186 FdDesc
*d
= fddesc(thr
, pc
, fd
);
187 // To catch races between fd usage and close.
188 MemoryWrite(thr
, pc
, (uptr
)d
, kSizeLog8
);
189 // We need to clear it, because if we do not intercept any call out there
190 // that creates fd, we will hit false postives.
191 MemoryResetRange(thr
, pc
, (uptr
)d
, 8);
192 unref(thr
, pc
, d
->sync
);
195 d
->creation_stack
= 0;
198 void FdFileCreate(ThreadState
*thr
, uptr pc
, int fd
) {
199 DPrintf("#%d: FdFileCreate(%d)\n", thr
->tid
, fd
);
202 init(thr
, pc
, fd
, &fdctx
.filesync
);
205 void FdDup(ThreadState
*thr
, uptr pc
, int oldfd
, int newfd
) {
206 DPrintf("#%d: FdDup(%d, %d)\n", thr
->tid
, oldfd
, newfd
);
207 if (bogusfd(oldfd
) || bogusfd(newfd
))
209 // Ignore the case when user dups not yet connected socket.
210 FdDesc
*od
= fddesc(thr
, pc
, oldfd
);
211 MemoryRead(thr
, pc
, (uptr
)od
, kSizeLog8
);
212 FdClose(thr
, pc
, newfd
);
213 init(thr
, pc
, newfd
, ref(od
->sync
));
216 void FdPipeCreate(ThreadState
*thr
, uptr pc
, int rfd
, int wfd
) {
217 DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr
->tid
, rfd
, wfd
);
218 FdSync
*s
= allocsync(thr
, pc
);
219 init(thr
, pc
, rfd
, ref(s
));
220 init(thr
, pc
, wfd
, ref(s
));
224 void FdEventCreate(ThreadState
*thr
, uptr pc
, int fd
) {
225 DPrintf("#%d: FdEventCreate(%d)\n", thr
->tid
, fd
);
228 init(thr
, pc
, fd
, allocsync(thr
, pc
));
231 void FdSignalCreate(ThreadState
*thr
, uptr pc
, int fd
) {
232 DPrintf("#%d: FdSignalCreate(%d)\n", thr
->tid
, fd
);
235 init(thr
, pc
, fd
, 0);
238 void FdInotifyCreate(ThreadState
*thr
, uptr pc
, int fd
) {
239 DPrintf("#%d: FdInotifyCreate(%d)\n", thr
->tid
, fd
);
242 init(thr
, pc
, fd
, 0);
245 void FdPollCreate(ThreadState
*thr
, uptr pc
, int fd
) {
246 DPrintf("#%d: FdPollCreate(%d)\n", thr
->tid
, fd
);
249 init(thr
, pc
, fd
, allocsync(thr
, pc
));
252 void FdSocketCreate(ThreadState
*thr
, uptr pc
, int fd
) {
253 DPrintf("#%d: FdSocketCreate(%d)\n", thr
->tid
, fd
);
256 // It can be a UDP socket.
257 init(thr
, pc
, fd
, &fdctx
.socksync
);
260 void FdSocketAccept(ThreadState
*thr
, uptr pc
, int fd
, int newfd
) {
261 DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr
->tid
, fd
, newfd
);
264 // Synchronize connect->accept.
265 Acquire(thr
, pc
, (uptr
)&fdctx
.connectsync
);
266 init(thr
, pc
, newfd
, &fdctx
.socksync
);
269 void FdSocketConnecting(ThreadState
*thr
, uptr pc
, int fd
) {
270 DPrintf("#%d: FdSocketConnecting(%d)\n", thr
->tid
, fd
);
273 // Synchronize connect->accept.
274 Release(thr
, pc
, (uptr
)&fdctx
.connectsync
);
277 void FdSocketConnect(ThreadState
*thr
, uptr pc
, int fd
) {
278 DPrintf("#%d: FdSocketConnect(%d)\n", thr
->tid
, fd
);
281 init(thr
, pc
, fd
, &fdctx
.socksync
);
284 uptr
File2addr(const char *path
) {
290 uptr
Dir2addr(const char *path
) {
296 } // namespace __tsan