Source code

Revision control

Other Tools

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/. */
6
7
#include "FuzzyLayer.h"
8
#include "nsDataHashtable.h"
9
#include "nsDeque.h"
10
#include "nsIRunnable.h"
11
#include "nsSocketTransportService2.h"
12
#include "nsThreadUtils.h"
13
14
#include "prmem.h"
15
#include "prio.h"
16
#include "mozilla/Logging.h"
17
#include "mozilla/Mutex.h"
18
19
namespace mozilla {
20
namespace net {
21
22
LazyLogModule gFuzzingLog("nsFuzzingNecko");
23
24
#define FUZZING_LOG(args) \
25
MOZ_LOG(mozilla::net::gFuzzingLog, mozilla::LogLevel::Verbose, args)
26
27
// Mutex for modifying our hash tables
28
Mutex gConnRecvMutex("ConnectRecvMutex");
29
30
// This structure will be created by addNetworkFuzzingBuffer below
31
// and then added to the gNetworkFuzzingBuffers structure.
32
//
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
} NetworkFuzzingBuffer;
40
41
// This holds all connections we have currently open.
42
static nsDataHashtable<nsPtrHashKey<PRFileDesc>, NetworkFuzzingBuffer*>
43
gConnectedNetworkFuzzingBuffers;
44
45
// This holds all buffers for connections we can still open.
46
static nsDeque gNetworkFuzzingBuffers;
47
48
// This is `true` once all connections are closed and either there are
49
// no buffers left to be used or all remaining buffers are marked optional.
50
// Used by `signalNetworkFuzzingDone` to tell the main thread if it needs
51
// to spin-wait for `gFuzzingConnClosed`.
52
static Atomic<bool> fuzzingNoWaitRequired(false);
53
54
// Used to memorize if the main thread has indicated that it is done with
55
// its iteration and we don't expect more connections now.
56
static Atomic<bool> fuzzingMainSignaledDone(false);
57
58
/*
59
* The flag `gFuzzingConnClosed` is set by `FuzzyClose` when all connections
60
* are closed *and* there are no more buffers in `gNetworkFuzzingBuffers` that
61
* must be used. The main thread spins until this becomes true to synchronize
62
* the fuzzing iteration between the main thread and the socket thread, if
63
* the prior call to `signalNetworkFuzzingDone` returned `false`.
64
*/
65
Atomic<bool> gFuzzingConnClosed(true);
66
67
void addNetworkFuzzingBuffer(const uint8_t* data, size_t size, bool readFirst,
68
bool useIsOptional) {
69
if (size > INT32_MAX) {
70
MOZ_CRASH("Unsupported buffer size");
71
}
72
73
MutexAutoLock lock(gConnRecvMutex);
74
75
NetworkFuzzingBuffer* buf = new NetworkFuzzingBuffer();
76
buf->buf = data;
77
buf->size = size;
78
buf->allowRead = readFirst;
79
buf->allowUnused = useIsOptional;
80
81
gNetworkFuzzingBuffers.Push(buf);
82
83
fuzzingMainSignaledDone = false;
84
fuzzingNoWaitRequired = false;
85
}
86
87
/*
88
* This method should be called by fuzzing from the main thread to signal to
89
* the layer code that a fuzzing iteration is done. As a result, we can throw
90
* away any optional buffers and signal back once all connections have been
91
* closed. The main thread should synchronize on all connections being closed
92
* after the actual request/action is complete.
93
*/
94
bool signalNetworkFuzzingDone() {
95
FUZZING_LOG(("[signalNetworkFuzzingDone] Called."));
96
MutexAutoLock lock(gConnRecvMutex);
97
bool rv = false;
98
99
if (fuzzingNoWaitRequired) {
100
FUZZING_LOG(("[signalNetworkFuzzingDone] Purging remaining buffers."));
101
// Easy case, we already have no connections and non-optional buffers left.
102
gNetworkFuzzingBuffers.Erase();
103
gFuzzingConnClosed = true;
104
rv = true;
105
} else {
106
// We still have either connections left open or non-optional buffers left.
107
// In this case, FuzzyClose will handle the tear-down and signaling.
108
fuzzingMainSignaledDone = true;
109
}
110
111
return rv;
112
}
113
114
static PRDescIdentity sFuzzyLayerIdentity;
115
static PRIOMethods sFuzzyLayerMethods;
116
static PRIOMethods* sFuzzyLayerMethodsPtr = nullptr;
117
118
static PRInt16 PR_CALLBACK FuzzyPoll(PRFileDesc* fd, PRInt16 in_flags,
119
PRInt16* out_flags) {
120
*out_flags = 0;
121
122
FUZZING_LOG(("[FuzzyPoll] Called with in_flags=%X.", in_flags));
123
124
NetworkFuzzingBuffer* fuzzBuf = gConnectedNetworkFuzzingBuffers.Get(fd);
125
126
if (in_flags & PR_POLL_READ && fuzzBuf && fuzzBuf->allowRead) {
127
*out_flags = PR_POLL_READ;
128
return PR_POLL_READ;
129
}
130
131
if (in_flags & PR_POLL_WRITE) {
132
*out_flags = PR_POLL_WRITE;
133
return PR_POLL_WRITE;
134
}
135
136
return in_flags;
137
}
138
139
static PRStatus FuzzyConnect(PRFileDesc* fd, const PRNetAddr* addr,
140
PRIntervalTime timeout) {
141
MOZ_RELEASE_ASSERT(fd->identity == sFuzzyLayerIdentity);
142
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
143
144
MutexAutoLock lock(gConnRecvMutex);
145
146
NetworkFuzzingBuffer* buf =
147
(NetworkFuzzingBuffer*)gNetworkFuzzingBuffers.PopFront();
148
if (!buf) {
149
FUZZING_LOG(("[FuzzyConnect] Denying additional connection."));
150
return PR_FAILURE;
151
}
152
153
gConnectedNetworkFuzzingBuffers.Put(fd, buf);
154
fuzzingNoWaitRequired = false;
155
156
FUZZING_LOG(("[FuzzyConnect] Successfully opened connection: %p", fd));
157
158
gFuzzingConnClosed = false;
159
160
return PR_SUCCESS;
161
}
162
163
static PRInt32 FuzzySend(PRFileDesc* fd, const void* buf, PRInt32 amount,
164
PRIntn flags, PRIntervalTime timeout) {
165
MOZ_RELEASE_ASSERT(fd->identity == sFuzzyLayerIdentity);
166
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
167
168
MutexAutoLock lock(gConnRecvMutex);
169
170
NetworkFuzzingBuffer* fuzzBuf = gConnectedNetworkFuzzingBuffers.Get(fd);
171
if (!fuzzBuf) {
172
FUZZING_LOG(("[FuzzySend] Write on socket that is not connected."));
173
amount = 0;
174
}
175
176
// Allow reading once the implementation has written at least some data
177
if (fuzzBuf && !fuzzBuf->allowRead) {
178
FUZZING_LOG(("[FuzzySend] Write received, allowing further reads."));
179
fuzzBuf->allowRead = true;
180
}
181
182
FUZZING_LOG(("[FuzzySend] Sent %" PRIx32 " bytes of data.", amount));
183
184
return amount;
185
}
186
187
static PRInt32 FuzzyWrite(PRFileDesc* fd, const void* buf, PRInt32 amount) {
188
return FuzzySend(fd, buf, amount, 0, PR_INTERVAL_NO_WAIT);
189
}
190
191
static PRInt32 FuzzyRecv(PRFileDesc* fd, void* buf, PRInt32 amount,
192
PRIntn flags, PRIntervalTime timeout) {
193
MOZ_RELEASE_ASSERT(fd->identity == sFuzzyLayerIdentity);
194
195
MutexAutoLock lock(gConnRecvMutex);
196
197
NetworkFuzzingBuffer* fuzzBuf = gConnectedNetworkFuzzingBuffers.Get(fd);
198
if (!fuzzBuf) {
199
FUZZING_LOG(("[FuzzyRecv] Denying read, connection is closed."));
200
return 0;
201
}
202
203
// As long as we haven't written anything, act as if no data was there yet
204
if (!fuzzBuf->allowRead) {
205
FUZZING_LOG(("[FuzzyRecv] Denying read, nothing written before."));
206
PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
207
return -1;
208
}
209
210
if (gFuzzingConnClosed) {
211
FUZZING_LOG(("[FuzzyRecv] Denying read, connection is closed."));
212
return 0;
213
}
214
215
// No data left, act as if the connection was closed.
216
if (!fuzzBuf->size) {
217
FUZZING_LOG(("[FuzzyRecv] Read failed, no data left."));
218
return 0;
219
}
220
221
// Use the remains of fuzzing buffer, if too little is left
222
if (fuzzBuf->size < (PRUint32)amount) amount = fuzzBuf->size;
223
224
// Consume buffer, copy data
225
memcpy(buf, fuzzBuf->buf, amount);
226
227
if (!(flags & PR_MSG_PEEK)) {
228
fuzzBuf->buf += amount;
229
fuzzBuf->size -= amount;
230
}
231
232
FUZZING_LOG(("[FuzzyRecv] Read %" PRIx32 " bytes of data.", amount));
233
234
return amount;
235
}
236
237
static PRInt32 FuzzyRead(PRFileDesc* fd, void* buf, PRInt32 amount) {
238
return FuzzyRecv(fd, buf, amount, 0, PR_INTERVAL_NO_WAIT);
239
}
240
241
static PRStatus FuzzyClose(PRFileDesc* fd) {
242
if (!fd) {
243
return PR_FAILURE;
244
}
245
PRFileDesc* layer = PR_PopIOLayer(fd, PR_TOP_IO_LAYER);
246
MOZ_RELEASE_ASSERT(layer && layer->identity == sFuzzyLayerIdentity,
247
"Fuzzy Layer not on top of stack");
248
249
layer->dtor(layer);
250
251
MutexAutoLock lock(gConnRecvMutex);
252
253
NetworkFuzzingBuffer* fuzzBuf = nullptr;
254
if (gConnectedNetworkFuzzingBuffers.Remove(fd, &fuzzBuf)) {
255
FUZZING_LOG(("[FuzzyClose] Received close on socket %p", fd));
256
delete fuzzBuf;
257
} else {
258
/* Happens when close is called on a non-connected socket */
259
FUZZING_LOG(("[FuzzyClose] Received close on unknown socket %p.", fd));
260
}
261
262
PRStatus ret = fd->methods->close(fd);
263
264
if (!gConnectedNetworkFuzzingBuffers.Count()) {
265
// At this point, all connections are closed, but we might still have
266
// unused network buffers that were not marked as optional.
267
bool haveRemainingUnusedBuffers = false;
268
for (size_t i = 0; i < gNetworkFuzzingBuffers.GetSize(); ++i) {
269
NetworkFuzzingBuffer* buf = static_cast<NetworkFuzzingBuffer*>(
270
gNetworkFuzzingBuffers.ObjectAt(i));
271
272
if (!buf->allowUnused) {
273
haveRemainingUnusedBuffers = true;
274
break;
275
}
276
}
277
278
if (haveRemainingUnusedBuffers) {
279
FUZZING_LOG(
280
("[FuzzyClose] All connections closed, waiting for remaining "
281
"connections."));
282
} else if (!fuzzingMainSignaledDone) {
283
// We have no connections left, but the main thread hasn't signaled us
284
// yet. For now, that means once the main thread signals us, we can tell
285
// it immediately that it won't have to wait for closing connections.
286
FUZZING_LOG(
287
("[FuzzyClose] All connections closed, waiting for main thread."));
288
fuzzingNoWaitRequired = true;
289
} else {
290
// No connections left and main thread is already done. Perform cleanup
291
// and then signal the main thread to continue.
292
FUZZING_LOG(("[FuzzyClose] All connections closed, cleaning up."));
293
294
gNetworkFuzzingBuffers.Erase();
295
gFuzzingConnClosed = true;
296
297
// We need to dispatch this so the main thread is guaranteed to wake up
298
nsCOMPtr<nsIRunnable> r(new mozilla::Runnable("Dummy"));
299
NS_DispatchToMainThread(r.forget());
300
}
301
} else {
302
FUZZING_LOG(("[FuzzyClose] Connection closed."));
303
}
304
305
return ret;
306
}
307
308
nsresult AttachFuzzyIOLayer(PRFileDesc* fd) {
309
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
310
311
if (!sFuzzyLayerMethodsPtr) {
312
sFuzzyLayerIdentity = PR_GetUniqueIdentity("Fuzzy Layer");
313
sFuzzyLayerMethods = *PR_GetDefaultIOMethods();
314
sFuzzyLayerMethods.connect = FuzzyConnect;
315
sFuzzyLayerMethods.send = FuzzySend;
316
sFuzzyLayerMethods.write = FuzzyWrite;
317
sFuzzyLayerMethods.recv = FuzzyRecv;
318
sFuzzyLayerMethods.read = FuzzyRead;
319
sFuzzyLayerMethods.close = FuzzyClose;
320
sFuzzyLayerMethods.poll = FuzzyPoll;
321
sFuzzyLayerMethodsPtr = &sFuzzyLayerMethods;
322
}
323
324
PRFileDesc* layer =
325
PR_CreateIOLayerStub(sFuzzyLayerIdentity, sFuzzyLayerMethodsPtr);
326
327
if (!layer) {
328
return NS_ERROR_FAILURE;
329
}
330
331
PRStatus status = PR_PushIOLayer(fd, PR_TOP_IO_LAYER, layer);
332
333
if (status == PR_FAILURE) {
334
PR_Free(layer); // PR_CreateIOLayerStub() uses PR_Malloc().
335
return NS_ERROR_FAILURE;
336
}
337
338
return NS_OK;
339
}
340
341
} // namespace net
342
} // namespace mozilla