Skip to content

Commit

Permalink
Use loopCanSleep instead of a lock in Router
Browse files Browse the repository at this point in the history
  • Loading branch information
esev authored and fifieldt committed Jan 9, 2025
1 parent f17b4a8 commit aaa88b8
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 20 deletions.
47 changes: 41 additions & 6 deletions .clusterfuzzlite/router_fuzzer.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Fuzzer implementation that sends MeshPackets to Router::enqueueReceivedMessage.
#include <condition_variable>
#include <mutex>
#include <pb_decode.h>
#include <string>
Expand All @@ -15,7 +16,30 @@
namespace
{
constexpr uint32_t nodeId = 0x12345678;
// Set to true when lateInitVariant finishes. Used to ensure lateInitVariant was called during startup.
bool hasBeenConfigured = false;

// These are used to block the Arduino loop() function until a fuzzer input is ready. This is
// an optimization that prevents a sleep from happening before the loop is run. The Arduino loop
// function calls loopCanSleep() before sleeping. loopCanSleep is implemented here in the fuzzer
// and blocks until startLoop() is called to signal for the loop to run.
bool fuzzerRunning = false; // Set to true once LLVMFuzzerTestOneInput has started running.
bool loopCanRun = true; // The main Arduino loop() can run when this is true.
bool loopIsWaiting = false; // The main Arduino loop() is waiting to be signaled to run.
std::mutex loopLock;
std::condition_variable loopCV;

// Start the loop for one test case and wait till the loop has completed. This ensures fuzz
// test cases do not overlap with one another. This helps the fuzzer attribute a crash to the
// single, currently running, test case.
void runLoopOnce()
{
std::unique_lock<std::mutex> lck(loopLock);
fuzzerRunning = true;
loopCanRun = true;
loopCV.notify_one();
loopCV.wait(lck, [] { return !loopCanRun && loopIsWaiting; });
}
} // namespace

// Called just prior to starting Meshtastic. Allows for setting config values before startup.
Expand Down Expand Up @@ -71,6 +95,22 @@ void lateInitVariant()
hasBeenConfigured = true;
}

// Called in the main Arduino loop function to determine if the loop can delay/sleep before running again.
// We use this as a way to block the loop from sleeping and to start the loop function immediately when a
// fuzzer input is ready.
bool loopCanSleep()
{
std::unique_lock<std::mutex> lck(loopLock);
loopIsWaiting = true;
loopCV.notify_one();
loopCV.wait(lck, [] { return loopCanRun; });
loopIsWaiting = false;
if (!fuzzerRunning)
return true; // The loop can sleep before the fuzzer starts.
loopCanRun = false; // Only run the loop once before waiting again.
return false;
}

extern "C" {
int portduino_main(int argc, char **argv); // Renamed "main" function from Meshtastic binary.

Expand Down Expand Up @@ -134,13 +174,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t length)
if (p.pki_encrypted && config.security.admin_key_count)
memcpy(&p.public_key, &config.security.admin_key[0], sizeof(p.public_key));

// Ideally only one packet, the one generated by the fuzzer, is being processed by the firmware at
// a time. We acquire a lock here, and the router unlocks it after it has processed all queued packets.
// Grabbing the lock again, below, should block until the queue has been emptied.
router->inProgressLock.lock();
router->enqueueReceivedMessage(packetPool.allocCopy(p));

const std::lock_guard<std::mutex> lck(router->inProgressLock);
runLoopOnce();
return 0; // Accept: The input may be added to the corpus.
}
}
4 changes: 0 additions & 4 deletions src/mesh/Router.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,6 @@ int32_t Router::runOnce()
perhapsHandleReceived(mp);
}

#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
inProgressLock.unlock();
return 5;
#endif
// LOG_DEBUG("Sleep forever!");
return INT32_MAX; // Wait a long time - until we get woken for the message queue
}
Expand Down
10 changes: 0 additions & 10 deletions src/mesh/Router.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@
#include "RadioInterface.h"
#include "concurrency/OSThread.h"

#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
#include <mutex>
#endif

/**
* A mesh aware router that supports multiple interfaces.
*/
Expand Down Expand Up @@ -90,12 +86,6 @@ class Router : protected concurrency::OSThread
before us */
uint32_t rxDupe = 0, txRelayCanceled = 0;

#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
// Used by router_fuzzer.cpp to detect when the Router has finished processing a packet.
// See LLVMFuzzerTestOneInput in router_fuzzer.cpp & Router::runOnce for how this is used.
std::mutex inProgressLock;
#endif

protected:
friend class RoutingModule;

Expand Down

0 comments on commit aaa88b8

Please sign in to comment.