tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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