Skip to content

Commit 310c096

Browse files
authored
Merge pull request networkupstools#2963 from sebastiankuttnig/issue-2948
common.c: VA copies with buffer upsizing, upsdrvquery.c: str handling fixes
2 parents ffeeb47 + cd7b6e4 commit 310c096

File tree

6 files changed

+103
-9
lines changed

6 files changed

+103
-9
lines changed

NEWS.adoc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ https://github.com/networkupstools/nut/milestone/9
5959
`DRIVERLIST` variables inspected by `configure` script, rather than
6060
having all their names hard-coded like before, led to inability to
6161
`configure --with-drivers=dummy-ups`. [#2825, #2927, fixed by PR #2929]
62+
* A problem noted with `upsdrvquery` (since NUT v2.8.1) message logging
63+
at high debug verbosity levels (5+) with very large blocks of content
64+
has exposed a deficiency in variable-argument handling, and specifically
65+
adaptive resizing of the output buffer or truncation of logged inputs
66+
(which is something NUT code tried to do since the beginning of time),
67+
and could lead to "segmentation fault" crashes on some platforms.
68+
[issue #2948, PR #2963]
6269

6370
- common code:
6471
* Revised common `writepid()` to use `altpidpath()` as location for the

common/common.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3206,9 +3206,25 @@ static void vupslog(int priority, const char *fmt, va_list va, int use_strerror)
32063206
* or the calling methods should check it against their
32073207
* "fmt_dynamic" expectations). */
32083208
do {
3209+
#ifdef HAVE_VA_COPY_VARIANT
3210+
va_list va_snprintf;
3211+
3212+
/* va_copy() to avoid mangling on re-use (see issue #2948),
3213+
* this lets us retry safely vsnprintf() with the VA copies */
3214+
va_copy(va_snprintf, va);
3215+
ret = vsnprintf(buf, bufsize, fmt, va_snprintf);
3216+
va_end(va_snprintf);
3217+
#else
3218+
/* Without va_copy(), we cannot safely retry vsnprintf()
3219+
* and will accept truncation if the buffer is too small */
32093220
ret = vsnprintf(buf, bufsize, fmt, va);
3221+
#endif
32103222

32113223
if ((ret < 0) || ((uintmax_t)ret >= (uintmax_t)bufsize)) {
3224+
/* Not building the below block on systems without va_copy(),
3225+
* otherwise consumed VAs would get re-used & mangled on retries.
3226+
* Instead, we want fall-through directly to the truncation message. */
3227+
#ifdef HAVE_VA_COPY_VARIANT
32123228
/* Try to adjust bufsize until we can print the
32133229
* whole message. Note that standards only require
32143230
* up to 4095 bytes to be manageable in printf-like
@@ -3261,6 +3277,7 @@ static void vupslog(int priority, const char *fmt, va_list va, int use_strerror)
32613277
/* Arbitrary limit, gotta stop somewhere */
32623278
if (bufsize > LARGEBUF * 64) {
32633279
vupslog_too_long:
3280+
#endif
32643281
if (syslog_is_disabled()) {
32653282
fprintf(stderr, "vupslog: vsnprintf needed "
32663283
"more than %" PRIuSIZE " bytes; logged "

configure.ac

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,6 +1433,54 @@ AS_IF([test x"${ac_cv_func_strdup}" = xyes],
14331433
[AC_MSG_WARN([Required C library routine strdup not found; try adding -D_POSIX_C_SOURCE=200112L])]
14341434
)
14351435

1436+
AC_CACHE_CHECK([for va_copy(dest,src)],
1437+
[ac_cv_have_va_copy],
1438+
[
1439+
AX_RUN_OR_LINK_IFELSE(
1440+
[AC_LANG_PROGRAM(
1441+
[[#include <stdarg.h>
1442+
void check_va_copy(const char *fmt, ...) {
1443+
va_list ap1, ap2;
1444+
va_start(ap1, fmt);
1445+
va_copy(ap2, ap1);
1446+
va_end(ap1);
1447+
va_end(ap2);
1448+
}]],
1449+
[[check_va_copy("%s", "test");]])],
1450+
[ac_cv_have_va_copy=yes],
1451+
[ac_cv_have_va_copy=no])
1452+
])
1453+
1454+
AS_IF([test x"$ac_cv_have_va_copy" = xyes],
1455+
[AC_DEFINE([HAVE_VA_COPY], [1], [defined if va_copy(dest,src) is available])])
1456+
1457+
AC_CACHE_CHECK([for __va_copy(dest,src)],
1458+
[ac_cv_have___va_copy],
1459+
[
1460+
AX_RUN_OR_LINK_IFELSE(
1461+
[AC_LANG_PROGRAM(
1462+
[[#include <stdarg.h>
1463+
void check___va_copy(const char *fmt, ...) {
1464+
va_list ap1, ap2;
1465+
va_start(ap1, fmt);
1466+
__va_copy(ap2, ap1);
1467+
va_end(ap1);
1468+
va_end(ap2);
1469+
}]],
1470+
[[check___va_copy("%s", "test");]])],
1471+
[ac_cv_have___va_copy=yes],
1472+
[ac_cv_have___va_copy=no])
1473+
])
1474+
1475+
AS_IF([test x"$ac_cv_have___va_copy" = xyes],
1476+
[AC_DEFINE([HAVE___VA_COPY], [1], [defined if __va_copy(dest,src) is available])])
1477+
1478+
AS_IF([test x"$ac_cv_have_va_copy" = xyes -o x"$ac_cv_have___va_copy" = xyes],
1479+
[AC_DEFINE([HAVE_VA_COPY_VARIANT], [1], [defined if va_copy or __va_copy is available])])
1480+
1481+
AS_IF([test x"$ac_cv_have_va_copy" != xyes -a x"$ac_cv_have___va_copy" != xyes],
1482+
[AC_MSG_WARN([No va_copy(dest,src) or __va_copy(dest,src) available - may truncate oversized logs])])
1483+
14361484
AC_CHECK_HEADER([sys/select.h],
14371485
[AC_DEFINE([HAVE_SYS_SELECT_H], [1],
14381486
[Define to 1 if you have <sys/select.h>.])])

docs/nut.dict

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
personal_ws-1.1 en 3502 utf-8
1+
personal_ws-1.1 en 3503 utf-8
22
AAC
33
AAS
44
ABI
@@ -2902,6 +2902,7 @@ reposurgeon
29022902
repotec
29032903
req
29042904
resetter
2905+
resizing
29052906
resolv
29062907
restoreconf
29072908
resync

drivers/upsdrvquery.c

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -363,12 +363,21 @@ ssize_t upsdrvquery_read_timeout(udq_pipe_conn_t *conn, struct timeval tv) {
363363
ret = -1;
364364
#endif /* WIN32 */
365365

366-
upsdebugx(ret > 0 ? 5 : 6,
367-
"%s: received %" PRIiMAX " bytes from driver socket: %s",
368-
__func__, (intmax_t)ret, (ret > 0 ? conn->buf : "<null>"));
369-
if (ret > 0 && conn->buf[0] == '\0')
370-
upsdebug_hex(5, "payload starts with zero byte: ",
371-
conn->buf, ((size_t)ret > sizeof(conn->buf) ? sizeof(conn->buf) : (size_t)ret));
366+
if (ret > 0) {
367+
size_t len = (size_t)ret > sizeof(conn->buf) ? sizeof(conn->buf) : (size_t)ret;
368+
369+
upsdebugx(5, "%s: received %" PRIiMAX " bytes from driver socket: %.*s",
370+
__func__, (intmax_t)ret, (int)len, conn->buf);
371+
372+
if (conn->buf[0] == '\0') {
373+
upsdebug_hex(5, "payload starts with zero byte: ",
374+
conn->buf, len);
375+
}
376+
} else {
377+
upsdebugx(6, "%s: received %" PRIiMAX " bytes from driver socket: <null>",
378+
__func__, (intmax_t)ret);
379+
}
380+
372381
return ret;
373382
}
374383

@@ -483,7 +492,11 @@ ssize_t upsdrvquery_prepare(udq_pipe_conn_t *conn, struct timeval tv) {
483492
}
484493

485494
/* Check that we can have a civilized dialog --
486-
* nope, this one is for network protocol */
495+
* nope, this one is for network protocol
496+
*
497+
* FIXME: strcmp(conn->buf, "ON") -> buf is NOT null terminated!
498+
* The above FIXME is not the reason this block is/was commented out.
499+
*/
487500
/*
488501
if (upsdrvquery_write(conn, "GET TRACKING\n") < 0)
489502
goto socket_error;
@@ -745,7 +758,8 @@ ssize_t upsdrvquery_oneshot_conn(
745758
}
746759

747760
if (buf) {
748-
snprintf(buf, bufsz, "%s", conn->buf);
761+
size_t len = strnlen(conn->buf, sizeof(conn->buf));
762+
snprintf(buf, bufsz, "%.*s", (int)len, conn->buf);
749763
}
750764

751765
finish:

include/common.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,13 @@ int vsnprintfcat(char *dst, size_t size, const char *fmt, va_list ap);
546546
int snprintfcat(char *dst, size_t size, const char *fmt, ...)
547547
__attribute__ ((__format__ (__printf__, 3, 4)));
548548

549+
/* Define a missing va_copy using __va_copy, if available: */
550+
#ifndef HAVE_VA_COPY
551+
# ifdef HAVE___VA_COPY
552+
# define va_copy(dest, src) __va_copy(dest, src)
553+
# endif
554+
#endif
555+
549556
/* Mitigate the inherent insecurity of dynamically constructed formatting
550557
* strings vs. a fixed vararg list with its amounts and types of variables
551558
* printed by this or that method and pre-compiled in the program.

0 commit comments

Comments
 (0)