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