From 5bf823ae1355e06478d213911d6d24ac9a2d10c6 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Wed, 10 Dec 2025 14:50:17 +0100 Subject: [PATCH] fix buffer overflow in NMEA2000 driver (CVE-2025-67268) Resolves: CVE-2025-67268 RHEL-138270 --- gpsd-cve-2025-67268.patch | 369 ++++++++++++++++++++++++++++++++++++++ gpsd.spec | 4 + 2 files changed, 373 insertions(+) create mode 100644 gpsd-cve-2025-67268.patch diff --git a/gpsd-cve-2025-67268.patch b/gpsd-cve-2025-67268.patch new file mode 100644 index 0000000..2cf1bde --- /dev/null +++ b/gpsd-cve-2025-67268.patch @@ -0,0 +1,369 @@ +commit c65e1a51d2e33acbba93b4f2c73c595edabc21c0 +Author: Gary E. Miller +Date: Tue Dec 2 19:36:04 2025 -0800 + + drivers/driver_nmea2000.c: Fix issue 356, skyview buffer overrun. + +diff --git a/drivers/driver_nmea2000.c b/drivers/driver_nmea2000.c +index 71e04e134..3e40dc768 100644 +--- a/drivers/driver_nmea2000.c ++++ b/drivers/driver_nmea2000.c +@@ -12,11 +12,11 @@ + * Message contents can be had from canboat/analyzer: + * analyzer -explain + * +- * This file is Copyright 2012 by the GPSD project ++ * This file is Copyright by the GPSD project + * SPDX-License-Identifier: BSD-2-clause + */ + +-#include "../include/gpsd_config.h" /* must be before all includes */ ++#include "../include/gpsd_config.h" // must be before all includes + + #if defined(NMEA2000_ENABLE) + +@@ -68,7 +68,7 @@ typedef struct PGN + + #if LOG_FILE + FILE *logFile = NULL; +-#endif /* of if LOG_FILE */ ++#endif // of if LOG_FILE + + extern bool __attribute__ ((weak)) gpsd_add_device(const char *device_name, + bool flag_nowait); +@@ -89,14 +89,14 @@ static int scale_int(int32_t var, const int64_t factor) + static void print_data(struct gps_context_t *context, + unsigned char *buffer, int len, PGN *pgn) + { +- if ((libgps_debuglevel >= LOG_IO) != 0) { +- int l1, l2, ptr; ++ if (LOG_IO <= libgps_debuglevel) { ++ int l1; + char bu[128]; + +- ptr = 0; +- l2 = sprintf(&bu[ptr], "got data:%6u:%3d: ", pgn->pgn, len); ++ int ptr = 0; ++ int l2 = sprintf(&bu[ptr], "got data:%6u:%3d: ", pgn->pgn, len); + ptr += l2; +- for (l1=0;l1errout, "%s\n", bu); + ptr = 0; +@@ -276,7 +276,7 @@ static gps_mask_t hnd_127258(unsigned char *bu, int len, PGN *pgn, + struct gps_device_t *session) + { + print_data(session->context, bu, len, pgn); +- /* FIXME? Get magnetic variation */ ++ // FIXME? Get magnetic variation + GPSD_LOG(LOG_DATA, &session->context->errout, + "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit); + return(0); +@@ -358,7 +358,7 @@ static gps_mask_t hnd_126992(unsigned char *bu, int len, PGN *pgn, + { + // uint8_t sid; + // uint8_t source; +- uint64_t usecs; /* time in us */ ++ uint64_t usecs; // time in us + + print_data(session->context, bu, len, pgn); + GPSD_LOG(LOG_DATA, &session->context->errout, +@@ -434,6 +434,7 @@ static gps_mask_t hnd_129540(unsigned char *bu, int len, PGN *pgn, + struct gps_device_t *session) + { + int l1; ++ int expected_len; + + print_data(session->context, bu, len, pgn); + GPSD_LOG(LOG_DATA, &session->context->errout, +@@ -441,24 +442,39 @@ static gps_mask_t hnd_129540(unsigned char *bu, int len, PGN *pgn, + + session->driver.nmea2000.sid[2] = bu[0]; + session->gpsdata.satellites_visible = (int)bu[2]; ++ if (MAXCHANNELS <= session->gpsdata.satellites_visible) { ++ // Handle a CVE for overrunning skyview[] ++ GPSD_LOG(LOG_WARN, &session->context->errout, ++ "pgn %6d(%3d): Too many sats %d\n", ++ pgn->pgn, session->driver.nmea2000.unit, ++ session->gpsdata.satellites_visible); ++ session->gpsdata.satellites_visible = MAXCHANNELS; ++ } ++ expected_len = 3 + (12 * session->gpsdata.satellites_visible); ++ if (len != expected_len) { ++ GPSD_LOG(LOG_WARN, &session->context->errout, ++ "pgn %6d(%3d): wrong length %d s/b %d\n", ++ pgn->pgn, session->driver.nmea2000.unit, ++ len, expected_len); ++ return 0; ++ } + + memset(session->gpsdata.skyview, '\0', sizeof(session->gpsdata.skyview)); +- for (l1=0;l1gpsdata.satellites_visible;l1++) { +- int svt; +- double azi, elev, snr; ++ for (l1 = 0; l1 < session->gpsdata.satellites_visible; l1++) { ++ int offset = 3 + (12 * l1); ++ double elev = getles16(bu, offset + 1) * 1e-4 * RAD_2_DEG; ++ double azi = getleu16(bu, offset + 3) * 1e-4 * RAD_2_DEG; ++ double snr = getles16(bu, offset + 5) * 1e-2; + +- elev = getles16(bu, 3+12*l1+1) * 1e-4 * RAD_2_DEG; +- azi = getleu16(bu, 3+12*l1+3) * 1e-4 * RAD_2_DEG; +- snr = getles16(bu, 3+12*l1+5) * 1e-2; ++ int svt = (int)(bu[offset + 11] & 0x0f); + +- svt = (int)(bu[3+12*l1+11] & 0x0f); +- +- session->gpsdata.skyview[l1].elevation = (short) (round(elev)); +- session->gpsdata.skyview[l1].azimuth = (short) (round(azi)); ++ session->gpsdata.skyview[l1].elevation = elev; ++ session->gpsdata.skyview[l1].azimuth = azi; + session->gpsdata.skyview[l1].ss = snr; +- session->gpsdata.skyview[l1].PRN = (short)bu[3+12*l1+0]; ++ session->gpsdata.skyview[l1].PRN = (int16_t)bu[offset]; + session->gpsdata.skyview[l1].used = false; +- if ((svt == 2) || (svt == 5)) { ++ if ((2 == svt) || ++ (5 == svt)) { + session->gpsdata.skyview[l1].used = true; + } + } +@@ -588,7 +604,7 @@ static gps_mask_t hnd_129029(unsigned char *bu, int len, PGN *pgn, + struct gps_device_t *session) + { + gps_mask_t mask; +- uint64_t usecs; /* time in us */ ++ uint64_t usecs; // time in us + + print_data(session->context, bu, len, pgn); + GPSD_LOG(LOG_DATA, &session->context->errout, +@@ -675,7 +691,7 @@ static gps_mask_t hnd_129038(unsigned char *bu, int len, PGN *pgn, + (unsigned int)ais_direction((unsigned int)getleu16(bu, 21), 1.0); + ais->type1.turn = ais_turn_rate((int)getles16(bu, 23)); + ais->type1.status = (unsigned int) ((bu[25] >> 0) & 0x0f); +- ais->type1.maneuver = 0; /* Not transmitted ???? */ ++ ais->type1.maneuver = 0; // Not transmitted ???? + decode_ais_channel_info(bu, len, 163, session); + + return(ONLINE_SET | AIS_SET); +@@ -730,8 +746,9 @@ static gps_mask_t hnd_129039(unsigned char *bu, int len, PGN *pgn, + + /* + * PGN 129040: AIS Class B Extended Position Report ++ * ++ * No test case for this message at the moment + */ +-/* No test case for this message at the moment */ + static gps_mask_t hnd_129040(unsigned char *bu, int len, PGN *pgn, + struct gps_device_t *session) + { +@@ -781,8 +798,8 @@ static gps_mask_t hnd_129040(unsigned char *bu, int len, PGN *pgn, + ais->type19.epfd = (unsigned int) ((bu[23] >> 4) & 0x0f); + ais->type19.dte = (unsigned int) ((bu[52] >> 0) & 0x01); + ais->type19.assigned = (bool) ((bu[52] >> 1) & 0x01); +- for (l=0;ltype19.shipname[l] = (char) bu[32+l]; ++ for (l = 0; l < AIS_SHIPNAME_MAXLEN; l++) { ++ ais->type19.shipname[l] = (char)bu[32+l]; + } + ais->type19.shipname[AIS_SHIPNAME_MAXLEN] = (char) 0; + decode_ais_channel_info(bu, len, 422, session); +@@ -914,7 +931,7 @@ static gps_mask_t hnd_129794(unsigned char *bu, int len, PGN *pgn, + ais->type5.draught = (unsigned int) (getleu16(bu, 51)/10); + ais->type5.dte = (unsigned int) ((bu[73] >> 6) & 0x01); + +- for (l=0,cpy_stop=0;l<7;l++) { ++ for (l = 0, cpy_stop = 0; l < 7; l++) { + char next; + + next = (char) bu[9+l]; +@@ -929,7 +946,7 @@ static gps_mask_t hnd_129794(unsigned char *bu, int len, PGN *pgn, + } + ais->type5.callsign[7] = (char) 0; + +- for (l=0,cpy_stop=0;ltype5.shipname[AIS_SHIPNAME_MAXLEN] = (char) 0; + +- for (l=0,cpy_stop=0;l<20;l++) { ++ for (l = 0, cpy_stop = 0; l < 20; l++) { + char next; + + next = (char) bu[53+l]; +@@ -978,7 +995,7 @@ static gps_mask_t hnd_129794(unsigned char *bu, int len, PGN *pgn, + date2.tm_year+1900, + ais->type5.hour, + ais->type5.minute); +-#endif /* of #if NMEA2000_DEBUG_AIS */ ++#endif // end of #if NMEA2000_DEBUG_AIS + decode_ais_channel_info(bu, len, 592, session); + return(ONLINE_SET | AIS_SET); + } +@@ -988,8 +1005,9 @@ static gps_mask_t hnd_129794(unsigned char *bu, int len, PGN *pgn, + + /* + * PGN 129798: AIS SAR Aircraft Position Report ++ * ++ * No test case for this message at the moment + */ +-/* No test case for this message at the moment */ + static gps_mask_t hnd_129798(unsigned char *bu, int len, PGN *pgn, + struct gps_device_t *session) + { +@@ -1016,8 +1034,8 @@ static gps_mask_t hnd_129798(unsigned char *bu, int len, PGN *pgn, + ais->type9.alt = (unsigned int) (getleu64(bu, 21)/1000000); + ais->type9.regional = (unsigned int) ((bu[29] >> 0) & 0xff); + ais->type9.dte = (unsigned int) ((bu[30] >> 0) & 0x01); +-/* ais->type9.spare = (bu[30] >> 1) & 0x7f; */ +- ais->type9.assigned = 0; /* Not transmitted ???? */ ++// ais->type9.spare = (bu[30] >> 1) & 0x7f; ++ ais->type9.assigned = 0; // Not transmitted ???? + decode_ais_channel_info(bu, len, 163, session); + + return(ONLINE_SET | AIS_SET); +@@ -1028,8 +1046,9 @@ static gps_mask_t hnd_129798(unsigned char *bu, int len, PGN *pgn, + + /* + * PGN 129802: AIS Safety Related Broadcast Message ++ * ++ * No test case for this message at the moment + */ +-/* No test case for this message at the moment */ + static gps_mask_t hnd_129802(unsigned char *bu, int len, PGN *pgn, + struct gps_device_t *session) + { +@@ -1043,8 +1062,8 @@ static gps_mask_t hnd_129802(unsigned char *bu, int len, PGN *pgn, + if (decode_ais_header(session->context, bu, len, ais, 0x3fffffff) != 0) { + int l; + +-/* ais->type14.channel = (bu[ 5] >> 0) & 0x1f; */ +- for (l=0;l<36;l++) { ++// ais->type14.channel = (bu[ 5] >> 0) & 0x1f; ++ for (l = 0; l < 36; l++) { + ais->type14.text[l] = (char) bu[6+l]; + } + ais->type14.text[36] = (char) 0; +@@ -1079,7 +1098,7 @@ static gps_mask_t hnd_129809(unsigned char *bu, int len, PGN *pgn, + "NMEA2000: AIS message 24A from %09u stashed.\n", + ais->mmsi); + +- for (l=0;ltype24.shipname[l] = (char) bu[ 5+l]; + saveptr->shipname[l] = (char) bu[ 5+l]; + } +@@ -1119,12 +1138,12 @@ static gps_mask_t hnd_129810(unsigned char *bu, int len, PGN *pgn, + + ais->type24.shiptype = (unsigned int) ((bu[ 5] >> 0) & 0xff); + +- for (l=0;l<7;l++) { ++ for (l = 0; l < 7; l++) { + ais->type24.vendorid[l] = (char) bu[ 6+l]; + } + ais->type24.vendorid[7] = (char) 0; + +- for (l=0;l<7;l++) { ++ for (l = 0; l < 7; l++) { + ais->type24.callsign[l] = (char) bu[13+l]; + } + ais->type24.callsign[7] = (char )0; +@@ -1158,7 +1177,7 @@ static gps_mask_t hnd_129810(unsigned char *bu, int len, PGN *pgn, + for (i = 0; i < MAX_TYPE24_INTERLEAVE; i++) { + if (session->driver.aivdm.context[0].type24_queue.ships[i].mmsi == + ais->mmsi) { +- for (l=0;ltype24.shipname[l] = + (char)(session->driver.aivdm.context[0].type24_queue.ships[i].shipname[l]); + } +@@ -1566,7 +1585,7 @@ static void find_pgn(struct can_frame *frame, struct gps_device_t *session) + frame->can_id & 0x1ffffff); + if ((frame->can_dlc & 0x0f) > 0) { + int l1; +- for(l1=0;l1<(frame->can_dlc & 0x0f);l1++) { ++ for(l1 = 0; l1 < (frame->can_dlc & 0x0f); l1++) { + (void)fprintf(logFile, "%02x", frame->data[l1]); + } + } +@@ -1591,8 +1610,8 @@ static void find_pgn(struct can_frame *frame, struct gps_device_t *session) + if (!session->driver.nmea2000.unit_valid) { + unsigned int l1, l2; + +- for (l1=0;l1driver.nmea2000.unit = l2; + session->driver.nmea2000.unit_valid = true; +@@ -1641,7 +1660,7 @@ static void find_pgn(struct can_frame *frame, struct gps_device_t *session) + "pgn %6d:%s \n", work->pgn, work->name); + session->driver.nmea2000.workpgn = (void *) work; + session->lexer.outbuflen = frame->can_dlc & 0x0f; +- for (l2=0;l2lexer.outbuflen;l2++) { ++ for (l2 = 0; l2 < session->lexer.outbuflen; l2++) { + session->lexer.outbuffer[l2]= frame->data[l2]; + } + } else if ((frame->data[0] & 0x1f) == 0) { +@@ -1659,7 +1678,7 @@ static void find_pgn(struct can_frame *frame, struct gps_device_t *session) + #endif /* of #if NMEA2000_FAST_DEBUG */ + session->lexer.inbuflen = 0; + session->driver.nmea2000.idx += 1; +- for (l2=2;l2<8;l2++) { ++ for (l2 = 2; l2 < 8; l2++) { + session->lexer.inbuffer[session->lexer.inbuflen++] = + frame->data[l2]; + } +@@ -1668,7 +1687,7 @@ static void find_pgn(struct can_frame *frame, struct gps_device_t *session) + } else if (frame->data[0] == session->driver.nmea2000.idx) { + unsigned int l2; + +- for (l2=1;l2<8;l2++) { ++ for (l2 = 1; l2 < 8; l2++) { + if (session->driver.nmea2000.fast_packet_len > + session->lexer.inbuflen) { + session->lexer.inbuffer[session->lexer.inbuflen++] = +@@ -1689,7 +1708,7 @@ static void find_pgn(struct can_frame *frame, struct gps_device_t *session) + session->driver.nmea2000.workpgn = (void *) work; + session->lexer.outbuflen = + session->driver.nmea2000.fast_packet_len; +- for(l2 = 0;l2 < (unsigned int)session->lexer.outbuflen; ++ for(l2 = 0; l2 < (unsigned int)session->lexer.outbuflen; + l2++) { + session->lexer.outbuffer[l2] = + session->lexer.inbuffer[l2]; +@@ -1792,7 +1811,7 @@ int nmea2000_open(struct gps_device_t *session) + (void)strlcpy(interface_name, session->gpsdata.dev.path + 11, + sizeof(interface_name)); + unit_ptr = NULL; +- for (l=0;ldriver.nmea2000.unit_valid = false; +- for (l=0;ldriver.nmea2000.unit_valid) { + unsigned int l1, l2; + +- for (l1=0;l1driver.nmea2000.unit_valid = false; + session->driver.nmea2000.unit = 0; diff --git a/gpsd.spec b/gpsd.spec index 227ee5e..5dc75c5 100644 --- a/gpsd.spec +++ b/gpsd.spec @@ -25,6 +25,9 @@ Source0: https://download-mirror.savannah.gnu.org/releases/gpsd/%{name}-% Source1: https://github.com/SCons/scons/archive/%{scons_ver}/scons-%{scons_ver}.tar.gz Source11: gpsd.sysconfig +# fix buffer overflow in NMEA2000 driver +Patch1: gpsd-cve-2025-67268.patch + BuildRequires: gcc BuildRequires: dbus-devel BuildRequires: ncurses-devel @@ -162,6 +165,7 @@ This package contains X clients using gpsd. %prep %setup -q -a 1 +%autopatch -p1 # add note to man pages about limited support sed -i ':a;$!{N;ba};s|\(\.SH "[^"]*"\)|.SH "NOTE"\n%{note1}\n%{note2}\n\1|3' \