[packages/net-snmp] Rel 7. Fix CVE-2026-89147 (SMUX DoS), TSM stack overflow and SMUX use-after-free.

arekm arekm at pld-linux.org
Fri Oct 9 11:20:33 CEST 2026


commit 26682ad929a289131c77dca0ab0ad9d2c9d3d4f6
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date:   Fri Oct 9 11:19:55 2026 +0200

    Rel 7. Fix CVE-2026-89147 (SMUX DoS), TSM stack overflow and SMUX use-after-free.

 net-snmp-CVE-2026-89147.patch        | 330 +++++++++++++++++++++++++++++++++++
 net-snmp-smux-blocking-peers.patch   |  38 ++++
 net-snmp-smux-select-list-full.patch |  16 ++
 net-snmp-smux-uaf.patch              |  22 +++
 net-snmp-tsm-engineid-overflow.patch |  26 +++
 net-snmp.spec                        |  15 +-
 6 files changed, 446 insertions(+), 1 deletion(-)
---
diff --git a/net-snmp.spec b/net-snmp.spec
index 76402b1..5770446 100644
--- a/net-snmp.spec
+++ b/net-snmp.spec
@@ -25,7 +25,7 @@ Summary(ru.UTF-8):	Набор утилит для протокола SNMP от U
 Summary(uk.UTF-8):	Набір утиліт для протоколу SNMP від UC-Davis
 Name:		net-snmp
 Version:	5.9.5.2
-Release:	6
+Release:	7
 License:	BSD-like
 Group:		Networking/Daemons
 Source0:	https://downloads.sourceforge.net/net-snmp/%{name}-%{version}.tar.gz
@@ -54,6 +54,14 @@ Patch12:	%{name}-TCP_STATS_CACHE_TIMEOUT.patch
 Patch13:	%{name}-logging.patch
 Patch14:	%{name}-Remove-U64-typedef.patch
 Patch15:	1314610.patch
+# https://github.com/net-snmp/net-snmp/issues/1134
+Patch16:	%{name}-CVE-2026-89147.patch
+Patch17:	%{name}-smux-blocking-peers.patch
+Patch18:	%{name}-smux-select-list-full.patch
+# https://github.com/net-snmp/net-snmp/commit/1d9f04ad83ca5b0140b35bb6995637c6fc6d39b7
+Patch19:	%{name}-tsm-engineid-overflow.patch
+# https://github.com/net-snmp/net-snmp/issues/1098
+Patch20:	%{name}-smux-uaf.patch
 URL:		http://www.net-snmp.org/
 BuildRequires:	autoconf >= 2.63
 BuildRequires:	automake
@@ -467,6 +475,11 @@ SNMP dla trzech wersji tego protokołu (SNMPv3, SNMPv2c, SNMPv1).
 %patch -P13 -p1
 %patch -P14 -p1
 %patch -P15 -p1
+%patch -P16 -p1
+%patch -P17 -p1
+%patch -P18 -p1
+%patch -P19 -p1
+%patch -P20 -p1
 
 %{__sed} -E -i -e '1s,#!\s*/usr/bin/env\s+perl(\s|$),#!%{__perl}\1,' \
 	perl/SNMP/examples/pingmib.pl
