The MSVCRT `strtoul()` function resets `errno` to zero upon success. On such
a system, `libiberty_vprintf_buffer_size()` can clobber `errno` like this:

   MINGW64 ~
   $ ld nonexistent.file
   C:\MSYS64\mingw64\bin\ld.exe: cannot find nonexistent.file: No error

libiberty/ChangeLog:

        * vprintf-support.c (do_strtoul): New function.
        (libiberty_vprintf_buffer_size): Replace `strtoul` with `do_strtoul`.

Signed-off-by: LIU Hao <[email protected]>
---
 libiberty/vprintf-support.c | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/libiberty/vprintf-support.c b/libiberty/vprintf-support.c
index 5a998fbf4ae0..905e86b0437c 100644
--- a/libiberty/vprintf-support.c
+++ b/libiberty/vprintf-support.c
@@ -27,6 +27,7 @@ Floor, Boston, MA 02110-1301, USA.  */
 # define va_copy(d,s)  __va_copy((d),(s))
 #endif
 #include <stdio.h>
+#include <errno.h>
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
@@ -37,6 +38,21 @@ extern unsigned long strtoul ();
 #endif
 #include "libiberty.h"
 +static inline unsigned long
+do_strtoul (const char *str, char **endptr, int base)
+  {
+#ifdef _WIN32
+    /* The MSVCRT `strtoul()` function resets `errno` to zero upon success.
+       We must preserve it across this call.  */
+    int saved_errno = errno;
+#endif
+    long value = strtoul (str, endptr, base);
+#ifdef _WIN32
+    errno = saved_errno;
+#endif
+    return value;
+  }
+
 int
 libiberty_vprintf_buffer_size (const char *format, va_list args)
 {
@@ -65,7 +81,7 @@ libiberty_vprintf_buffer_size (const char *format, va_list 
args)
              total_width += abs (va_arg (ap, int));
            }
          else
-           total_width += strtoul (p, (char **) &p, 10);
+           total_width += do_strtoul (p, (char **) &p, 10);
          if (*p == '.')
            {
              ++p;
@@ -75,7 +91,7 @@ libiberty_vprintf_buffer_size (const char *format, va_list 
args)
                  total_width += abs (va_arg (ap, int));
                }
              else
-             total_width += strtoul (p, (char **) &p, 10);
+               total_width += do_strtoul (p, (char **) &p, 10);
            }
          do
            {
--
2.53.0

From c46c0aa863f6e880de1fb861c05e9f18621ba38e Mon Sep 17 00:00:00 2001
From: LIU Hao <[email protected]>
Date: Mon, 9 Feb 2026 21:44:07 +0800
Subject: [PATCH] libiberty: Preserve `errno` across calls to
 `libiberty_vprintf_buffer_size()`

The MSVCRT `strtoul()` function resets `errno` to zero upon success. On such
a system, `libiberty_vprintf_buffer_size()` could clobber `errno` like this:

   MINGW64 ~
   $ ld nonexistent.file
   C:\MSYS64\mingw64\bin\ld.exe: cannot find nonexistent.file: No error

libiberty/ChangeLog:

        * vprintf-support.c (do_strtoul): New function.
        (libiberty_vprintf_buffer_size): Replace `strtoul` with `do_strtoul`.

Signed-off-by: LIU Hao <[email protected]>
---
 libiberty/vprintf-support.c | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/libiberty/vprintf-support.c b/libiberty/vprintf-support.c
index 5a998fbf4ae0..905e86b0437c 100644
--- a/libiberty/vprintf-support.c
+++ b/libiberty/vprintf-support.c
@@ -27,6 +27,7 @@ Floor, Boston, MA 02110-1301, USA.  */
 # define va_copy(d,s)  __va_copy((d),(s))
 #endif
 #include <stdio.h>
+#include <errno.h>
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
@@ -37,6 +38,21 @@ extern unsigned long strtoul ();
 #endif
 #include "libiberty.h"
 
+static inline unsigned long
+do_strtoul (const char *str, char **endptr, int base)
+  {
+#ifdef _WIN32
+    /* The MSVCRT `strtoul()` function resets `errno` to zero upon success.
+       We must preserve it across this call.  */
+    int saved_errno = errno;
+#endif
+    long value = strtoul (str, endptr, base);
+#ifdef _WIN32
+    errno = saved_errno;
+#endif
+    return value;
+  }
+
 int
 libiberty_vprintf_buffer_size (const char *format, va_list args)
 {
@@ -65,7 +81,7 @@ libiberty_vprintf_buffer_size (const char *format, va_list 
args)
              total_width += abs (va_arg (ap, int));
            }
          else
-           total_width += strtoul (p, (char **) &p, 10);
+           total_width += do_strtoul (p, (char **) &p, 10);
          if (*p == '.')
            {
              ++p;
@@ -75,7 +91,7 @@ libiberty_vprintf_buffer_size (const char *format, va_list 
args)
                  total_width += abs (va_arg (ap, int));
                }
              else
-             total_width += strtoul (p, (char **) &p, 10);
+               total_width += do_strtoul (p, (char **) &p, 10);
            }
          do
            {
-- 
2.53.0

Attachment: OpenPGP_signature.asc
Description: OpenPGP digital signature

Reply via email to