Bug 1861709 replace AudioCallbackDriver::ThreadRunning() assertions that mean to...
[gecko.git] / netwerk / base / FuzzyLayer.cpp
blob8a9ee694dd35d96b5011dbf68061697faed26eef
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "FuzzyLayer.h"
8 #include "nsTHashMap.h"
9 #include "nsDeque.h"
10 #include "nsIRunnable.h"
11 #include "nsSocketTransportService2.h"
12 #include "nsThreadUtils.h"
14 #include "prmem.h"
15 #include "prio.h"
16 #include "mozilla/Logging.h"
17 #include "mozilla/Mutex.h"
19 namespace mozilla {
20 namespace net {
22 LazyLogModule gFuzzingLog("nsFuzzingNecko");
24 #define FUZZING_LOG(args) \
25 MOZ_LOG(mozilla::net::gFuzzingLog, mozilla::LogLevel::Verbose, args)
27 // Mutex for modifying our hash tables
28 Mutex gConnRecvMutex("ConnectRecvMutex");
30 // This structure will be created by addNetworkFuzzingBuffer below
31 // and then added to the gNetworkFuzzingBuffers structure.
33 // It is assigned on connect and associated with the socket it belongs to.
34 typedef struct {
35 const uint8_t* buf;
36 size_t size;
37 bool allowRead;
38 bool allowUnused;
39 PRNetAddr* addr;
40 } NetworkFuzzingBuffer;
42 // This holds all connections we have currently open.
43 static nsTHashMap<nsPtrHashKey<PRFileDesc>, NetworkFuzzingBuffer*>
44 gConnectedNetworkFuzzingBuffers;
46 // This holds all buffers for connections we can still open.
47 static nsDeque<NetworkFuzzingBuffer> gNetworkFuzzingBuffers;
49 // This is `true` once all connections are closed and either there are
50 // no buffers left to be used or all remaining buffers are marked optional.
51 // Used by `signalNetworkFuzzingDone` to tell the main thread if it needs
52 // to spin-wait for `gFuzzingConnClosed`.
53 static Atomic<bool> fuzzingNoWaitRequired(false);
55 // Used to memorize if the main thread has indicated that it is done with
56 // its iteration and we don't expect more connections now.
57 static Atomic<bool> fuzzingMainSignaledDone(false);
60 * The flag `gFuzzingConnClosed` is set by `FuzzyClose` when all connections
61 * are closed *and* there are no more buffers in `gNetworkFuzzingBuffers` that
62 * must be used. The main thread spins until this becomes true to synchronize
63 * the fuzzing iteration between the main thread and the socket thread, if
64 * the prior call to `signalNetworkFuzzingDone` returned `false`.
66 Atomic<bool> gFuzzingConnClosed(true);
68 void addNetworkFuzzingBuffer(const uint8_t* data, size_t size, bool readFirst,
69 bool useIsOptional) {
70 if (size > INT32_MAX) {
71 MOZ_CRASH("Unsupported buffer size");
74 MutexAutoLock lock(gConnRecvMutex);
76 NetworkFuzzingBuffer* buf = new NetworkFuzzingBuffer();
77 buf->buf = data;
78 buf->size = size;
79 buf->allowRead = readFirst;
80 buf->allowUnused = useIsOptional;
81 buf->addr = nullptr;
83 gNetworkFuzzingBuffers.Push(buf);
85 fuzzingMainSignaledDone = false;
86 fuzzingNoWaitRequired = false;
90 * This method should be called by fuzzing from the main thread to signal to
91 * the layer code that a fuzzing iteration is done. As a result, we can throw
92 * away any optional buffers and signal back once all connections have been
93 * closed. The main thread should synchronize on all connections being closed
94 * after the actual request/action is complete.
96 bool signalNetworkFuzzingDone() {
97 FUZZING_LOG(("[signalNetworkFuzzingDone] Called."));
98 MutexAutoLock lock(gConnRecvMutex);
99 bool rv = false;
101 if (fuzzingNoWaitRequired) {
102 FUZZING_LOG(("[signalNetworkFuzzingDone] Purging remaining buffers."));
103 // Easy case, we already have no connections and non-optional buffers left.
104 gNetworkFuzzingBuffers.Erase();
105 gFuzzingConnClosed = true;
106 rv = true;
107 } else {
108 // We still have either connections left open or non-optional buffers left.
109 // In this case, FuzzyClose will handle the tear-down and signaling.
110 fuzzingMainSignaledDone = true;
113 return rv;
116 static PRDescIdentity sFuzzyLayerIdentity;
117 static PRIOMethods sFuzzyLayerMethods;
118 static PRIOMethods* sFuzzyLayerMethodsPtr = nullptr;
120 static PRInt16 PR_CALLBACK FuzzyPoll(PRFileDesc* fd, PRInt16 in_flags,
121 PRInt16* out_flags) {
122 *out_flags = 0;
124 FUZZING_LOG(("[FuzzyPoll] Called with in_flags=%X.", in_flags));
126 NetworkFuzzingBuffer* fuzzBuf = gConnectedNetworkFuzzingBuffers.Get(fd);
128 if (in_flags & PR_POLL_READ && fuzzBuf && fuzzBuf->allowRead) {
129 *out_flags = PR_POLL_READ;
130 return PR_POLL_READ;
133 if (in_flags & PR_POLL_WRITE) {
134 *out_flags = PR_POLL_WRITE;
135 return PR_POLL_WRITE;
138 return in_flags;
141 static PRStatus FuzzyConnect(PRFileDesc* fd, const PRNetAddr* addr,
142 PRIntervalTime timeout) {
143 MOZ_RELEASE_ASSERT(fd->identity == sFuzzyLayerIdentity);
144 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
146 MutexAutoLock lock(gConnRecvMutex);
148 NetworkFuzzingBuffer* buf = gNetworkFuzzingBuffers.PopFront();
149 if (!buf) {
150 FUZZING_LOG(("[FuzzyConnect] Denying additional connection."));
151 return PR_FAILURE;
154 gConnectedNetworkFuzzingBuffers.InsertOrUpdate(fd, buf);
155 fuzzingNoWaitRequired = false;
157 FUZZING_LOG(("[FuzzyConnect] Successfully opened connection: %p", fd));
159 gFuzzingConnClosed = false;
161 return PR_SUCCESS;
164 static PRInt32 FuzzySendTo(PRFileDesc* fd, const void* buf, PRInt32 amount,
165 PRIntn flags, const PRNetAddr* addr,
166 PRIntervalTime timeout) {
167 MOZ_RELEASE_ASSERT(fd->identity == sFuzzyLayerIdentity);
168 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
170 MutexAutoLock lock(gConnRecvMutex);
172 NetworkFuzzingBuffer* fuzzBuf = gConnectedNetworkFuzzingBuffers.Get(fd);
173 if (!fuzzBuf) {
174 NetworkFuzzingBuffer* buf = gNetworkFuzzingBuffers.PopFront();
175 if (!buf) {
176 FUZZING_LOG(("[FuzzySentTo] Denying additional connection."));
177 return 0;
180 gConnectedNetworkFuzzingBuffers.InsertOrUpdate(fd, buf);
182 // Store connection address
183 buf->addr = new PRNetAddr;
184 memcpy(buf->addr, addr, sizeof(PRNetAddr));
186 fuzzingNoWaitRequired = false;
188 FUZZING_LOG(("[FuzzySendTo] Successfully opened connection: %p", fd));
190 gFuzzingConnClosed = false;
193 // Allow reading once the implementation has written at least some data
194 if (fuzzBuf && !fuzzBuf->allowRead) {
195 FUZZING_LOG(("[FuzzySendTo] Write received, allowing further reads."));
196 fuzzBuf->allowRead = true;
199 FUZZING_LOG(("[FuzzySendTo] Sent %" PRIx32 " bytes of data.", amount));
201 return amount;
204 static PRInt32 FuzzySend(PRFileDesc* fd, const void* buf, PRInt32 amount,
205 PRIntn flags, PRIntervalTime timeout) {
206 MOZ_RELEASE_ASSERT(fd->identity == sFuzzyLayerIdentity);
207 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
209 MutexAutoLock lock(gConnRecvMutex);
211 NetworkFuzzingBuffer* fuzzBuf = gConnectedNetworkFuzzingBuffers.Get(fd);
212 if (!fuzzBuf) {
213 FUZZING_LOG(("[FuzzySend] Write on socket that is not connected."));
214 amount = 0;
217 // Allow reading once the implementation has written at least some data
218 if (fuzzBuf && !fuzzBuf->allowRead) {
219 FUZZING_LOG(("[FuzzySend] Write received, allowing further reads."));
220 fuzzBuf->allowRead = true;
223 FUZZING_LOG(("[FuzzySend] Sent %" PRIx32 " bytes of data.", amount));
225 return amount;
228 static PRInt32 FuzzyWrite(PRFileDesc* fd, const void* buf, PRInt32 amount) {
229 return FuzzySend(fd, buf, amount, 0, PR_INTERVAL_NO_WAIT);
232 static PRInt32 FuzzyRecv(PRFileDesc* fd, void* buf, PRInt32 amount,
233 PRIntn flags, PRIntervalTime timeout) {
234 MOZ_RELEASE_ASSERT(fd->identity == sFuzzyLayerIdentity);
236 MutexAutoLock lock(gConnRecvMutex);
238 NetworkFuzzingBuffer* fuzzBuf = gConnectedNetworkFuzzingBuffers.Get(fd);
239 if (!fuzzBuf) {
240 FUZZING_LOG(("[FuzzyRecv] Denying read, connection is closed."));
241 return 0;
244 // As long as we haven't written anything, act as if no data was there yet
245 if (!fuzzBuf->allowRead) {
246 FUZZING_LOG(("[FuzzyRecv] Denying read, nothing written before."));
247 PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
248 return -1;
251 if (gFuzzingConnClosed) {
252 FUZZING_LOG(("[FuzzyRecv] Denying read, connection is closed."));
253 return 0;
256 // No data left, act as if the connection was closed.
257 if (!fuzzBuf->size) {
258 FUZZING_LOG(("[FuzzyRecv] Read failed, no data left."));
259 return 0;
262 // Use the remains of fuzzing buffer, if too little is left
263 if (fuzzBuf->size < (PRUint32)amount) amount = fuzzBuf->size;
265 // Consume buffer, copy data
266 memcpy(buf, fuzzBuf->buf, amount);
268 if (!(flags & PR_MSG_PEEK)) {
269 fuzzBuf->buf += amount;
270 fuzzBuf->size -= amount;
273 FUZZING_LOG(("[FuzzyRecv] Read %" PRIx32 " bytes of data.", amount));
275 return amount;
278 static PRInt32 FuzzyRecvFrom(PRFileDesc* fd, void* buf, PRInt32 amount,
279 PRIntn flags, PRNetAddr* addr,
280 PRIntervalTime timeout) {
281 // Return the same address used for initial SendTo on this fd
282 if (addr) {
283 NetworkFuzzingBuffer* fuzzBuf = gConnectedNetworkFuzzingBuffers.Get(fd);
284 if (!fuzzBuf) {
285 FUZZING_LOG(("[FuzzyRecvFrom] Denying read, connection is closed."));
286 return 0;
289 if (fuzzBuf->addr) {
290 memcpy(addr, fuzzBuf->addr, sizeof(PRNetAddr));
291 } else {
292 FUZZING_LOG(("[FuzzyRecvFrom] No address found for connection"));
295 return FuzzyRecv(fd, buf, amount, flags, timeout);
298 static PRInt32 FuzzyRead(PRFileDesc* fd, void* buf, PRInt32 amount) {
299 return FuzzyRecv(fd, buf, amount, 0, PR_INTERVAL_NO_WAIT);
302 static PRStatus FuzzyClose(PRFileDesc* fd) {
303 if (!fd) {
304 return PR_FAILURE;
306 PRFileDesc* layer = PR_PopIOLayer(fd, PR_TOP_IO_LAYER);
307 MOZ_RELEASE_ASSERT(layer && layer->identity == sFuzzyLayerIdentity,
308 "Fuzzy Layer not on top of stack");
310 layer->dtor(layer);
312 MutexAutoLock lock(gConnRecvMutex);
314 NetworkFuzzingBuffer* fuzzBuf = nullptr;
315 if (gConnectedNetworkFuzzingBuffers.Remove(fd, &fuzzBuf)) {
316 FUZZING_LOG(("[FuzzyClose] Received close on socket %p", fd));
317 if (fuzzBuf->addr) {
318 delete fuzzBuf->addr;
320 delete fuzzBuf;
321 } else {
322 /* Happens when close is called on a non-connected socket */
323 FUZZING_LOG(("[FuzzyClose] Received close on unknown socket %p.", fd));
326 PRStatus ret = fd->methods->close(fd);
328 if (!gConnectedNetworkFuzzingBuffers.Count()) {
329 // At this point, all connections are closed, but we might still have
330 // unused network buffers that were not marked as optional.
331 bool haveRemainingUnusedBuffers = false;
332 for (size_t i = 0; i < gNetworkFuzzingBuffers.GetSize(); ++i) {
333 NetworkFuzzingBuffer* buf = gNetworkFuzzingBuffers.ObjectAt(i);
335 if (!buf->allowUnused) {
336 haveRemainingUnusedBuffers = true;
337 break;
341 if (haveRemainingUnusedBuffers) {
342 FUZZING_LOG(
343 ("[FuzzyClose] All connections closed, waiting for remaining "
344 "connections."));
345 } else if (!fuzzingMainSignaledDone) {
346 // We have no connections left, but the main thread hasn't signaled us
347 // yet. For now, that means once the main thread signals us, we can tell
348 // it immediately that it won't have to wait for closing connections.
349 FUZZING_LOG(
350 ("[FuzzyClose] All connections closed, waiting for main thread."));
351 fuzzingNoWaitRequired = true;
352 } else {
353 // No connections left and main thread is already done. Perform cleanup
354 // and then signal the main thread to continue.
355 FUZZING_LOG(("[FuzzyClose] All connections closed, cleaning up."));
357 gNetworkFuzzingBuffers.Erase();
358 gFuzzingConnClosed = true;
360 // We need to dispatch this so the main thread is guaranteed to wake up
361 nsCOMPtr<nsIRunnable> r(new mozilla::Runnable("Dummy"));
362 NS_DispatchToMainThread(r.forget());
364 } else {
365 FUZZING_LOG(("[FuzzyClose] Connection closed."));
368 return ret;
371 nsresult AttachFuzzyIOLayer(PRFileDesc* fd) {
372 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
374 if (!sFuzzyLayerMethodsPtr) {
375 sFuzzyLayerIdentity = PR_GetUniqueIdentity("Fuzzy Layer");
376 sFuzzyLayerMethods = *PR_GetDefaultIOMethods();
377 sFuzzyLayerMethods.connect = FuzzyConnect;
378 sFuzzyLayerMethods.send = FuzzySend;
379 sFuzzyLayerMethods.sendto = FuzzySendTo;
380 sFuzzyLayerMethods.write = FuzzyWrite;
381 sFuzzyLayerMethods.recv = FuzzyRecv;
382 sFuzzyLayerMethods.recvfrom = FuzzyRecvFrom;
383 sFuzzyLayerMethods.read = FuzzyRead;
384 sFuzzyLayerMethods.close = FuzzyClose;
385 sFuzzyLayerMethods.poll = FuzzyPoll;
386 sFuzzyLayerMethodsPtr = &sFuzzyLayerMethods;
389 PRFileDesc* layer =
390 PR_CreateIOLayerStub(sFuzzyLayerIdentity, sFuzzyLayerMethodsPtr);
392 if (!layer) {
393 return NS_ERROR_FAILURE;
396 PRStatus status = PR_PushIOLayer(fd, PR_TOP_IO_LAYER, layer);
398 if (status == PR_FAILURE) {
399 PR_Free(layer); // PR_CreateIOLayerStub() uses PR_Malloc().
400 return NS_ERROR_FAILURE;
403 return NS_OK;
406 } // namespace net
407 } // namespace mozilla