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