1616#include " mesh/TypeConversions.h"
1717#include " mesh/mesh-pb-constants.h"
1818
19- namespace
20- {
19+ namespace {
2120constexpr uint32_t nodeId = 0x12345678 ;
2221// Set to true when lateInitVariant finishes. Used to ensure lateInitVariant was called during startup.
2322bool hasBeenConfigured = false ;
@@ -35,135 +34,130 @@ std::condition_variable loopCV;
3534std::thread meshtasticThread;
3635
3736// This exception is thrown when the portuino main thread should exit.
38- class ShouldExitException : public std ::runtime_error
39- {
40- public:
41- using std::runtime_error::runtime_error;
37+ class ShouldExitException : public std ::runtime_error {
38+ public:
39+ using std::runtime_error::runtime_error;
4240};
4341
4442// Start the loop for one test case and wait till the loop has completed. This ensures fuzz
4543// test cases do not overlap with one another. This helps the fuzzer attribute a crash to the
4644// single, currently running, test case.
47- void runLoopOnce ()
48- {
49- realHardware = true ; // Avoids delay(100) within portduino/main.cpp
50- std::unique_lock<std::mutex> lck (loopLock);
51- fuzzerRunning = true ;
52- loopCanRun = true ;
53- loopCV.notify_one ();
54- loopCV.wait (lck, [] { return !loopCanRun && loopIsWaiting; });
45+ void runLoopOnce () {
46+ realHardware = true ; // Avoids delay(100) within portduino/main.cpp
47+ std::unique_lock<std::mutex> lck (loopLock);
48+ fuzzerRunning = true ;
49+ loopCanRun = true ;
50+ loopCV.notify_one ();
51+ loopCV.wait (lck, [] { return !loopCanRun && loopIsWaiting; });
5552}
5653} // namespace
5754
5855// Called in the main Arduino loop function to determine if the loop can delay/sleep before running again.
5956// We use this as a way to block the loop from sleeping and to start the loop function immediately when a
6057// fuzzer input is ready.
61- bool loopCanSleep ()
62- {
63- std::unique_lock<std::mutex> lck (loopLock);
64- loopIsWaiting = true ;
65- loopCV.notify_one ();
66- loopCV.wait (lck, [] { return loopCanRun || loopShouldExit; });
67- loopIsWaiting = false ;
68- if (loopShouldExit)
69- throw ShouldExitException (" exit" );
70- if (!fuzzerRunning)
71- return true ; // The loop can sleep before the fuzzer starts.
72- loopCanRun = false ; // Only run the loop once before waiting again.
73- return false ;
58+ bool loopCanSleep () {
59+ std::unique_lock<std::mutex> lck (loopLock);
60+ loopIsWaiting = true ;
61+ loopCV.notify_one ();
62+ loopCV.wait (lck, [] { return loopCanRun || loopShouldExit; });
63+ loopIsWaiting = false ;
64+ if (loopShouldExit)
65+ throw ShouldExitException (" exit" );
66+ if (!fuzzerRunning)
67+ return true ; // The loop can sleep before the fuzzer starts.
68+ loopCanRun = false ; // Only run the loop once before waiting again.
69+ return false ;
7470}
7571
7672// Called just prior to starting Meshtastic. Allows for setting config values before startup.
77- void lateInitVariant ()
78- {
79- portduino_config.logoutputlevel = level_error;
80- channelFile.channels [0 ] = meshtastic_Channel{
81- .has_settings = true ,
82- .settings =
83- meshtastic_ChannelSettings{
84- .psk = {.size = 1 , .bytes = {/* defaultpskIndex=*/ 1 }},
85- .name = " LongFast" ,
86- .uplink_enabled = true ,
87- .has_module_settings = true ,
88- .module_settings = {.position_precision = 16 },
89- },
90- .role = meshtastic_Channel_Role_PRIMARY,
91- };
92- config.security .admin_key [0 ] = {
93- .size = 32 ,
94- .bytes = {0xcd , 0xc0 , 0xb4 , 0x3c , 0x53 , 0x24 , 0xdf , 0x13 , 0xca , 0x5a , 0xa6 , 0x0c , 0x0d , 0xec , 0x85 , 0x5a ,
95- 0x4c , 0xf6 , 0x1a , 0x96 , 0x04 , 0x1a , 0x3e , 0xfc , 0xbb , 0x8e , 0x33 , 0x71 , 0xe5 , 0xfc , 0xff , 0x3c },
96- };
97- config.security .admin_key_count = 1 ;
98- config.lora .region = meshtastic_Config_LoRaConfig_RegionCode_US;
99- moduleConfig.has_mqtt = true ;
100- moduleConfig.mqtt = meshtastic_ModuleConfig_MQTTConfig{
101- .enabled = true ,
102- .proxy_to_client_enabled = true ,
103- };
104- moduleConfig.has_store_forward = true ;
105- moduleConfig.store_forward = meshtastic_ModuleConfig_StoreForwardConfig{
106- .enabled = true ,
107- .history_return_max = 4 ,
108- .history_return_window = 600 ,
109- .is_server = true ,
110- };
111- meshtastic_Position fixedGPS = meshtastic_Position{
112- .has_latitude_i = true ,
113- .latitude_i = static_cast <uint32_t >(1 * 1e7 ),
114- .has_longitude_i = true ,
115- .longitude_i = static_cast <uint32_t >(3 * 1e7 ),
116- .has_altitude = true ,
117- .altitude = 64 ,
118- .location_source = meshtastic_Position_LocSource_LOC_MANUAL,
119- };
120- nodeDB->setLocalPosition (fixedGPS);
121- config.has_position = true ;
122- config.position .fixed_position = true ;
123- meshtastic_NodeInfoLite *info = nodeDB->getMeshNode (nodeDB->getNodeNum ());
124- info->has_position = true ;
125- info->position = TypeConversions::ConvertToPositionLite (fixedGPS);
126- hasBeenConfigured = true ;
73+ void lateInitVariant () {
74+ portduino_config.logoutputlevel = level_error;
75+ channelFile.channels [0 ] = meshtastic_Channel{
76+ .has_settings = true ,
77+ .settings =
78+ meshtastic_ChannelSettings{
79+ .psk = {.size = 1 , .bytes = {/* defaultpskIndex=*/ 1 }},
80+ .name = " LongFast" ,
81+ .uplink_enabled = true ,
82+ .has_module_settings = true ,
83+ .module_settings = {.position_precision = 16 },
84+ },
85+ .role = meshtastic_Channel_Role_PRIMARY,
86+ };
87+ config.security .admin_key [0 ] = {
88+ .size = 32 ,
89+ .bytes = {0xcd , 0xc0 , 0xb4 , 0x3c , 0x53 , 0x24 , 0xdf , 0x13 , 0xca , 0x5a , 0xa6 , 0x0c , 0x0d , 0xec , 0x85 , 0x5a ,
90+ 0x4c , 0xf6 , 0x1a , 0x96 , 0x04 , 0x1a , 0x3e , 0xfc , 0xbb , 0x8e , 0x33 , 0x71 , 0xe5 , 0xfc , 0xff , 0x3c },
91+ };
92+ config.security .admin_key_count = 1 ;
93+ config.lora .region = meshtastic_Config_LoRaConfig_RegionCode_US;
94+ moduleConfig.has_mqtt = true ;
95+ moduleConfig.mqtt = meshtastic_ModuleConfig_MQTTConfig{
96+ .enabled = true ,
97+ .proxy_to_client_enabled = true ,
98+ };
99+ moduleConfig.has_store_forward = true ;
100+ moduleConfig.store_forward = meshtastic_ModuleConfig_StoreForwardConfig{
101+ .enabled = true ,
102+ .history_return_max = 4 ,
103+ .history_return_window = 600 ,
104+ .is_server = true ,
105+ };
106+ meshtastic_Position fixedGPS = meshtastic_Position{
107+ .has_latitude_i = true ,
108+ .latitude_i = static_cast <uint32_t >(1 * 1e7 ),
109+ .has_longitude_i = true ,
110+ .longitude_i = static_cast <uint32_t >(3 * 1e7 ),
111+ .has_altitude = true ,
112+ .altitude = 64 ,
113+ .location_source = meshtastic_Position_LocSource_LOC_MANUAL,
114+ };
115+ nodeDB->setLocalPosition (fixedGPS);
116+ config.has_position = true ;
117+ config.position .fixed_position = true ;
118+ meshtastic_NodeInfoLite *info = nodeDB->getMeshNode (nodeDB->getNodeNum ());
119+ info->has_position = true ;
120+ info->position = TypeConversions::ConvertToPositionLite (fixedGPS);
121+ hasBeenConfigured = true ;
127122}
128123
129124extern " C" {
130125int portduino_main (int argc, char **argv); // Renamed "main" function from Meshtastic binary.
131126
132127// Start Meshtastic in a thread and wait till it has reached the ON state.
133- int LLVMFuzzerInitialize (int *argc, char ***argv)
134- {
135- portduino_config.maxtophone = 5 ;
128+ int LLVMFuzzerInitialize (int *argc, char ***argv) {
129+ portduino_config.maxtophone = 5 ;
136130
137- meshtasticThread = std::thread ([program = *argv[0 ]]() {
138- char nodeIdStr[12 ];
139- strcpy (nodeIdStr, std::to_string (nodeId).c_str ());
140- int argc = 7 ;
141- char *argv[] = {program, " -d" , " /tmp/meshtastic" , " -h" , nodeIdStr, " -p" , " 0" , nullptr };
142- try {
143- portduino_main (argc, argv);
144- } catch (const ShouldExitException &) {
145- }
146- });
147- std::atexit ([] {
148- {
149- const std::lock_guard<std::mutex> lck (loopLock);
150- loopShouldExit = true ;
151- loopCV.notify_one ();
152- }
153- meshtasticThread.join ();
154- });
131+ meshtasticThread = std::thread ([program = *argv[0 ]]() {
132+ char nodeIdStr[12 ];
133+ strcpy (nodeIdStr, std::to_string (nodeId).c_str ());
134+ int argc = 7 ;
135+ char *argv[] = {program, " -d" , " /tmp/meshtastic" , " -h" , nodeIdStr, " -p" , " 0" , nullptr };
136+ try {
137+ portduino_main (argc, argv);
138+ } catch (const ShouldExitException &) {
139+ }
140+ });
141+ std::atexit ([] {
142+ {
143+ const std::lock_guard<std::mutex> lck (loopLock);
144+ loopShouldExit = true ;
145+ loopCV.notify_one ();
146+ }
147+ meshtasticThread.join ();
148+ });
155149
156- // Wait for startup.
157- for (int i = 1 ; i < 20 ; ++i) {
158- if (powerFSM.getState () == &stateON) {
159- assert (hasBeenConfigured);
160- assert (router);
161- assert (nodeDB);
162- return 0 ;
163- }
164- std::this_thread::sleep_for (std::chrono::seconds (1 ));
150+ // Wait for startup.
151+ for (int i = 1 ; i < 20 ; ++i) {
152+ if (powerFSM.getState () == &stateON) {
153+ assert (hasBeenConfigured);
154+ assert (router);
155+ assert (nodeDB);
156+ return 0 ;
165157 }
166- return 1 ;
158+ std::this_thread::sleep_for (std::chrono::seconds (1 ));
159+ }
160+ return 1 ;
167161}
168162
169163// This is the main entrypoint for the fuzzer (the fuzz target). The fuzzer will provide an array of bytes to be
@@ -173,34 +167,33 @@ int LLVMFuzzerInitialize(int *argc, char ***argv)
173167//
174168// This guide provides best practices for writing a fuzzer target.
175169// https://github.com/google/fuzzing/blob/master/docs/good-fuzz-target.md
176- int LLVMFuzzerTestOneInput (const uint8_t *data, size_t length)
177- {
178- meshtastic_MeshPacket p = meshtastic_MeshPacket_init_default;
179- pb_istream_t stream = pb_istream_from_buffer (data, length);
180- // Ignore any inputs that fail to decode or have fields set that are not transmitted over LoRa.
181- if (!pb_decode (&stream, &meshtastic_MeshPacket_msg, &p) || p.rx_time || p.rx_snr || p.priority || p.rx_rssi || p.delayed ||
182- p.public_key .size || p.next_hop || p.relay_node || p.tx_after )
183- return -1 ; // Reject: The input will not be added to the corpus.
184- if (p.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
185- meshtastic_Data d;
186- stream = pb_istream_from_buffer (p.decoded .payload .bytes , p.decoded .payload .size );
187- if (!pb_decode (&stream, &meshtastic_Data_msg, &d))
188- return -1 ; // Reject: The input will not be added to the corpus.
189- }
170+ int LLVMFuzzerTestOneInput (const uint8_t *data, size_t length) {
171+ meshtastic_MeshPacket p = meshtastic_MeshPacket_init_default;
172+ pb_istream_t stream = pb_istream_from_buffer (data, length);
173+ // Ignore any inputs that fail to decode or have fields set that are not transmitted over LoRa.
174+ if (!pb_decode (&stream, &meshtastic_MeshPacket_msg, &p) || p.rx_time || p.rx_snr || p.priority || p.rx_rssi || p.delayed || p.public_key .size ||
175+ p.next_hop || p.relay_node || p.tx_after )
176+ return -1 ; // Reject: The input will not be added to the corpus.
177+ if (p.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
178+ meshtastic_Data d;
179+ stream = pb_istream_from_buffer (p.decoded .payload .bytes , p.decoded .payload .size );
180+ if (!pb_decode (&stream, &meshtastic_Data_msg, &d))
181+ return -1 ; // Reject: The input will not be added to the corpus.
182+ }
190183
191- // Provide default values for a few fields so the fuzzer doesn't need to guess them.
192- if (p.from == 0 )
193- p.from = nodeDB->getNodeNum ();
194- if (p.to == 0 )
195- p.to = nodeDB->getNodeNum ();
196- static uint32_t packetId = 0 ;
197- if (p.id == 0 )
198- p.id == ++packetId;
199- if (p.pki_encrypted && config.security .admin_key_count )
200- memcpy (&p.public_key , &config.security .admin_key [0 ], sizeof (p.public_key ));
184+ // Provide default values for a few fields so the fuzzer doesn't need to guess them.
185+ if (p.from == 0 )
186+ p.from = nodeDB->getNodeNum ();
187+ if (p.to == 0 )
188+ p.to = nodeDB->getNodeNum ();
189+ static uint32_t packetId = 0 ;
190+ if (p.id == 0 )
191+ p.id == ++packetId;
192+ if (p.pki_encrypted && config.security .admin_key_count )
193+ memcpy (&p.public_key , &config.security .admin_key [0 ], sizeof (p.public_key ));
201194
202- router->enqueueReceivedMessage (packetPool.allocCopy (p));
203- runLoopOnce ();
204- return 0 ; // Accept: The input may be added to the corpus.
195+ router->enqueueReceivedMessage (packetPool.allocCopy (p));
196+ runLoopOnce ();
197+ return 0 ; // Accept: The input may be added to the corpus.
205198}
206199}
0 commit comments