diff --git a/net-snmp-CVE-2026-89147.patch b/net-snmp-CVE-2026-89147.patch
new file mode 100644
index 0000000..eccc9cc
--- /dev/null
+++ b/net-snmp-CVE-2026-89147.patch
@@ -0,0 +1,330 @@
+SMUX: make the accepted peer handshake non-blocking (CVE-2026-89147)
+https://github.com/net-snmp/net-snmp/issues/1134
+Backport of upstream ad843e83da1c229847ae7884dc08311fad680389 to 5.9.
+
+--- net-snmp-5.9.5.2.orig/agent/mibgroup/smux/smux.c
++++ net-snmp-5.9.5.2/agent/mibgroup/smux/smux.c
+@@ -63,6 +63,7 @@
+ 
+ #include <net-snmp/net-snmp-includes.h>
+ #include <net-snmp/agent/net-snmp-agent-includes.h>
++#include <net-snmp/library/snmpSocketBaseDomain.h>
+ #include <net-snmp/library/tools.h>
+ 
+ #include "smux.h"
+@@ -92,6 +93,8 @@ static void     smux_send_close(int, int);
+ static void     smux_list_detach(smux_reg **, smux_reg *);
+ static void     smux_replace_active(smux_reg *, smux_reg *);
+ static void     smux_peer_cleanup(int);
++static int      smux_peer_is_authenticated(int);
++static void     smux_auth_timeout_cb(unsigned int, void *);
+ static int      smux_auth_peer(oid *, size_t, char *, int);
+ static int      smux_build(u_char, long, oid *,
+                            size_t *, u_char, u_char *, size_t, u_char *,
+@@ -623,23 +626,47 @@ var_smux_write(int action,
+ }
+ 
+ 
++static int
++smux_peer_is_authenticated(int sd)
++{
++    int i;
++
++    for (i = 0; i < nauths; i++)
++        if (Auths[i]->sa_active_fd == sd)
++            return 1;
++
++    return 0;
++}
++
++static void
++smux_auth_timeout_cb(unsigned int clientreg, void *clientarg)
++{
++    int fd = (int)(intptr_t)clientarg;
++    int i, found = 0;
++
++    for (i = 0; i < smux_snmp_select_list_get_length(); i++) {
++        if (smux_snmp_select_list_get_SD_from_List(i) == fd) {
++            found = 1;
++            break;
++        }
++    }
++    if (found && !smux_peer_is_authenticated(fd)) {
++        DEBUGMSGTL(("smux",
++                    "[smux_auth_timeout] closing idle unauthenticated fd %d\n",
++                    fd));
++        close(fd);
++        smux_snmp_select_list_del(fd);
++    }
++}
++
+ int
+ smux_accept(int sd)
+ {
+-    u_char          data[SMUXMAXPKTSIZE], *ptr, type;
+     struct sockaddr_in in_socket;
+-    struct timeval  tv;
+-    int             fail, fd;
++    int             fd;
+     socklen_t       alen;
+-    int             length;
+-    size_t          len;
+ 
+     alen = sizeof(struct sockaddr_in);
+-    /*
+-     * this may be too high 
+-     */
+-    tv.tv_sec = 5;
+-    tv.tv_usec = 0;
+ 
+     /*
+      * connection request 
+@@ -649,49 +676,76 @@ smux_accept(int sd)
+     if ((fd = (int) accept(sd, (struct sockaddr *) &in_socket, &alen)) < 0) {
+         snmp_log_perror("[smux_accept] accept failed");
+         return -1;
+-    } else {
+-        DEBUGMSGTL(("smux", "[smux_accept] accepted fd %d from %s:%d\n",
+-                 fd, inet_ntoa(in_socket.sin_addr),
+-                 ntohs(in_socket.sin_port)));
+-        if (npeers + 1 == SMUXMAXPEERS) {
+-            snmp_log(LOG_ERR,
+-                     "[smux_accept] denied peer on fd %d, limit %d reached",
+-                     fd, SMUXMAXPEERS);
+-            close(fd);
+-            return -1;
+-        }
++    }
++
++    netsnmp_set_non_blocking_mode(fd, TRUE);
++    DEBUGMSGTL(("smux", "[smux_accept] accepted fd %d from %s:%d\n",
++             fd, inet_ntoa(in_socket.sin_addr),
++             ntohs(in_socket.sin_port)));
++    if (npeers + 1 == SMUXMAXPEERS) {
++        snmp_log(LOG_ERR,
++                 "[smux_accept] denied peer on fd %d, limit %d reached",
++                 fd, SMUXMAXPEERS);
++        close(fd);
++        return -1;
++    }
++
++    snmp_alarm_register(5, 0, smux_auth_timeout_cb, (void *)(intptr_t)fd);
++
++    return fd;
++}
++
++int
++smux_process(int fd)
++{
++    int             length, tmp_length;
++    u_char          data[SMUXMAXPKTSIZE];
++    u_char          type, *ptr;
++    size_t          packet_len, len;
++    int             fail;
+ 
++    if (!smux_peer_is_authenticated(fd)) {
+         /*
+-         * now block for an OpenPDU 
++         * Unauthenticated peer: read OpenPDU non-blockingly.
+          */
+-        do
+-        {
+-           length = recvfrom(fd, (char *) data, SMUXMAXPKTSIZE, 0, NULL, NULL);
+-        }
+-        while((length == -1) && ((errno == EINTR) || (errno == EAGAIN)));
++        do {
++            length = recvfrom(fd, (char *) data, SMUXMAXPKTSIZE, 0, NULL, NULL);
++        } while (length == -1 && errno == EINTR);
+ 
+-        if (length <= 0) {
++        if (length < 0) {
++            if (errno == EAGAIN ||
++                (EAGAIN != EWOULDBLOCK && errno == EWOULDBLOCK))
++                return 0;
++            DEBUGMSGTL(("smux",
++                        "[smux_process] recvfrom failed on unauthenticated fd %d\n",
++                        fd));
++            close(fd);
++            return -1;
++        }
++        if (length == 0) {
+             DEBUGMSGTL(("smux",
+-                        "[smux_accept] peer on fd %d died or timed out\n",
++                        "[smux_process] peer on fd %d closed connection before auth\n",
+                         fd));
+             close(fd);
+             return -1;
+         }
++
+         /*
+-         * try to authorize him 
++         * Try to authorize peer.
+          */
+         ptr = data;
+         len = length;
+         if ((ptr = asn_parse_header(ptr, &len, &type)) == NULL) {
+             smux_send_close(fd, SMUXC_PACKETFORMAT);
+             close(fd);
+-            DEBUGMSGTL(("smux", "[smux_accept] peer on %d sent bad open", fd));
++            DEBUGMSGTL(("smux", "[smux_process] peer on %d sent bad open\n",
++                        fd));
+             return -1;
+         } else if (type != (u_char) SMUX_OPEN) {
+             smux_send_close(fd, SMUXC_PROTOCOLERROR);
+             close(fd);
+             DEBUGMSGTL(("smux",
+-                        "[smux_accept] peer on %d did not send open: (%d)\n",
++                        "[smux_process] peer on %d did not send open: (%d)\n",
+                         fd, type));
+             return -1;
+         }
+@@ -700,61 +754,48 @@ smux_accept(int sd)
+             smux_send_close(fd, SMUXC_AUTHENTICATIONFAILURE);
+             close(fd);
+             DEBUGMSGTL(("smux",
+-                        "[smux_accept] peer on %d failed authentication\n",
++                        "[smux_process] peer on %d failed authentication\n",
+                         fd));
+             return -1;
+         }
+ 
+-        /*
+-         * he's OK 
+-         */
+-#ifdef SO_RCVTIMEO
+-        if (setsockopt
+-            (fd, SOL_SOCKET, SO_RCVTIMEO, (void *) &tv, sizeof(tv)) < 0) {
+-            DEBUGMSGTL(("smux",
+-                        "[smux_accept] setsockopt(SO_RCVTIMEO) failed fd %d\n",
+-                        fd));
+-            snmp_log_perror("smux_accept: setsockopt SO_RCVTIMEO");
+-        }
+-#endif
+         npeers++;
+-        DEBUGMSGTL(("smux", "[smux_accept] fd %d\n", fd));
++        DEBUGMSGTL(("smux", "[smux_process] authenticated peer on fd %d\n", fd));
+ 
+         /*
+-         * Process other PDUs already read, e.g. a registerRequest. 
++         * Process other PDUs already read, e.g. a registerRequest.
+          */
+         len = length - (ptr - data);
+-        if (smux_pdu_process(fd, ptr, len) < 0) {
+-            /*
+-             * Easy come, easy go.  Clean-up is already done. 
+-             */
+-            return -1;
++        if (len > 0) {
++            if (smux_pdu_process(fd, ptr, len) < 0) {
++                return -1;
++            }
+         }
++        return 0;
+     }
+-    return fd;
+-}
+-
+-int
+-smux_process(int fd)
+-{
+-    int             length, tmp_length;
+-    u_char          data[SMUXMAXPKTSIZE];
+-    u_char          type, *ptr;
+-    size_t          packet_len;
+ 
+-    do
+-    {
++    /*
++     * Authenticated peer: process incoming data.
++     */
++    do {
+        length = recvfrom(fd, (char *) data, SMUXMAXPKTSIZE, MSG_PEEK, NULL,
+                          NULL);
+-    }
+-    while((length == -1) && ((errno == EINTR) || (errno == EAGAIN)));
++    } while (length == -1 && errno == EINTR);
+ 
+-    if (length <= 0)
+-    {
+-       if (length < 0)
+-           snmp_log_perror("[smux_process] peek failed");
+-       smux_peer_cleanup(fd);
+-       return -1;
++    if (length < 0) {
++        if (errno == EAGAIN ||
++            (EAGAIN != EWOULDBLOCK && errno == EWOULDBLOCK))
++            return 0;
++        snmp_log_perror("[smux_process] peek failed");
++        smux_peer_cleanup(fd);
++        return -1;
++    }
++    if (length == 0) {
++        DEBUGMSGTL(("smux",
++                    "[smux_process] peer on fd %d died or disconnected\n",
++                    fd));
++        smux_peer_cleanup(fd);
++        return -1;
+     }
+ 
+     /*
+@@ -762,10 +803,12 @@ smux_process(int fd)
+      */
+     packet_len = length;
+     ptr = asn_parse_header(data, &packet_len, &type);
+-    if (ptr == NULL)
++    if (ptr == NULL) {
++        smux_peer_cleanup(fd);
+         return -1;
++    }
+     packet_len += (ptr - data);
+-    if (length > packet_len) {
++    if ((size_t)length > packet_len) {
+         /*
+          * set length to receive only the first packet 
+          */
+@@ -773,18 +816,16 @@ smux_process(int fd)
+     }
+ 
+     tmp_length = length;
+-    do
+-    {
++    do {
+        length = tmp_length;
+        length = recvfrom(fd, (char *) data, length, 0, NULL, NULL);
+-    }
+-    while((length == -1) && ((errno == EINTR) || (errno == EAGAIN)));
++    } while (length == -1 && errno == EINTR);
+ 
+     if (length <= 0) {
+-        /*
+-         * the peer went away, close this descriptor 
+-         * * and delete it from the list
+-         */
++        if (length < 0 &&
++            (errno == EAGAIN ||
++             (EAGAIN != EWOULDBLOCK && errno == EWOULDBLOCK)))
++            return 0;
+         DEBUGMSGTL(("smux",
+                     "[smux_process] peer on fd %d died or timed out\n",
+                     fd));
+@@ -1907,11 +1948,6 @@ smux_peer_cleanup(int sd)
+         }
+     }
+ 
+-    /*
+-     * decrement the peer count 
+-     */
+-    npeers--;
+-
+     /*
+      * make his auth available again 
+      */
+@@ -1922,6 +1958,8 @@ smux_peer_cleanup(int sd)
+             snprint_objid(oid_name, sizeof(oid_name), Auths[i]->sa_oid,
+                           Auths[i]->sa_oid_len);
+             DEBUGMSGTL(("smux", "peer disconnected: %s\n", oid_name));
++            if (npeers > 0)
++                npeers--;
+         }
+     }
+ }
diff --git a/net-snmp-smux-blocking-peers.patch b/net-snmp-smux-blocking-peers.patch
new file mode 100644
index 0000000..2f3fae4
--- /dev/null
+++ b/net-snmp-smux-blocking-peers.patch
@@ -0,0 +1,38 @@
+SMUX: keep authenticated peer sockets blocking, as before the CVE-2026-89147
+fix; smux_snmp_process() retries recv() on EAGAIN and would busy-loop on a
+non-blocking socket while waiting for every peer response.
+
+--- net-snmp-5.9.5.2.orig/agent/mibgroup/smux/smux.c
++++ net-snmp-5.9.5.2/agent/mibgroup/smux/smux.c
+@@ -703,6 +703,10 @@ smux_process(int fd)
+     u_char          type, *ptr;
+     size_t          packet_len, len;
+     int             fail;
++    struct timeval  tv;
++
++    tv.tv_sec = 5;
++    tv.tv_usec = 0;
+ 
+     if (!smux_peer_is_authenticated(fd)) {
+         /*
+@@ -759,6 +763,20 @@ smux_process(int fd)
+             return -1;
+         }
+ 
++        /*
++         * smux_snmp_process() waits for the peer's response in a loop that
++         * retries on EAGAIN; a non-blocking socket turns that into a busy loop.
++         */
++        netsnmp_set_non_blocking_mode(fd, FALSE);
++#ifdef SO_RCVTIMEO
++        if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (void *) &tv,
++                       sizeof(tv)) < 0) {
++            DEBUGMSGTL(("smux",
++                        "[smux_process] setsockopt(SO_RCVTIMEO) failed fd %d\n",
++                        fd));
++            snmp_log_perror("smux_process: setsockopt SO_RCVTIMEO");
++        }
++#endif
+         npeers++;
+         DEBUGMSGTL(("smux", "[smux_process] authenticated peer on fd %d\n", fd));
+ 
diff --git a/net-snmp-smux-select-list-full.patch b/net-snmp-smux-select-list-full.patch
new file mode 100644
index 0000000..23d70f4
--- /dev/null
+++ b/net-snmp-smux-select-list-full.patch
@@ -0,0 +1,16 @@
+snmpd: close an accepted SMUX socket that does not fit in the select list;
+it is never read nor closed. Unauthenticated connections wait in that list,
+so a client can fill it and leak one descriptor per further connection.
+
+--- net-snmp-5.9.5.2.orig/agent/snmpd.c
++++ net-snmp-5.9.5.2/agent/snmpd.c
+@@ -1309,7 +1309,8 @@ receive(void)
+                  */
+                 if (NETSNMP_LARGE_FD_ISSET(smux_listen_sd, &readfds)) {
+                     if ((sd = smux_accept(smux_listen_sd)) >= 0) {
+-                        smux_snmp_select_list_add(sd);
++                        if (!smux_snmp_select_list_add(sd))
++                            close(sd);
+                     }
+                 }
+             }
diff --git a/net-snmp-smux-uaf.patch b/net-snmp-smux-uaf.patch
new file mode 100644
index 0000000..19e8518
--- /dev/null
+++ b/net-snmp-smux-uaf.patch
@@ -0,0 +1,22 @@
+From 6ed87acd275cac891667031e5d3e58d51adf1873 Mon Sep 17 00:00:00 2001
+From: Wes Hardaker <opensource at hardakers.net>
+Date: Sun, 31 May 2026 06:19:12 -0700
+Subject: [PATCH] fix #1098 as suggested by Aisle Research to prevent smux
+ crashes
+
+---
+ agent/mibgroup/smux/smux.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/agent/mibgroup/smux/smux.c b/agent/mibgroup/smux/smux.c
+index 078c4c6fa0..d89532beeb 100644
+--- a/agent/mibgroup/smux/smux.c
++++ b/agent/mibgroup/smux/smux.c
+@@ -1104,6 +1104,7 @@ smux_rreq_process(int sd, u_char * ptr, size_t * len)
+              * unregister the mib 
+              */
+             unregister_mib(rptr->sr_name, rptr->sr_name_len);
++            rptr->reginfo = NULL;
+             /*
+              * find a replacement 
+              */
diff --git a/net-snmp-tsm-engineid-overflow.patch b/net-snmp-tsm-engineid-overflow.patch
new file mode 100644
index 0000000..4525243
--- /dev/null
+++ b/net-snmp-tsm-engineid-overflow.patch
@@ -0,0 +1,26 @@
+From 1d9f04ad83ca5b0140b35bb6995637c6fc6d39b7 Mon Sep 17 00:00:00 2001
+From: Bart Van Assche <bvanassche at acm.org>
+Date: Tue, 6 Jan 2026 06:45:19 -0700
+Subject: [PATCH] libsnmp: Fix a stack buffer overflow
+
+Do not read past the end of the stack array ourEngineID.
+
+Fixes: https://issues.oss-fuzz.com/issues/473194539
+---
+ snmplib/snmptsm.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/snmplib/snmptsm.c b/snmplib/snmptsm.c
+index d7a9333a72..22b64d02b5 100644
+--- a/snmplib/snmptsm.c
++++ b/snmplib/snmptsm.c
+@@ -416,7 +416,8 @@ tsm_process_in_msg(struct snmp_secmod_incoming_params *parms)
+     ourEngineID_len =
+         snmpv3_get_engineID((u_char*) ourEngineID, ourEngineID_len);
+     netsnmp_assert_or_return(ourEngineID_len != 0 &&
+-                             ourEngineID_len <= *parms->secEngineIDLen,
++                             ourEngineID_len <= *parms->secEngineIDLen &&
++                             *parms->secEngineIDLen <= sizeof(ourEngineID),
+                              SNMPERR_GENERR);
+     memcpy(parms->secEngineID, ourEngineID, *parms->secEngineIDLen);
+ 
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/packages/net-snmp.git/commitdiff/26682ad929a289131c77dca0ab0ad9d2c9d3d4f6



More information about the pld-cvs-commit mailing list