curlx: add local snprintf() helper that always nul-terminates (Windows)

Make the helper use `vsnprintf()` internally on all supported Windows
toolchains (dropping `_snprintf()` and `snprintf()`), ensure to
nul-terminate. Omit the return value to avoid complexity.

Use the helper from `mprintf.c` / `out_double()`, from tests/server code
and the tests/server-specific build of `curlx_inet_ntop()`,
`curlx_strerror()` functions. In the single call (in tests) where the
returned length was used previously, determine it with `strlen()`.

Refs:
https://github.com/libssh2/libssh2/blob/libssh2-1.11.1/src/misc.c#L57-L79
https://learn.microsoft.com/cpp/c-runtime-library/reference/snprintf-snprintf-snprintf-l-snwprintf-snwprintf-l
https://learn.microsoft.com/cpp/c-runtime-library/reference/vsnprintf-vsnprintf-vsnprintf-l-vsnwprintf-vsnwprintf-l

Assisted-by: Jay Satiro
Follow-up to fa8bd1cc09 #20761
Follow-up to 8ab468c8aa #15997

Closes #20765
This commit is contained in:
Viktor Szakats 2026-02-27 16:43:16 +01:00
parent b83ade783d
commit 64f28b8f88
No known key found for this signature in database
8 changed files with 76 additions and 20 deletions

View file

@ -32,6 +32,7 @@ LIB_CURLX_CFILES = \
curlx/inet_pton.c \
curlx/multibyte.c \
curlx/nonblock.c \
curlx/snprintf.c \
curlx/strcopy.c \
curlx/strdup.c \
curlx/strerr.c \

View file

@ -90,7 +90,7 @@
/* Disable Visual Studio warnings: 4127 "conditional expression is constant" */
#pragma warning(disable:4127)
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS /* for _snprintf(), getenv(), sscanf() */
#define _CRT_SECURE_NO_WARNINGS /* for getenv(), sscanf() */
#endif
#endif /* _MSC_VER */

49
lib/curlx/snprintf.c Normal file
View file

@ -0,0 +1,49 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curlx/snprintf.h"
#ifdef _WIN32
#include <stdarg.h>
/* Simplified wrapper for the Windows platform to use the correct symbol and
ensuring null-termination. Omit returning a length to keep it simple. */
void curlx_win32_snprintf(char *buf, size_t maxlen, const char *fmt, ...)
{
va_list ap;
if(!maxlen)
return;
va_start(ap, fmt);
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif
/* !checksrc! disable BANNEDFUNC 1 */
(void)vsnprintf(buf, maxlen, fmt, ap);
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
buf[maxlen - 1] = 0;
va_end(ap);
}
#endif /* _WIN32 */

View file

@ -1,3 +1,5 @@
#ifndef HEADER_CURLX_SNPRINTF_H
#define HEADER_CURLX_SNPRINTF_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@ -21,12 +23,18 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
/* Raw snprintf() for curlx */
#ifdef _WIN32
void curlx_win32_snprintf(char *buf, size_t maxlen, const char *fmt, ...)
CURL_PRINTF(3, 4);
#endif
#ifdef WITHOUT_LIBCURL /* when built for the test servers */
#if defined(_MSC_VER) && (_MSC_VER < 1900) /* adjust for old MSVC */
#define SNPRINTF _snprintf
#ifdef _WIN32
#define SNPRINTF curlx_win32_snprintf
#else
#define SNPRINTF snprintf
#endif
@ -34,3 +42,4 @@
#include <curl/mprintf.h>
#define SNPRINTF curl_msnprintf
#endif /* WITHOUT_LIBCURL */
#endif /* HEADER_CURLX_SNPRINTF_H */

View file

@ -26,6 +26,7 @@
#include "curlx/dynbuf.h"
#include "curl_printf.h"
#include "curlx/strparse.h"
#include "curlx/snprintf.h" /* for curlx_win32_snprintf() */
#define BUFFSIZE 326 /* buffer for long-to-str and float-to-str calcs, should
fit negative DBL_MAX (317 letters) */
@ -671,29 +672,23 @@ static bool out_double(void *userp,
/* NOTE NOTE NOTE!! Not all sprintf implementations return number of
output characters */
#ifdef HAVE_SNPRINTF
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif
#ifdef _WIN32
curlx_win32_snprintf(work, BUFFSIZE, formatbuf, dnum);
#elif defined(HAVE_SNPRINTF)
/* !checksrc! disable BANNEDFUNC 1 */
/* !checksrc! disable LONGLINE */
/* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */
snprintf(work, BUFFSIZE, formatbuf, dnum);
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
#ifdef _WIN32
/* Old versions of the Windows CRT do not terminate the snprintf output
buffer if it reaches the max size so we do that here. */
work[BUFFSIZE - 1] = 0;
#endif
#elif defined(_MSC_VER) && (_MSC_VER < 1900)
_snprintf(work, BUFFSIZE, formatbuf, dnum);
work[BUFFSIZE - 1] = 0;
#else
/* float and double outputs do not work without snprintf support */
work[0] = 0;
#endif
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
DEBUGASSERT(strlen(work) < BUFFSIZE);
while(*work) {

View file

@ -40,6 +40,7 @@ CURLX_C = \
../../lib/curlx/inet_pton.c \
../../lib/curlx/multibyte.c \
../../lib/curlx/nonblock.c \
../../lib/curlx/snprintf.c \
../../lib/curlx/strcopy.c \
../../lib/curlx/strerr.c \
../../lib/curlx/strparse.c \

View file

@ -64,9 +64,9 @@ extern const struct entry_s s_entries[];
#include <curlx/curlx.h>
/* adjust for old MSVC */
#if defined(_MSC_VER) && (_MSC_VER < 1900)
# define snprintf _snprintf
#ifdef _WIN32
#include <curlx/snprintf.h>
#define snprintf curlx_win32_snprintf
#endif
#ifdef _WIN32

View file

@ -606,8 +606,9 @@ static int validate_access(struct testcase *test,
if(!strncmp("verifiedserver", filename, 14)) {
char weare[128];
size_t count = snprintf(weare, sizeof(weare), "WE ROOLZ: %ld\r\n",
(long)our_getpid());
size_t count;
snprintf(weare, sizeof(weare), "WE ROOLZ: %ld\r\n", (long)our_getpid());
count = strlen(weare);
logmsg("Are-we-friendly question received");
test->buffer = curlx_strdup(weare);