[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