On 12/03/2011 12:56, Bruno Haible wrote:
Hi,

On 2005-04-09, Jim noticed that mbswidth() can provoke integer overflow [1].
wcswidth() can do the same in gnulib, but the replacement in coreutils has
a safety check against it. Let's do the same in gnulib:

[1] http://lists.gnu.org/archive/html/bug-gnulib/2005-04/msg00022.html


2011-03-12  Bruno Haible  <[email protected]>

        wcswidth, mbswidth: Avoid integer overflow.
        * lib/wcswidth.c: Include <limits.h>.
        * lib/wcswidth-impl.h (wcswidth): Avoid 'int' overflow.
        * lib/mbswidth.c: Include <limits.h>.
        (mbsnwidth): Avoid 'int' overflow.
        Reported by Jim Meyering.

--- lib/mbswidth.c.orig Sat Mar 12 13:50:16 2011
+++ lib/mbswidth.c      Sat Mar 12 13:50:11 2011
@@ -35,12 +35,14 @@
  /* Get iswcntrl().  */
  #include <wctype.h>
+/* Get INT_MAX. */
+#include <limits.h>
+
  /* Returns the number of columns needed to represent the multibyte
     character string pointed to by STRING.  If a non-printable character
     occurs, and MBSW_REJECT_UNPRINTABLE is specified, -1 is returned.
     With flags = MBSW_REJECT_INVALID | MBSW_REJECT_UNPRINTABLE, this is
-   the multibyte analogue of the wcswidth function.
-   If STRING is not of length < INT_MAX / 2, integer overflow can occur.  */
+   the multibyte analogue of the wcswidth function.  */
  int
  mbswidth (const char *string, int flags)
  {
@@ -50,8 +52,7 @@
  /* Returns the number of columns needed to represent the multibyte
     character string pointed to by STRING of length NBYTES.  If a
     non-printable character occurs, and MBSW_REJECT_UNPRINTABLE is
-   specified, -1 is returned.
-   If NBYTES is not < INT_MAX / 2, integer overflow can occur.  */
+   specified, -1 is returned.  */
  int
  mbsnwidth (const char *string, size_t nbytes, int flags)
  {
@@ -135,11 +136,22 @@
                      w = wcwidth (wc);
                      if (w >= 0)
                        /* A printable multibyte character.  */
-                      width += w;
+                      {
+                        if (w > INT_MAX - width)
+                          goto overflow;
+                        width += w;
+                      }
                      else
                        /* An unprintable multibyte character.  */
                        if (!(flags & MBSW_REJECT_UNPRINTABLE))
-                        width += (iswcntrl (wc) ? 0 : 1);
+                        {
+                          if (!iswcntrl (wc))
+                            {
+                              if (width == INT_MAX)
+                                goto overflow;
+                              width++;
+                            }
+                        }
                        else
                          return -1;
@@ -157,11 +169,25 @@
        unsigned char c = (unsigned char) *p++;
if (isprint (c))
-        width++;
+        {
+          if (width == INT_MAX)
+            goto overflow;
+          width++;
+        }
        else if (!(flags & MBSW_REJECT_UNPRINTABLE))
-        width += (iscntrl (c) ? 0 : 1);
+        {
+          if (!iscntrl (c))
+            {
+              if (width == INT_MAX)
+                goto overflow;
+              width++;
+            }
+        }
        else
          return -1;
      }
    return width;
+
+ overflow:
+  return INT_MAX;
  }
--- lib/wcswidth-impl.h.orig    Sat Mar 12 13:50:16 2011
+++ lib/wcswidth-impl.h Sat Mar 12 13:19:58 2011
@@ -28,6 +28,8 @@
          int width = wcwidth (c);
          if (width < 0)
            goto found_nonprinting;
+        if (width > INT_MAX - count)
+          goto overflow;
          count += width;
        }
      }
@@ -35,4 +37,7 @@
found_nonprinting:
    return -1;
+
+ overflow:
+  return INT_MAX;
  }
--- lib/wcswidth.c.orig Sat Mar 12 13:50:16 2011
+++ lib/wcswidth.c      Sat Mar 12 13:20:10 2011
@@ -20,4 +20,6 @@
  /* Specification.  */
  #include <wchar.h>
+#include <limits.h>
+
  #include "wcswidth-impl.h"


This seems to have missed some overflow cases
(which I noticed when auditing c32width() usage in coreutils/gnulib).

I'll push the attached later to address this.

cheers,
Padraig
From 42db70aecfa176239d0fca14f9661fcc81bb4a64 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email protected]>
Date: Sat, 3 Oct 2026 18:57:39 +0100
Subject: [PATCH] mbswidth: Avoid integer overflow.

* lib/mbswidth.c (mbsnwidth): Avoid 'int' overflow.
---
 ChangeLog      | 5 +++++
 lib/mbswidth.c | 6 ++++++
 2 files changed, 11 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index 7c30897e06..4328e5cd4e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2026-10-03  Pádraig Brady  <[email protected]>
+
+	mbswidth: Avoid integer overflow.
+	* lib/mbswidth.c (mbsnwidth): Avoid 'int' overflow.
+
 2026-10-02  Bruno Haible  <[email protected]>
 
 	tzsanitize: Add more comments.
diff --git a/lib/mbswidth.c b/lib/mbswidth.c
index 6f490f4c80..6e30b84256 100644
--- a/lib/mbswidth.c
+++ b/lib/mbswidth.c
@@ -86,6 +86,8 @@ mbsnwidth (const char *string, size_t nbytes, int flags)
             case 'z': case '{': case '|': case '}': case '~':
               /* These characters are printable ASCII characters.  */
               p++;
+              if (width == INT_MAX)
+                goto overflow;
               width++;
               break;
             default:
@@ -104,6 +106,8 @@ mbsnwidth (const char *string, size_t nbytes, int flags)
                         if (!(flags & MBSW_REJECT_INVALID))
                           {
                             p++;
+                            if (width == INT_MAX)
+                              goto overflow;
                             width++;
                             break;
                           }
@@ -117,6 +121,8 @@ mbsnwidth (const char *string, size_t nbytes, int flags)
                         if (!(flags & MBSW_REJECT_INVALID))
                           {
                             p = plimit;
+                            if (width == INT_MAX)
+                              goto overflow;
                             width++;
                             break;
                           }
-- 
2.55.0

Reply via email to