File fix-upsd-buffer-overflow.patch of Package nut
From 16d7f45dea569efb6dc93c83a54de521683d5c74 Mon Sep 17 00:00:00 2001
From: Jeremy Cooper <jeremy.gthb@baymoo.org>
Date: Wed, 23 Apr 2025 15:20:31 -0700
Subject: [PATCH 1/8] Fix issue #2914: upsd misprint/buffer overflow.
---
server/upsd.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/server/upsd.c b/server/upsd.c
index a40b74d6b5..58f87a4fd2 100644
--- a/server/upsd.c
+++ b/server/upsd.c
@@ -507,17 +507,13 @@ static void setuptcp(stype_t *server)
}
if (ai->ai_next) {
- char ipaddrbuf[SMALLBUF];
- const char *ipaddr;
- snprintf(ipaddrbuf, sizeof(ipaddrbuf), " as ");
- ipaddr = inet_ntop(ai->ai_family, ai->ai_addr,
- ipaddrbuf + strlen(ipaddrbuf),
- sizeof(ipaddrbuf));
+ const char *ipaddr = inet_ntopW((struct sockaddr_storage *)ai->ai_addr);
upslogx(LOG_WARNING,
- "setuptcp: bound to %s%s but there seem to be "
+ "setuptcp: bound to %s%s%s but there seem to be "
"further (ignored) addresses resolved for this name",
server->addr,
- ipaddr == NULL ? "" : ipaddrbuf);
+ ipaddr == NULL ? "" : " as ",
+ ipaddr == NULL ? "" : ipaddr);
}
server->sock_fd = sock_fd;
From 49a12adf54b826d75e8b984c3ea310bb6d896a2f Mon Sep 17 00:00:00 2001
From: Jim Klimov <jimklimov+nut@gmail.com>
Date: Thu, 24 Apr 2025 09:55:58 +0200
Subject: [PATCH 3/8] server/upsd.c: introduce inet_ntopAI() specifically for
struct addrinfo conversion [#2915]
Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
---
server/upsd.c | 42 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/server/upsd.c b/server/upsd.c
index 58f87a4fd2..6a70e6b1a1 100644
--- a/server/upsd.c
+++ b/server/upsd.c
@@ -186,6 +186,46 @@ static const char *inet_ntopW (struct sockaddr_storage *s)
}
}
+static const char *inet_ntopAI(struct addrinfo *ai)
+{
+ /* Note: below we manipulate copies of ai - cannot cast into
+ * specific structure type pointers right away because:
+ * error: cast from 'struct sockaddr *' to 'struct sockaddr_storage *'
+ * increases required alignment from 2 to 8
+ */
+ /* https://stackoverflow.com/a/29147085/4715872
+ * obviously INET6_ADDRSTRLEN is expected to be larger
+ * than INET_ADDRSTRLEN, but this may be required in case
+ * if for some unexpected reason IPv6 is not supported, and
+ * INET6_ADDRSTRLEN is defined as 0
+ * but this is not very likely and I am aware of no cases of
+ * this in practice (editor)
+ */
+ static char addrstr[(INET6_ADDRSTRLEN > INET_ADDRSTRLEN ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN) + 1];
+
+ if (!ai || !ai->ai_addr) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ addrstr[0] = '\0';
+ switch (ai->ai_family) {
+ case AF_INET: {
+ struct sockaddr_in addr_in;
+ memcpy(&addr_in, ai->ai_addr, sizeof(addr_in));
+ return inet_ntop(AF_INET, &(addr_in.sin_addr), addrstr, INET_ADDRSTRLEN);
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 addr_in6;
+ memcpy(&addr_in6, ai->ai_addr, sizeof(addr_in6));
+ return inet_ntop(AF_INET6, &(addr_in6.sin6_addr), addrstr, INET6_ADDRSTRLEN);
+ }
+ default:
+ errno = EAFNOSUPPORT;
+ return NULL;
+ }
+}
+
/* return a pointer to the named ups if possible */
upstype_t *get_ups_ptr(const char *name)
{
@@ -507,7 +547,7 @@ static void setuptcp(stype_t *server)
}
if (ai->ai_next) {
- const char *ipaddr = inet_ntopW((struct sockaddr_storage *)ai->ai_addr);
+ const char *ipaddr = inet_ntopAI(ai);
upslogx(LOG_WARNING,
"setuptcp: bound to %s%s%s but there seem to be "
"further (ignored) addresses resolved for this name",
From db2cc95ddb884aa4c98875a5b9a034edb7f64849 Mon Sep 17 00:00:00 2001
From: Jim Klimov <jimklimov+nut@gmail.com>
Date: Thu, 24 Apr 2025 09:56:32 +0200
Subject: [PATCH 4/8] server/upsd.c: add a sanity check into inet_ntopW()
[#2915]
Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
---
server/upsd.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/server/upsd.c b/server/upsd.c
index 6a70e6b1a1..61539326d0 100644
--- a/server/upsd.c
+++ b/server/upsd.c
@@ -170,10 +170,15 @@ static int reload_flag = 0, exit_flag = 0;
# define SERVICE_UNIT_NAME "nut-server.service"
#endif
-static const char *inet_ntopW (struct sockaddr_storage *s)
+static const char *inet_ntopW(struct sockaddr_storage *s)
{
static char str[40];
+ if (!s) {
+ errno = EINVAL;
+ return NULL;
+ }
+
switch (s->ss_family)
{
case AF_INET:
From 4d4e84c7073a49f9787a660132988acd28ef399d Mon Sep 17 00:00:00 2001
From: Jim Klimov <jimklimov+nut@gmail.com>
Date: Thu, 24 Apr 2025 10:13:14 +0200
Subject: [PATCH 5/8] Refactor inet_ntopSS() and inet_ntopAI() into common
code, simplify upsclient and upsd
Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
---
clients/upsclient.c | 29 ++------------------
common/common.c | 64 +++++++++++++++++++++++++++++++++++++++++++++
docs/nut.dict | 6 ++++-
include/common.h | 17 +++++++++---
server/upsd.c | 63 +-------------------------------------------
6 files changed, 90 insertions(+), 94 deletions(-)
diff --git a/clients/upsclient.c b/clients/upsclient.c
index f2773b41d8..67efcf1339 100644
--- a/clients/upsclient.c
+++ b/clients/upsclient.c
@@ -1172,34 +1172,9 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, uint16_t port, int flags
ups->upserror == UPSCLI_ERR_CONNFAILURE &&
ups->syserrno == ETIMEDOUT
) {
- /* https://stackoverflow.com/a/29147085/4715872
- * obviously INET6_ADDRSTRLEN is expected to be larger
- * than INET_ADDRSTRLEN, but this may be required in case
- * if for some unexpected reason IPv6 is not supported, and
- * INET6_ADDRSTRLEN is defined as 0
- * but this is not very likely and I am aware of no cases of
- * this in practice (editor)
- */
- char addrstr[(INET6_ADDRSTRLEN > INET_ADDRSTRLEN ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN) + 1];
- addrstr[0] = '\0';
- switch (ai->ai_family) {
- case AF_INET: {
- struct sockaddr_in addr_in;
- memcpy(&addr_in, ai->ai_addr, sizeof(addr_in));
- inet_ntop(AF_INET, &(addr_in.sin_addr), addrstr, INET_ADDRSTRLEN);
- break;
- }
- case AF_INET6: {
- struct sockaddr_in6 addr_in6;
- memcpy(&addr_in6, ai->ai_addr, sizeof(addr_in6));
- inet_ntop(AF_INET6, &(addr_in6.sin6_addr), addrstr, INET6_ADDRSTRLEN);
- break;
- }
- default:
- break;
- }
+ const char *addrstr = inet_ntopAI(ai);
upslogx(LOG_WARNING, "%s: Connection to host timed out: '%s'",
- __func__, *addrstr ? addrstr : NUT_STRARG(host));
+ __func__, (addrstr && *addrstr) ? addrstr : NUT_STRARG(host));
break;
}
continue;
diff --git a/common/common.c b/common/common.c
index c1ea0d0243..cd0012d3fd 100644
--- a/common/common.c
+++ b/common/common.c
@@ -4485,3 +4485,67 @@ int match_regex_hex(const regex_t *preg, const int n)
return match_regex(preg, buf);
}
#endif /* HAVE_LIBREGEX */
+
+/* NOT THREAD SAFE!
+ * Helpers to convert one IP address to string from different structure types
+ * Return pointer to internal buffer, or NULL and errno upon errors */
+const char *inet_ntopSS(struct sockaddr_storage *s)
+{
+ static char str[40];
+
+ if (!s) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ switch (s->ss_family)
+ {
+ case AF_INET:
+ return inet_ntop (AF_INET, &(((struct sockaddr_in *)s)->sin_addr), str, 16);
+ case AF_INET6:
+ return inet_ntop (AF_INET6, &(((struct sockaddr_in6 *)s)->sin6_addr), str, 40);
+ default:
+ errno = EAFNOSUPPORT;
+ return NULL;
+ }
+}
+
+const char *inet_ntopAI(struct addrinfo *ai)
+{
+ /* Note: below we manipulate copies of ai - cannot cast into
+ * specific structure type pointers right away because:
+ * error: cast from 'struct sockaddr *' to 'struct sockaddr_storage *'
+ * increases required alignment from 2 to 8
+ */
+ /* https://stackoverflow.com/a/29147085/4715872
+ * obviously INET6_ADDRSTRLEN is expected to be larger
+ * than INET_ADDRSTRLEN, but this may be required in case
+ * if for some unexpected reason IPv6 is not supported, and
+ * INET6_ADDRSTRLEN is defined as 0
+ * but this is not very likely and I am aware of no cases of
+ * this in practice (editor)
+ */
+ static char addrstr[(INET6_ADDRSTRLEN > INET_ADDRSTRLEN ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN) + 1];
+
+ if (!ai || !ai->ai_addr) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ addrstr[0] = '\0';
+ switch (ai->ai_family) {
+ case AF_INET: {
+ struct sockaddr_in addr_in;
+ memcpy(&addr_in, ai->ai_addr, sizeof(addr_in));
+ return inet_ntop(AF_INET, &(addr_in.sin_addr), addrstr, INET_ADDRSTRLEN);
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 addr_in6;
+ memcpy(&addr_in6, ai->ai_addr, sizeof(addr_in6));
+ return inet_ntop(AF_INET6, &(addr_in6.sin6_addr), addrstr, INET6_ADDRSTRLEN);
+ }
+ default:
+ errno = EAFNOSUPPORT;
+ return NULL;
+ }
+}
diff --git a/docs/nut.dict b/docs/nut.dict
index 6286067839..7eaacafbe4 100644
--- a/docs/nut.dict
+++ b/docs/nut.dict
@@ -1,4 +1,4 @@
-personal_ws-1.1 en 3458 utf-8
+personal_ws-1.1 en 3462 utf-8
AAC
AAS
ABI
@@ -2206,6 +2206,7 @@ ina
includePath
includedir
inductor
+inet
infos
infoval
inh
@@ -2623,6 +2624,9 @@ nowarn
np
nspr
nss
+ntopAI
+ntopSS
+ntopW
nuget
num
numOfBytesFromUPS
diff --git a/include/common.h b/include/common.h
index 210f7e542e..b7e6fe27ee 100644
--- a/include/common.h
+++ b/include/common.h
@@ -65,11 +65,14 @@
#endif
#ifndef WIN32
-#include <syslog.h>
+# include <netdb.h>
+# include <sys/socket.h>
+# include <arpa/inet.h>
+# include <syslog.h>
#else /* WIN32 */
-#include <winsock2.h>
-#include <windows.h>
-#include <ws2tcpip.h>
+# include <winsock2.h>
+# include <windows.h>
+# include <ws2tcpip.h>
#endif /* WIN32 */
#include <unistd.h> /* useconds_t */
@@ -402,6 +405,12 @@ const char * rootpidpath(void);
/* Die with a standard message if socket filename is too long */
void check_unix_socket_filename(const char *fn);
+/* NOT THREAD SAFE!
+ * Helpers to convert one IP address to string from different structure types
+ * Return pointer to internal buffer, or NULL and errno upon errors */
+const char *inet_ntopSS(struct sockaddr_storage *s);
+const char *inet_ntopAI(struct addrinfo *ai);
+
/* Provide integration for systemd inhibitor interface (where available,
* dummy code otherwise) implementing the pseudo-code example from
* https://systemd.io/INHIBITOR_LOCKS/
diff --git a/server/upsd.c b/server/upsd.c
index 61539326d0..924a0708c3 100644
--- a/server/upsd.c
+++ b/server/upsd.c
@@ -170,67 +170,6 @@ static int reload_flag = 0, exit_flag = 0;
# define SERVICE_UNIT_NAME "nut-server.service"
#endif
-static const char *inet_ntopW(struct sockaddr_storage *s)
-{
- static char str[40];
-
- if (!s) {
- errno = EINVAL;
- return NULL;
- }
-
- switch (s->ss_family)
- {
- case AF_INET:
- return inet_ntop (AF_INET, &(((struct sockaddr_in *)s)->sin_addr), str, 16);
- case AF_INET6:
- return inet_ntop (AF_INET6, &(((struct sockaddr_in6 *)s)->sin6_addr), str, 40);
- default:
- errno = EAFNOSUPPORT;
- return NULL;
- }
-}
-
-static const char *inet_ntopAI(struct addrinfo *ai)
-{
- /* Note: below we manipulate copies of ai - cannot cast into
- * specific structure type pointers right away because:
- * error: cast from 'struct sockaddr *' to 'struct sockaddr_storage *'
- * increases required alignment from 2 to 8
- */
- /* https://stackoverflow.com/a/29147085/4715872
- * obviously INET6_ADDRSTRLEN is expected to be larger
- * than INET_ADDRSTRLEN, but this may be required in case
- * if for some unexpected reason IPv6 is not supported, and
- * INET6_ADDRSTRLEN is defined as 0
- * but this is not very likely and I am aware of no cases of
- * this in practice (editor)
- */
- static char addrstr[(INET6_ADDRSTRLEN > INET_ADDRSTRLEN ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN) + 1];
-
- if (!ai || !ai->ai_addr) {
- errno = EINVAL;
- return NULL;
- }
-
- addrstr[0] = '\0';
- switch (ai->ai_family) {
- case AF_INET: {
- struct sockaddr_in addr_in;
- memcpy(&addr_in, ai->ai_addr, sizeof(addr_in));
- return inet_ntop(AF_INET, &(addr_in.sin_addr), addrstr, INET_ADDRSTRLEN);
- }
- case AF_INET6: {
- struct sockaddr_in6 addr_in6;
- memcpy(&addr_in6, ai->ai_addr, sizeof(addr_in6));
- return inet_ntop(AF_INET6, &(addr_in6.sin6_addr), addrstr, INET6_ADDRSTRLEN);
- }
- default:
- errno = EAFNOSUPPORT;
- return NULL;
- }
-}
-
/* return a pointer to the named ups if possible */
upstype_t *get_ups_ptr(const char *name)
{
@@ -853,7 +792,7 @@ static void client_connect(stype_t *server)
time(&client->last_heard);
- client->addr = xstrdup(inet_ntopW(&csock));
+ client->addr = xstrdup(inet_ntopSS(&csock));
client->tracking = 0;
From e3e9093518fac446dc7558ce36a83ad46e581f14 Mon Sep 17 00:00:00 2001
From: Jim Klimov <jimklimov+nut@gmail.com>
Date: Thu, 24 Apr 2025 10:36:10 +0000
Subject: [PATCH 7/8] include/common.h: fix includes for inet_ntopXX [#2916]
Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
---
include/common.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/common.h b/include/common.h
index b7e6fe27ee..e9eb9a5def 100644
--- a/include/common.h
+++ b/include/common.h
@@ -68,6 +68,7 @@
# include <netdb.h>
# include <sys/socket.h>
# include <arpa/inet.h>
+# include <netinet/in.h>
# include <syslog.h>
#else /* WIN32 */
# include <winsock2.h>
From 862eb7c975dc4da99bd23873d2dc92d16f519981 Mon Sep 17 00:00:00 2001
From: Jim Klimov <jimklimov+nut@gmail.com>
Date: Thu, 24 Apr 2025 11:11:33 +0000
Subject: [PATCH 8/8] Constrain pre-processor visibility and impact of
inet_ntopXX() methods with NUT_WANT_INET_NTOP_XX macro [#2916]
Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
---
clients/upsclient.c | 2 ++
common/common.c | 2 ++
include/common.h | 19 +++++++++++++++----
server/upsd.c | 4 +++-
4 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/clients/upsclient.c b/clients/upsclient.c
index 67efcf1339..3bf2b84546 100644
--- a/clients/upsclient.c
+++ b/clients/upsclient.c
@@ -20,6 +20,8 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define NUT_WANT_INET_NTOP_XX 1
+
#include "config.h" /* safe because it doesn't contain prototypes */
#include "nut_platform.h"
diff --git a/common/common.c b/common/common.c
index cd0012d3fd..8d37881efc 100644
--- a/common/common.c
+++ b/common/common.c
@@ -18,6 +18,8 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define NUT_WANT_INET_NTOP_XX 1
+
#include "common.h"
#include "timehead.h"
diff --git a/include/common.h b/include/common.h
index e9eb9a5def..dce48bc888 100644
--- a/include/common.h
+++ b/include/common.h
@@ -65,10 +65,6 @@
#endif
#ifndef WIN32
-# include <netdb.h>
-# include <sys/socket.h>
-# include <arpa/inet.h>
-# include <netinet/in.h>
# include <syslog.h>
#else /* WIN32 */
# include <winsock2.h>
@@ -76,6 +72,19 @@
# include <ws2tcpip.h>
#endif /* WIN32 */
+#ifdef NUT_WANT_INET_NTOP_XX
+/* We currently have a few consumers who should define this macro before
+ * including common.h, while we do not want to encumber preprocessing the
+ * majority of codebase with these headers and our (thread-unsafe) methods.
+ */
+# ifndef WIN32
+# include <netdb.h>
+# include <sys/socket.h>
+# include <arpa/inet.h>
+# include <netinet/in.h>
+# endif /* WIN32 */
+#endif /* NUT_WANT_INET_NTOP_XX */
+
#include <unistd.h> /* useconds_t */
#ifndef HAVE_USECONDS_T
# define useconds_t unsigned long int
@@ -406,11 +415,13 @@ const char * rootpidpath(void);
/* Die with a standard message if socket filename is too long */
void check_unix_socket_filename(const char *fn);
+#ifdef NUT_WANT_INET_NTOP_XX
/* NOT THREAD SAFE!
* Helpers to convert one IP address to string from different structure types
* Return pointer to internal buffer, or NULL and errno upon errors */
const char *inet_ntopSS(struct sockaddr_storage *s);
const char *inet_ntopAI(struct addrinfo *ai);
+#endif /* NUT_WANT_INET_NTOP_XX */
/* Provide integration for systemd inhibitor interface (where available,
* dummy code otherwise) implementing the pseudo-code example from
diff --git a/server/upsd.c b/server/upsd.c
index 924a0708c3..abc5a28e47 100644
--- a/server/upsd.c
+++ b/server/upsd.c
@@ -5,7 +5,7 @@
2008 Arjen de Korte <adkorte-guest@alioth.debian.org>
2011 - 2012 Arnaud Quette <arnaud.quette.free.fr>
2019 Eaton (author: Arnaud Quette <ArnaudQuette@eaton.com>)
- 2020 - 2024 Jim Klimov <jimklimov+nut@gmail.com>
+ 2020 - 2025 Jim Klimov <jimklimov+nut@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,6 +22,8 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define NUT_WANT_INET_NTOP_XX 1
+
#include "config.h" /* must be the first header */
#include "upsd.h"