FuzzyLayer.cpp (13169B)
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 "nsTHashMap.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/StaticMutex.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 StaticMutex gConnRecvMutex; 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 PRNetAddr* addr; 40 } NetworkFuzzingBuffer; 41 42 // This holds all connections we have currently open. 43 MOZ_RUNINIT static nsTHashMap<nsPtrHashKey<PRFileDesc>, NetworkFuzzingBuffer*> 44 gConnectedNetworkFuzzingBuffers; 45 46 // This holds all buffers for connections we can still open. 47 MOZ_RUNINIT static nsDeque<NetworkFuzzingBuffer> gNetworkFuzzingBuffers; 48 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); 54 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); 58 59 /* 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`. 65 */ 66 Atomic<bool> gFuzzingConnClosed(true); 67 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"); 72 } 73 74 StaticMutexAutoLock lock(gConnRecvMutex); 75 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; 82 83 gNetworkFuzzingBuffers.Push(buf); 84 85 fuzzingMainSignaledDone = false; 86 fuzzingNoWaitRequired = false; 87 } 88 89 /* 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. 95 */ 96 bool signalNetworkFuzzingDone() { 97 FUZZING_LOG(("[signalNetworkFuzzingDone] Called.")); 98 StaticMutexAutoLock lock(gConnRecvMutex); 99 bool rv = false; 100 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; 111 } 112 113 return rv; 114 } 115 116 static PRDescIdentity sFuzzyLayerIdentity; 117 static PRIOMethods sFuzzyLayerMethods; 118 static PRIOMethods* sFuzzyLayerMethodsPtr = nullptr; 119 120 static PRInt16 PR_CALLBACK FuzzyPoll(PRFileDesc* fd, PRInt16 in_flags, 121 PRInt16* out_flags) { 122 *out_flags = 0; 123 124 FUZZING_LOG(("[FuzzyPoll] Called with in_flags=%X.", in_flags)); 125 126 NetworkFuzzingBuffer* fuzzBuf = gConnectedNetworkFuzzingBuffers.Get(fd); 127 128 if (in_flags & PR_POLL_READ && fuzzBuf && fuzzBuf->allowRead) { 129 *out_flags = PR_POLL_READ; 130 return PR_POLL_READ; 131 } 132 133 if (in_flags & PR_POLL_WRITE) { 134 *out_flags = PR_POLL_WRITE; 135 return PR_POLL_WRITE; 136 } 137 138 return in_flags; 139 } 140 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"); 145 146 StaticMutexAutoLock lock(gConnRecvMutex); 147 148 NetworkFuzzingBuffer* buf = gNetworkFuzzingBuffers.PopFront(); 149 if (!buf) { 150 FUZZING_LOG(("[FuzzyConnect] Denying additional connection.")); 151 return PR_FAILURE; 152 } 153 154 gConnectedNetworkFuzzingBuffers.InsertOrUpdate(fd, buf); 155 fuzzingNoWaitRequired = false; 156 157 FUZZING_LOG(("[FuzzyConnect] Successfully opened connection: %p", fd)); 158 159 gFuzzingConnClosed = false; 160 161 return PR_SUCCESS; 162 } 163 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"); 169 170 StaticMutexAutoLock lock(gConnRecvMutex); 171 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; 178 } 179 180 gConnectedNetworkFuzzingBuffers.InsertOrUpdate(fd, buf); 181 182 // Store connection address 183 buf->addr = new PRNetAddr; 184 memcpy(buf->addr, addr, sizeof(PRNetAddr)); 185 186 fuzzingNoWaitRequired = false; 187 188 FUZZING_LOG(("[FuzzySendTo] Successfully opened connection: %p", fd)); 189 190 gFuzzingConnClosed = false; 191 } 192 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; 197 } 198 199 FUZZING_LOG(("[FuzzySendTo] Sent %" PRIx32 " bytes of data.", amount)); 200 201 return amount; 202 } 203 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"); 208 209 StaticMutexAutoLock lock(gConnRecvMutex); 210 211 NetworkFuzzingBuffer* fuzzBuf = gConnectedNetworkFuzzingBuffers.Get(fd); 212 if (!fuzzBuf) { 213 FUZZING_LOG(("[FuzzySend] Write on socket that is not connected.")); 214 amount = 0; 215 } 216 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; 221 } 222 223 FUZZING_LOG(("[FuzzySend] Sent %" PRIx32 " bytes of data.", amount)); 224 225 return amount; 226 } 227 228 static PRInt32 FuzzyWrite(PRFileDesc* fd, const void* buf, PRInt32 amount) { 229 return FuzzySend(fd, buf, amount, 0, PR_INTERVAL_NO_WAIT); 230 } 231 232 static PRInt32 FuzzyRecv(PRFileDesc* fd, void* buf, PRInt32 amount, 233 PRIntn flags, PRIntervalTime timeout) { 234 MOZ_RELEASE_ASSERT(fd->identity == sFuzzyLayerIdentity); 235 236 StaticMutexAutoLock lock(gConnRecvMutex); 237 238 NetworkFuzzingBuffer* fuzzBuf = gConnectedNetworkFuzzingBuffers.Get(fd); 239 if (!fuzzBuf) { 240 FUZZING_LOG(("[FuzzyRecv] Denying read, connection is closed.")); 241 return 0; 242 } 243 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; 249 } 250 251 if (gFuzzingConnClosed) { 252 FUZZING_LOG(("[FuzzyRecv] Denying read, connection is closed.")); 253 return 0; 254 } 255 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; 260 } 261 262 // Use the remains of fuzzing buffer, if too little is left 263 if (fuzzBuf->size < (PRUint32)amount) amount = fuzzBuf->size; 264 265 // Consume buffer, copy data 266 memcpy(buf, fuzzBuf->buf, amount); 267 268 if (!(flags & PR_MSG_PEEK)) { 269 fuzzBuf->buf += amount; 270 fuzzBuf->size -= amount; 271 } 272 273 FUZZING_LOG(("[FuzzyRecv] Read %" PRIx32 " bytes of data.", amount)); 274 275 return amount; 276 } 277 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; 287 } 288 289 if (fuzzBuf->addr) { 290 memcpy(addr, fuzzBuf->addr, sizeof(PRNetAddr)); 291 } else { 292 FUZZING_LOG(("[FuzzyRecvFrom] No address found for connection")); 293 } 294 } 295 return FuzzyRecv(fd, buf, amount, flags, timeout); 296 } 297 298 static PRInt32 FuzzyRead(PRFileDesc* fd, void* buf, PRInt32 amount) { 299 return FuzzyRecv(fd, buf, amount, 0, PR_INTERVAL_NO_WAIT); 300 } 301 302 static PRStatus FuzzyClose(PRFileDesc* fd) { 303 if (!fd) { 304 return PR_FAILURE; 305 } 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"); 309 310 layer->dtor(layer); 311 312 StaticMutexAutoLock lock(gConnRecvMutex); 313 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; 319 } 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)); 324 } 325 326 PRStatus ret = fd->methods->close(fd); 327 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); 334 335 if (!buf->allowUnused) { 336 haveRemainingUnusedBuffers = true; 337 break; 338 } 339 } 340 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.")); 356 357 gNetworkFuzzingBuffers.Erase(); 358 gFuzzingConnClosed = true; 359 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()); 363 } 364 } else { 365 FUZZING_LOG(("[FuzzyClose] Connection closed.")); 366 } 367 368 return ret; 369 } 370 371 nsresult AttachFuzzyIOLayer(PRFileDesc* fd) { 372 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 373 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; 387 } 388 389 PRFileDesc* layer = 390 PR_CreateIOLayerStub(sFuzzyLayerIdentity, sFuzzyLayerMethodsPtr); 391 392 if (!layer) { 393 return NS_ERROR_FAILURE; 394 } 395 396 PRStatus status = PR_PushIOLayer(fd, PR_TOP_IO_LAYER, layer); 397 398 if (status == PR_FAILURE) { 399 PR_Free(layer); // PR_CreateIOLayerStub() uses PR_Malloc(). 400 return NS_ERROR_FAILURE; 401 } 402 403 return NS_OK; 404 } 405 406 } // namespace net 407 } // namespace mozilla