Harden against crafted-packet crashes + adversarial fuzzing (#10862)
Audit and fuzzing of the RF-packet decode -> dispatch -> display/phone paths for the "crash a node or phone with a crafted packet" surface, beyond the XEdDSA authenticity work. Crash fixes (reproduced under AddressSanitizer / UBSan): - GeoCoord::latLongToUTM/latLongToMGRS read fixed letter tables out of bounds on extreme latitude_i/longitude_i from a received Position, and narrowed out-of-range easting/northing doubles to unsigned (float-cast-overflow UB). Clamp the UTM zone, the easting/northing narrowing, and the band/col/row indices. Regression: test_geocoord_extreme_coords_no_oob. - EnvironmentTelemetry/AirQualityTelemetry render attacker floats via String(float), which on nRF52/RP2040/STM32/portduino formats into a fixed char[33] (dtostrf) and overflows near FLT_MAX. Clamp the rendered metrics via UnitConversions::displaySafeFloat (finite + magnitude <= 1e9), unit-tested in test_type_conversions. Defense-in-depth + robustness: - TraceRouteModule::printRoute: fix an snr_back[-1] OOB read (wrong count in the guard) and stop formatting the INT8_MIN "unknown SNR" sentinel as a dB value. - WaypointModule/NodeDB: sanitize untrusted strings before the OLED renderer and the phone-facing ClientNotification (belt-and-suspenders vs PB_VALIDATE_UTF8). - MeshService::sendToPhone: withhold NODEINFO/WAYPOINT packets whose nested string won't cleanly decode, protecting strict phone protobuf decoders without affecting mesh relay. Tests: new test_fuzz_decode (protobuf decode + UTF-8 sanitizer fuzz) and test_fuzz_packets (perhapsDecode / module-handler / traceroute / phone-gate fuzz), all under AddressSanitizer; native-suite-count 25 -> 27. Full suite 515/515 green.
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
#include "Channels.h"
|
||||
#include "GeoCoord.h"
|
||||
#include "PositionPrecision.h"
|
||||
#include "TestUtil.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <unity.h>
|
||||
|
||||
@@ -207,6 +209,26 @@ static void test_cryptoKeyIsPublic_invalidKeyIsNotPublic()
|
||||
TEST_ASSERT_FALSE(cryptoKeyIsPublic(makeCryptoKey(nullptr, -1)));
|
||||
}
|
||||
|
||||
// Regression for out-of-bounds indexing in GeoCoord's UTM/MGRS conversion on extreme
|
||||
// latitude_i/longitude_i that arrive in a received Position (raw int32, unvalidated on decode).
|
||||
// Pre-fix, latitude_i = INT32_MAX made latLongToUTM read latBands[36] on a 21-char string
|
||||
// (stack-buffer-overflow at GeoCoord.cpp:128, an AddressSanitizer abort); extreme longitude produced
|
||||
// a negative UTM zone feeding the MGRS letter tables. The fix clamps the zone/band/col/row indices.
|
||||
// This exercises the fix under the coverage env's ASan.
|
||||
static void test_geocoord_extreme_coords_no_oob()
|
||||
{
|
||||
const int32_t vals[] = {INT32_MIN, INT32_MAX, INT32_MIN + 1, INT32_MAX - 1, 0, 1, -1, 900000000, -900000000, // +/-90 deg
|
||||
1800000000, -1800000000, // +/-180 deg
|
||||
2000000000, -2000000000, 123456789, -123456789};
|
||||
const size_t n = sizeof(vals) / sizeof(vals[0]);
|
||||
for (size_t i = 0; i < n; i++)
|
||||
for (size_t j = 0; j < n; j++) {
|
||||
GeoCoord g(vals[i], vals[j], 0); // ctor -> setCoords() -> UTM/MGRS/OSGR/OLC
|
||||
// Surviving every extreme pair (no ASan fault) means the index clamps hold.
|
||||
TEST_ASSERT_EQUAL_INT32(vals[i], g.getLatitude());
|
||||
}
|
||||
}
|
||||
|
||||
void setUp(void) {}
|
||||
|
||||
void tearDown(void) {}
|
||||
@@ -232,6 +254,7 @@ void setup()
|
||||
RUN_TEST(test_cryptoKeyIsPublic_strongKeyIsPrivate);
|
||||
RUN_TEST(test_cryptoKeyIsPublic_aes256KeyIsPrivate);
|
||||
RUN_TEST(test_cryptoKeyIsPublic_invalidKeyIsNotPublic);
|
||||
RUN_TEST(test_geocoord_extreme_coords_no_oob);
|
||||
exit(UNITY_END());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user