From ffa1d6f40bca0b035fc7f5e563160ebb67199da7 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: https://gitlab.com/gpsd/gpsd/-/commit/ffa1d6f40bca0b035fc7f5e563160ebb67199da7 [thomas: backport remove hunk added in https://gitlab.com/gpsd/gpsd/-/commit/247a89136cce6bfada24d811271defb0f0dbc89d] Signed-off-by: Thomas Perale --- gpsd/packet.c | 64 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/gpsd/packet.c b/gpsd/packet.c index 368dc551d..fdb3f25d0 100644 --- a/gpsd/packet.c +++ b/gpsd/packet.c @@ -1020,18 +1020,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) { @@ -1073,6 +1077,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; @@ -1255,14 +1260,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: @@ -1389,11 +1409,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) { @@ -1429,6 +1449,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; @@ -1575,16 +1596,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; @@ -1896,6 +1917,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 } -- GitLab