From 0a0448c7d5dabe0eef940108c6e85de16e45e757 Mon Sep 17 00:00:00 2001 From: "Gary E. Miller" Date: Wed, 3 Dec 2025 19:04:03 -0800 Subject: [PATCH] gpsd/packet.c: Fix integer underflow is malicious Navcom packet Causes DoS. Fix issue 358 CVE: CVE-2025-67269 Upstream-Status: Backport [https://gitlab.com/gpsd/gpsd/-/commit/ffa1d6f40bca0b035fc7f5e563160ebb67199da7] Signed-off-by: Ankur Tyagi --- gpsd/packet.c | 63 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/gpsd/packet.c b/gpsd/packet.c index 8e14a17ff..51c51ced9 100644 --- a/gpsd/packet.c +++ b/gpsd/packet.c @@ -947,18 +947,22 @@ static bool nextstate(struct gps_lexer_t *lexer, unsigned char c) #endif // SIRF_ENABLE || SKYTRAQ_ENABLE #ifdef SIRF_ENABLE case SIRF_LEADER_2: - // first part of length - lexer->length = (size_t) (c << 8); + // first part of length, MSB + lexer->length = (c & 0x7f) << 8; + if (lexer->length > MAX_PACKET_LENGTH) { + lexer->length = 0; + return character_pushback(lexer, GROUND_STATE); + } // else lexer->state = SIRF_LENGTH_1; break; case SIRF_LENGTH_1: // second part of length lexer->length += c + 2; - if (lexer->length <= MAX_PACKET_LENGTH) { - lexer->state = SIRF_PAYLOAD; - } else { + if (lexer->length > MAX_PACKET_LENGTH) { + lexer->length = 0; return character_pushback(lexer, GROUND_STATE); - } + } // else + lexer->state = SIRF_PAYLOAD; break; case SIRF_PAYLOAD: if (0 == --lexer->length) { @@ -1000,6 +1004,7 @@ static bool nextstate(struct gps_lexer_t *lexer, unsigned char c) return character_pushback(lexer, GROUND_STATE); } if (MAX_PACKET_LENGTH < lexer->length) { + lexer->length = 0; return character_pushback(lexer, GROUND_STATE); } lexer->state = SKY_PAYLOAD; @@ -1182,14 +1187,29 @@ static bool nextstate(struct gps_lexer_t *lexer, unsigned char c) } break; case NAVCOM_LEADER_3: + // command ID lexer->state = NAVCOM_ID; break; case NAVCOM_ID: - lexer->length = (size_t)c - 4; + /* Length LSB + * Navcom length includes command ID, length bytes. and checksum. + * So for more than just the payload length. + * Minimum 4 bytes */ + if (4 > c) { + return character_pushback(lexer, GROUND_STATE); + } + lexer->length = c; lexer->state = NAVCOM_LENGTH_1; break; case NAVCOM_LENGTH_1: + // Length USB. Navcom allows payload length up to 65,531 lexer->length += (c << 8); + // don't count ID, length and checksum in payload length + lexer->length -= 4; + if (MAX_PACKET_LENGTH < lexer->length) { + lexer->length = 0; + return character_pushback(lexer, GROUND_STATE); + } // else lexer->state = NAVCOM_LENGTH_2; break; case NAVCOM_LENGTH_2: @@ -1316,11 +1336,11 @@ static bool nextstate(struct gps_lexer_t *lexer, unsigned char c) lexer->length += 2; // checksum // 10 bytes is the length of the Zodiac header // no idea what Zodiac max length really is - if ((MAX_PACKET_LENGTH - 10) >= lexer->length) { - lexer->state = ZODIAC_PAYLOAD; - } else { + if ((MAX_PACKET_LENGTH - 10) < lexer->length) { + lexer->length = 0; return character_pushback(lexer, GROUND_STATE); - } + } // else + lexer->state = ZODIAC_PAYLOAD; break; case ZODIAC_PAYLOAD: if (0 == --lexer->length) { @@ -1356,6 +1376,7 @@ static bool nextstate(struct gps_lexer_t *lexer, unsigned char c) lexer->state = UBX_LENGTH_2; } else { // bad length + lexer->length = 0; return character_pushback(lexer, GROUND_STATE); } break; @@ -1502,16 +1523,16 @@ static bool nextstate(struct gps_lexer_t *lexer, unsigned char c) lexer->state = GEOSTAR_MESSAGE_ID_2; break; case GEOSTAR_MESSAGE_ID_2: - lexer->length = (size_t)c * 4; + lexer->length = c * 4; lexer->state = GEOSTAR_LENGTH_1; break; case GEOSTAR_LENGTH_1: lexer->length += (c << 8) * 4; - if (MAX_PACKET_LENGTH >= lexer->length) { - lexer->state = GEOSTAR_LENGTH_2; - } else { + if (MAX_PACKET_LENGTH < lexer->length) { + lexer->length = 0; return character_pushback(lexer, GROUND_STATE); - } + } // else + lexer->state = GEOSTAR_LENGTH_2; break; case GEOSTAR_LENGTH_2: lexer->state = GEOSTAR_PAYLOAD; @@ -1823,6 +1844,16 @@ static bool nextstate(struct gps_lexer_t *lexer, unsigned char c) #endif // STASH_ENABLE } + /* Catch length overflow. Should not happen. + * length is size_t, so underflow looks like overflow too. */ + if (MAX_PACKET_LENGTH <= lexer->length) { + GPSD_LOG(LOG_WARN, &lexer->errout, + "Too long: %zu state %u %s c x%x\n", + lexer->length, lexer->state, state_table[lexer->state], c); + // exit(255); + lexer->length = 0; + return character_pushback(lexer, GROUND_STATE); + } return true; // no pushback }