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"
10 #include "nsIRunnable.h"
11 #include "nsSocketTransportService2.h"
12 #include "nsThreadUtils.h"
16 #include "mozilla/Logging.h"
17 #include "mozilla/StaticMutex.h"
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 StaticMutex gConnRecvMutex
;
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.
40 } NetworkFuzzingBuffer
;
42 // This holds all connections we have currently open.
43 MOZ_RUNINIT
static nsTHashMap
<nsPtrHashKey
<PRFileDesc
>, NetworkFuzzingBuffer
*>
44 gConnectedNetworkFuzzingBuffers
;
46 // This holds all buffers for connections we can still open.
47 MOZ_RUNINIT
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
,
70 if (size
> INT32_MAX
) {
71 MOZ_CRASH("Unsupported buffer size");
74 StaticMutexAutoLock
lock(gConnRecvMutex
);
76 NetworkFuzzingBuffer
* buf
= new NetworkFuzzingBuffer();
79 buf
->allowRead
= readFirst
;
80 buf
->allowUnused
= useIsOptional
;
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 StaticMutexAutoLock
lock(gConnRecvMutex
);
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;
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;
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
) {
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
;
133 if (in_flags
& PR_POLL_WRITE
) {
134 *out_flags
= PR_POLL_WRITE
;
135 return PR_POLL_WRITE
;
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 StaticMutexAutoLock
lock(gConnRecvMutex
);
148 NetworkFuzzingBuffer
* buf
= gNetworkFuzzingBuffers
.PopFront();
150 FUZZING_LOG(("[FuzzyConnect] Denying additional connection."));
154 gConnectedNetworkFuzzingBuffers
.InsertOrUpdate(fd
, buf
);
155 fuzzingNoWaitRequired
= false;
157 FUZZING_LOG(("[FuzzyConnect] Successfully opened connection: %p", fd
));
159 gFuzzingConnClosed
= false;
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 StaticMutexAutoLock
lock(gConnRecvMutex
);
172 NetworkFuzzingBuffer
* fuzzBuf
= gConnectedNetworkFuzzingBuffers
.Get(fd
);
174 NetworkFuzzingBuffer
* buf
= gNetworkFuzzingBuffers
.PopFront();
176 FUZZING_LOG(("[FuzzySentTo] Denying additional connection."));
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
));
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 StaticMutexAutoLock
lock(gConnRecvMutex
);
211 NetworkFuzzingBuffer
* fuzzBuf
= gConnectedNetworkFuzzingBuffers
.Get(fd
);
213 FUZZING_LOG(("[FuzzySend] Write on socket that is not connected."));
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
));
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 StaticMutexAutoLock
lock(gConnRecvMutex
);
238 NetworkFuzzingBuffer
* fuzzBuf
= gConnectedNetworkFuzzingBuffers
.Get(fd
);
240 FUZZING_LOG(("[FuzzyRecv] Denying read, connection is closed."));
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);
251 if (gFuzzingConnClosed
) {
252 FUZZING_LOG(("[FuzzyRecv] Denying read, connection is closed."));
256 // No data left, act as if the connection was closed.
257 if (!fuzzBuf
->size
) {
258 FUZZING_LOG(("[FuzzyRecv] Read failed, no data left."));
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
));
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
283 NetworkFuzzingBuffer
* fuzzBuf
= gConnectedNetworkFuzzingBuffers
.Get(fd
);
285 FUZZING_LOG(("[FuzzyRecvFrom] Denying read, connection is closed."));
290 memcpy(addr
, fuzzBuf
->addr
, sizeof(PRNetAddr
));
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
) {
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");
312 StaticMutexAutoLock
lock(gConnRecvMutex
);
314 NetworkFuzzingBuffer
* fuzzBuf
= nullptr;
315 if (gConnectedNetworkFuzzingBuffers
.Remove(fd
, &fuzzBuf
)) {
316 FUZZING_LOG(("[FuzzyClose] Received close on socket %p", fd
));
318 delete fuzzBuf
->addr
;
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;
341 if (haveRemainingUnusedBuffers
) {
343 ("[FuzzyClose] All connections closed, waiting for remaining "
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.
350 ("[FuzzyClose] All connections closed, waiting for main thread."));
351 fuzzingNoWaitRequired
= true;
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());
365 FUZZING_LOG(("[FuzzyClose] Connection closed."));
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
;
390 PR_CreateIOLayerStub(sFuzzyLayerIdentity
, sFuzzyLayerMethodsPtr
);
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
;
407 } // namespace mozilla