From b3abe9d49d8fcc3f824d74a5c2cdcc30838f5904 Mon Sep 17 00:00:00 2001
From: "Gary E. Miller" <gem@rellim.com>
Date: Tue, 2 Dec 2025 19:36:04 -0800
Subject: [PATCH] drivers/driver_nmea2000.c: Fix issue 356, skyview buffer
 overrun.

CVE: CVE-2025-67268
Upstream-Status: Backport [https://github.com/ntpsec/gpsd/commit/dc966aa74c075d0a6535811d98628625cbfbe3f4]
Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
---
 drivers/driver_nmea2000.c | 123 ++++++++++++++++++++++----------------
 1 file changed, 71 insertions(+), 52 deletions(-)

diff --git a/drivers/driver_nmea2000.c b/drivers/driver_nmea2000.c
index 66959f0..70462b3 100644
--- a/drivers/driver_nmea2000.c
+++ b/drivers/driver_nmea2000.c
@@ -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;l1<len;l1++) {
+        for (l1 = 0; l1 < len; l1++) {
             if (((l1 % 20) == 0) && (l1 != 0)) {
                 GPSD_LOG(LOG_IO, &context->errout, "%s\n", bu);
                 ptr = 0;
@@ -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;l1<session->gpsdata.satellites_visible;l1++) {
-        int    svt;
-        double azi, elev, snr;
-
-        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;
+    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;
 
-        svt   = (int)(bu[3+12*l1+11] & 0x0f);
+        int svt   = (int)(bu[offset + 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;
         }
     }
