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 FdSync
*allocsync() {
44 FdSync
*s
= (FdSync
*)internal_alloc(MBlockFD
, sizeof(FdSync
));
45 atomic_store(&s
->rc
, 1, memory_order_relaxed
);
49 static FdSync
*ref(FdSync
*s
) {
50 if (s
&& atomic_load(&s
->rc
, memory_order_relaxed
) != (u64
)-1)
51 atomic_fetch_add(&s
->rc
, 1, memory_order_relaxed
);
55 static void unref(ThreadState
*thr
, uptr pc
, FdSync
*s
) {
56 if (s
&& atomic_load(&s
->rc
, memory_order_relaxed
) != (u64
)-1) {
57 if (atomic_fetch_sub(&s
->rc
, 1, memory_order_acq_rel
) == 1) {
58 CHECK_NE(s
, &fdctx
.globsync
);
59 CHECK_NE(s
, &fdctx
.filesync
);
60 CHECK_NE(s
, &fdctx
.socksync
);
61 SyncVar
*v
= CTX()->synctab
.GetAndRemove(thr
, pc
, (uptr
)s
);
69 static FdDesc
*fddesc(ThreadState
*thr
, uptr pc
, int fd
) {
70 CHECK_LT(fd
, kTableSize
);
71 atomic_uintptr_t
*pl1
= &fdctx
.tab
[fd
/ kTableSizeL2
];
72 uptr l1
= atomic_load(pl1
, memory_order_consume
);
74 uptr size
= kTableSizeL2
* sizeof(FdDesc
);
75 void *p
= internal_alloc(MBlockFD
, size
);
76 internal_memset(p
, 0, size
);
77 MemoryResetRange(thr
, (uptr
)&fddesc
, (uptr
)p
, size
);
78 if (atomic_compare_exchange_strong(pl1
, &l1
, (uptr
)p
, memory_order_acq_rel
))
83 return &((FdDesc
*)l1
)[fd
% kTableSizeL2
]; // NOLINT
86 // pd must be already ref'ed.
87 static void init(ThreadState
*thr
, uptr pc
, int fd
, FdSync
*s
) {
88 FdDesc
*d
= fddesc(thr
, pc
, fd
);
89 // As a matter of fact, we don't intercept all close calls.
90 // See e.g. libc __res_iclose().
92 unref(thr
, pc
, d
->sync
);
95 if (flags()->io_sync
== 0) {
97 } else if (flags()->io_sync
== 1) {
99 } else if (flags()->io_sync
== 2) {
101 d
->sync
= &fdctx
.globsync
;
103 d
->creation_tid
= thr
->tid
;
104 d
->creation_stack
= CurrentStackId(thr
, pc
);
105 // To catch races between fd usage and open.
106 MemoryRangeImitateWrite(thr
, pc
, (uptr
)d
, 8);
110 atomic_store(&fdctx
.globsync
.rc
, (u64
)-1, memory_order_relaxed
);
111 atomic_store(&fdctx
.filesync
.rc
, (u64
)-1, memory_order_relaxed
);
112 atomic_store(&fdctx
.socksync
.rc
, (u64
)-1, memory_order_relaxed
);
115 void FdOnFork(ThreadState
*thr
, uptr pc
) {
116 // On fork() we need to reset all fd's, because the child is going
117 // close all them, and that will cause races between previous read/write
119 for (int l1
= 0; l1
< kTableSizeL1
; l1
++) {
120 FdDesc
*tab
= (FdDesc
*)atomic_load(&fdctx
.tab
[l1
], memory_order_relaxed
);
123 for (int l2
= 0; l2
< kTableSizeL2
; l2
++) {
124 FdDesc
*d
= &tab
[l2
];
125 MemoryResetRange(thr
, pc
, (uptr
)d
, 8);
130 bool FdLocation(uptr addr
, int *fd
, int *tid
, u32
*stack
) {
131 for (int l1
= 0; l1
< kTableSizeL1
; l1
++) {
132 FdDesc
*tab
= (FdDesc
*)atomic_load(&fdctx
.tab
[l1
], memory_order_relaxed
);
135 if (addr
>= (uptr
)tab
&& addr
< (uptr
)(tab
+ kTableSizeL2
)) {
136 int l2
= (addr
- (uptr
)tab
) / sizeof(FdDesc
);
137 FdDesc
*d
= &tab
[l2
];
138 *fd
= l1
* kTableSizeL1
+ l2
;
139 *tid
= d
->creation_tid
;
140 *stack
= d
->creation_stack
;
147 void FdAcquire(ThreadState
*thr
, uptr pc
, int fd
) {
148 FdDesc
*d
= fddesc(thr
, pc
, fd
);
150 DPrintf("#%d: FdAcquire(%d) -> %p\n", thr
->tid
, fd
, s
);
151 MemoryRead(thr
, pc
, (uptr
)d
, kSizeLog8
);
153 Acquire(thr
, pc
, (uptr
)s
);
156 void FdRelease(ThreadState
*thr
, uptr pc
, int fd
) {
157 FdDesc
*d
= fddesc(thr
, pc
, fd
);
159 DPrintf("#%d: FdRelease(%d) -> %p\n", thr
->tid
, fd
, s
);
161 Release(thr
, pc
, (uptr
)s
);
162 MemoryRead(thr
, pc
, (uptr
)d
, kSizeLog8
);
165 void FdAccess(ThreadState
*thr
, uptr pc
, int fd
) {
166 DPrintf("#%d: FdAccess(%d)\n", thr
->tid
, fd
);
167 FdDesc
*d
= fddesc(thr
, pc
, fd
);
168 MemoryRead(thr
, pc
, (uptr
)d
, kSizeLog8
);
171 void FdClose(ThreadState
*thr
, uptr pc
, int fd
) {
172 DPrintf("#%d: FdClose(%d)\n", thr
->tid
, fd
);
173 FdDesc
*d
= fddesc(thr
, pc
, fd
);
174 // To catch races between fd usage and close.
175 MemoryWrite(thr
, pc
, (uptr
)d
, kSizeLog8
);
176 // We need to clear it, because if we do not intercept any call out there
177 // that creates fd, we will hit false postives.
178 MemoryResetRange(thr
, pc
, (uptr
)d
, 8);
179 unref(thr
, pc
, d
->sync
);
182 d
->creation_stack
= 0;
185 void FdFileCreate(ThreadState
*thr
, uptr pc
, int fd
) {
186 DPrintf("#%d: FdFileCreate(%d)\n", thr
->tid
, fd
);
187 init(thr
, pc
, fd
, &fdctx
.filesync
);
190 void FdDup(ThreadState
*thr
, uptr pc
, int oldfd
, int newfd
) {
191 DPrintf("#%d: FdDup(%d, %d)\n", thr
->tid
, oldfd
, newfd
);
192 // Ignore the case when user dups not yet connected socket.
193 FdDesc
*od
= fddesc(thr
, pc
, oldfd
);
194 MemoryRead(thr
, pc
, (uptr
)od
, kSizeLog8
);
195 FdClose(thr
, pc
, newfd
);
196 init(thr
, pc
, newfd
, ref(od
->sync
));
199 void FdPipeCreate(ThreadState
*thr
, uptr pc
, int rfd
, int wfd
) {
200 DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr
->tid
, rfd
, wfd
);
201 FdSync
*s
= allocsync();
202 init(thr
, pc
, rfd
, ref(s
));
203 init(thr
, pc
, wfd
, ref(s
));
207 void FdEventCreate(ThreadState
*thr
, uptr pc
, int fd
) {
208 DPrintf("#%d: FdEventCreate(%d)\n", thr
->tid
, fd
);
209 init(thr
, pc
, fd
, allocsync());
212 void FdSignalCreate(ThreadState
*thr
, uptr pc
, int fd
) {
213 DPrintf("#%d: FdSignalCreate(%d)\n", thr
->tid
, fd
);
214 init(thr
, pc
, fd
, 0);
217 void FdInotifyCreate(ThreadState
*thr
, uptr pc
, int fd
) {
218 DPrintf("#%d: FdInotifyCreate(%d)\n", thr
->tid
, fd
);
219 init(thr
, pc
, fd
, 0);
222 void FdPollCreate(ThreadState
*thr
, uptr pc
, int fd
) {
223 DPrintf("#%d: FdPollCreate(%d)\n", thr
->tid
, fd
);
224 init(thr
, pc
, fd
, allocsync());
227 void FdSocketCreate(ThreadState
*thr
, uptr pc
, int fd
) {
228 DPrintf("#%d: FdSocketCreate(%d)\n", thr
->tid
, fd
);
229 // It can be a UDP socket.
230 init(thr
, pc
, fd
, &fdctx
.socksync
);
233 void FdSocketAccept(ThreadState
*thr
, uptr pc
, int fd
, int newfd
) {
234 DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr
->tid
, fd
, newfd
);
235 // Synchronize connect->accept.
236 Acquire(thr
, pc
, (uptr
)&fdctx
.connectsync
);
237 init(thr
, pc
, newfd
, &fdctx
.socksync
);
240 void FdSocketConnecting(ThreadState
*thr
, uptr pc
, int fd
) {
241 DPrintf("#%d: FdSocketConnecting(%d)\n", thr
->tid
, fd
);
242 // Synchronize connect->accept.
243 Release(thr
, pc
, (uptr
)&fdctx
.connectsync
);
246 void FdSocketConnect(ThreadState
*thr
, uptr pc
, int fd
) {
247 DPrintf("#%d: FdSocketConnect(%d)\n", thr
->tid
, fd
);
248 init(thr
, pc
, fd
, &fdctx
.socksync
);
251 uptr
File2addr(char *path
) {
257 uptr
Dir2addr(char *path
) {
263 } // namespace __tsan