From 879236ea04cf1044c44a303b3e0d65725ea1180e Mon Sep 17 00:00:00 2001 From: ladyada Date: Mon, 14 Sep 2026 17:29:24 -0400 Subject: [PATCH] Validate NMEA checksum digits and line ending Co-authored-by: Limor Fried --- extras/tests/checksum/checksum.ino | 38 ++++++++++++++++++++++++++++++ src/NMEA_parse.cpp | 14 +++++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 extras/tests/checksum/checksum.ino diff --git a/extras/tests/checksum/checksum.ino b/extras/tests/checksum/checksum.ino new file mode 100644 index 0000000..f902a28 --- /dev/null +++ b/extras/tests/checksum/checksum.ino @@ -0,0 +1,38 @@ +#include + +Adafruit_GPS GPS; +bool testsPassed = false; + +void setup() { + Serial.begin(115200); + // Wait briefly for the Serial Monitor on native USB boards. + while (!Serial && millis() < 5000) { + delay(10); + } + delay(250); + Serial.println("Adafruit GPS checksum regression"); + + char good[] = "$GPGGA,120009,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*40"; + if (!GPS.parse(good)) { + Serial.println("FAIL: valid checksum was rejected"); + return; + } + Serial.println("PASS: valid checksum was accepted"); + + char bad[] = "$GPGGA,120009,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*4Z"; + if (GPS.parse(bad)) { + Serial.println("FAIL: invalid checksum character was accepted"); + return; + } + Serial.println("PASS: invalid checksum character was rejected"); + testsPassed = true; +} + +void loop() { + if (testsPassed) { + Serial.println("PASS: checksum regression"); + } else { + Serial.println("FAIL: checksum regression"); + } + delay(2000); +} diff --git a/src/NMEA_parse.cpp b/src/NMEA_parse.cpp index 71ae51c..45d3e42 100644 --- a/src/NMEA_parse.cpp +++ b/src/NMEA_parse.cpp @@ -21,6 +21,7 @@ /**************************************************************************/ #include +#include /**************************************************************************/ /*! @@ -575,8 +576,17 @@ bool Adafruit_GPS::check(char *nmea) { if (*ast != '*') return false; // there is no asterisk else { - uint16_t sum = parseHex(*(ast + 1)) * 16; // extract checksum - sum += parseHex(*(ast + 2)); + if (!isxdigit((unsigned char)ast[1]) || !isxdigit((unsigned char)ast[2])) + return false; // checksum must contain two hexadecimal digits + char *end = ast + 3; + if (*end == '\r') + end++; + if (*end == '\n') + end++; + if (*end != '\0') + return false; // only a line ending may follow the checksum + uint16_t sum = parseHex(toupper((unsigned char)ast[1])) * 16; + sum += parseHex(toupper((unsigned char)ast[2])); char *p = nmea; // check checksum for (char *p1 = p + 1; p1 < ast; p1++) sum ^= *p1;