striconveh: Ensure that none of the offsets[] are out-of-range

Bruno Haible via Gnulib discussion list <[email protected]>
Newsgroups gmane.comp.lib.gnulib.bugs
Message-ID <6848749.peFUeoqG7q@cagnes>
The mem_iconveh functions is documented to return an offsets[] array:

   If OFFSETS is not NULL, it should point to an array of SRCLEN integers; this
   array is filled with offsets into the result, i.e. the character starting
   at SRC[i] corresponds to the character starting at (*RESULTP)[OFFSETS[i]],
   and other offsets are set to (size_t)(-1).

In a couple of cases, OFFSETS[i] can point to the end of the result string:
  - When the input ends in an invalid character and handler == iconveh_error,
  - When the input is in a stateful encoding and ends in a shift sequence,
  - When the input ends in one or more Unicode LANGUAGE TAG characters.

None of these leads to a bug in libunistring. But it's worth fixing anyway.


2026-08-16  Bruno Haible  <[email protected]>

	striconveh: Ensure that none of the offsets[] are out-of-range.
	Reported by AISLE via Mike Fabian <[email protected]>.
	* lib/striconveh.c (mem_cd_iconveh_internal): Ensure that no offsets[i]
	is == length.
	* tests/test-striconveh.c (main): Adjust expected contents of the
	offsets[] array.
	* lib/striconveh.c (main): Likewise.
	* tests/test-striconveh.c (main): Likewise.
	* tests/test-striconveha.c (main): Likewise.
	* tests/uniconv/test-u8-conv-from-enc.c (main): Likewise.
	* tests/uniconv/test-u16-conv-from-enc.c (main): Likewise.
	* tests/uniconv/test-u32-conv-from-enc.c (main): Likewise.
	* tests/uniconv/test-u8-conv-to-enc.c (main): Likewise.
	* tests/uniconv/test-u16-conv-to-enc.c (main): Likewise.

	striconveh tests: Add some more test cases.
	* tests/test-striconveh.c (main): Add some more test cases for
	mem_iconveh() with offsets.
0001-striconveh-tests-Add-some-more-test-cases.patch (text/x-patch, 4.6 KB)
From e42d012275045cfa68c857fa648ab94c653fea0e Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Sun, 16 Aug 2026 14:34:27 +0200
Subject: [PATCH 1/2] striconveh tests: Add some more test cases.

* tests/test-striconveh.c (main): Add some more test cases for
mem_iconveh() with offsets.
---
 ChangeLog               |  6 +++
 tests/test-striconveh.c | 83 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 89 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index b2e2619314..a82a816666 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2026-08-16  Bruno Haible  <[email protected]>
+
+	striconveh tests: Add some more test cases.
+	* tests/test-striconveh.c (main): Add some more test cases for
+	mem_iconveh() with offsets.
+
 2026-08-16  Bruno Haible  <[email protected]>
 
 	malloc-posix: Fix file list (regression 2026-07-27).
diff --git a/tests/test-striconveh.c b/tests/test-striconveh.c
index ebb0a775c0..d4c45383ad 100644
--- a/tests/test-striconveh.c
+++ b/tests/test-striconveh.c
@@ -1367,6 +1367,89 @@ main ()
         }
     }
 
+# if (defined _LIBICONV_VERSION && !(_LIBICONV_VERSION == 0x10b && defined __APPLE__)) || (defined __GLIBC__ && !defined __UCLIBC__)
+  /* Test conversion from ISO-2022-JP-2 to UTF-8 with no errors,
+     but a trailing shift sequence.  */
+  {
+    static const char input[] = "Sch" "\033$(D+S\033(B" "ner Gru" "\033$(D)N\033(B"; /* "Schöner Gruß" */
+    static const char expected[] = "Sch\303\266ner Gru\303\237";
+    size_t *offsets = new_offsets (strlen (input));
+    char *result = NULL;
+    size_t length = 0;
+    int retval = mem_iconveh (input, strlen (input),
+                              "ISO-2022-JP-2", "UTF-8",
+                              iconveh_question_mark,
+                              offsets,
+                              &result, &length);
+    ASSERT (retval == 0);
+    ASSERT (length == strlen (expected));
+    ASSERT (result != NULL && memeq (result, expected, strlen (expected)));
+    for (size_t i = 0; i < 28; i++)
+      ASSERT (offsets[i] == (i <= 3 ? i :
+                             i == 9 ? 5 :
+                             i >= 13 && i <= 19 ? i - 7 :
+                             i == 25 ? 14 :
+                             (size_t)(-1)));
+    ASSERT (offsets[28] == MAGIC);
+    free (offsets);
+    free (result);
+  }
+
+  /* Test conversion from UTF-8 to ISO-2022-JP-2 with no errors,
+     but a trailing shift sequence.  */
+  {
+    static const char input[] = "Sch\303\266ner Gru\303\237"; /* "Schöner Gruß" */
+    static const char expected[] = "Sch" "\033$(D+S\033(B" "ner Gru" "\033$(D)N\033(B";
+    size_t *offsets = new_offsets (strlen (input));
+    char *result = NULL;
+    size_t length = 0;
+    int retval = mem_iconveh (input, strlen (input),
+                              "UTF-8", "ISO-2022-JP-2",
+                              iconveh_question_mark,
+                              offsets,
+                              &result, &length);
+    ASSERT (retval == 0);
+    ASSERT (length == strlen (expected));
+    ASSERT (result != NULL && memeq (result, expected, strlen (expected)));
+    for (size_t i = 0; i < 14; i++)
+      ASSERT (offsets[i] == (i <= 3 ? i :
+                             i == 5 ? 9 :
+                             i >= 6 && i <= 12 ? i + 7 :
+                             (size_t)(-1)));
+    ASSERT (offsets[14] == MAGIC);
+    free (offsets);
+    free (result);
+  }
+# endif
+
+# if (defined __GLIBC__ && !defined __UCLIBC__)
+  /* Test conversion from UTF-8 to ASCII with no errors,
+     but some discarded characters.  */
+  {
+    static const char input[] = "a\363\240\201\270bc\363\240\201\246\363\240\201\262"; /* "a<U+E0078>bc<U+E0066><U+E0072>" */
+    static const char expected[] = "abc";
+    size_t *offsets = new_offsets (strlen (input));
+    char *result = NULL;
+    size_t length = 0;
+    int retval = mem_iconveh (input, strlen (input),
+                              "UTF-8", "ASCII",
+                              iconveh_question_mark,
+                              offsets,
+                              &result, &length);
+    ASSERT (retval == 0);
+    ASSERT (length == strlen (expected));
+    ASSERT (result != NULL && memeq (result, expected, strlen (expected)));
+    for (size_t i = 0; i < 15; i++)
+      ASSERT (offsets[i] == (i <= 1 ? i :
+                             i == 6 ? 2 :
+                             i == 7 ? 3 :
+                             (size_t)(-1)));
+    ASSERT (offsets[15] == MAGIC);
+    free (offsets);
+    free (result);
+  }
+# endif
+
   /* ------------------------- Test str_iconveh() ------------------------- */
 
   /* Test conversion from ISO-8859-2 to ISO-8859-1 with no errors.  */
-- 
2.53.0
0002-striconveh-Ensure-that-none-of-the-offsets-are-out-o.patch (text/x-patch, 8.1 KB)
From 5f1137d6ba2f64dd9c1cf2eb05f0cac3172f210b Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Sun, 16 Aug 2026 15:21:13 +0200
Subject: [PATCH 2/2] striconveh: Ensure that none of the offsets[] are
 out-of-range.

Reported by AISLE via Mike Fabian <[email protected]>.

* lib/striconveh.c (mem_cd_iconveh_internal): Ensure that no offsets[i]
is == length.
* tests/test-striconveh.c (main): Adjust expected contents of the
offsets[] array.
* lib/striconveh.c (main): Likewise.
* tests/test-striconveh.c (main): Likewise.
* tests/test-striconveha.c (main): Likewise.
* tests/uniconv/test-u8-conv-from-enc.c (main): Likewise.
* tests/uniconv/test-u16-conv-from-enc.c (main): Likewise.
* tests/uniconv/test-u32-conv-from-enc.c (main): Likewise.
* tests/uniconv/test-u8-conv-to-enc.c (main): Likewise.
* tests/uniconv/test-u16-conv-to-enc.c (main): Likewise.
---
 ChangeLog                              | 15 +++++++++++++++
 lib/striconveh.c                       | 17 +++++++++++++++++
 tests/test-striconveh.c                |  6 ++----
 tests/test-striconveha.c               |  3 +--
 tests/uniconv/test-u16-conv-from-enc.c |  1 -
 tests/uniconv/test-u16-conv-to-enc.c   |  2 +-
 tests/uniconv/test-u32-conv-from-enc.c |  1 -
 tests/uniconv/test-u8-conv-from-enc.c  |  1 -
 tests/uniconv/test-u8-conv-to-enc.c    |  2 +-
 9 files changed, 37 insertions(+), 11 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index a82a816666..f4efbc514b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,20 @@
 2026-08-16  Bruno Haible  <[email protected]>
 
+	striconveh: Ensure that none of the offsets[] are out-of-range.
+	Reported by AISLE via Mike Fabian <[email protected]>.
+	* lib/striconveh.c (mem_cd_iconveh_internal): Ensure that no offsets[i]
+	is == length.
+	* tests/test-striconveh.c (main): Adjust expected contents of the
+	offsets[] array.
+	* lib/striconveh.c (main): Likewise.
+	* tests/test-striconveh.c (main): Likewise.
+	* tests/test-striconveha.c (main): Likewise.
+	* tests/uniconv/test-u8-conv-from-enc.c (main): Likewise.
+	* tests/uniconv/test-u16-conv-from-enc.c (main): Likewise.
+	* tests/uniconv/test-u32-conv-from-enc.c (main): Likewise.
+	* tests/uniconv/test-u8-conv-to-enc.c (main): Likewise.
+	* tests/uniconv/test-u16-conv-to-enc.c (main): Likewise.
+
 	striconveh tests: Add some more test cases.
 	* tests/test-striconveh.c (main): Add some more test cases for
 	mem_iconveh() with offsets.
diff --git a/lib/striconveh.c b/lib/striconveh.c
index b345923d4e..cac2ce2d57 100644
--- a/lib/striconveh.c
+++ b/lib/striconveh.c
@@ -976,6 +976,23 @@ mem_cd_iconveh_internal (const char *src, size_t srclen,
   }
 
  done:
+  if (offsets != NULL)
+    {
+      /* Ensure that the last assigned offsets[i] is < length.
+         (We know that the sequence of assigned offsets[i] is strictly
+         monotonically increasing.)  */
+      for (size_t i = srclen; i > 0; )
+        {
+          i--;
+          if (offsets[i] != (size_t)(-1))
+            {
+              if (offsets[i] == length)
+                offsets[i] = (size_t)(-1);
+              break;
+            }
+        }
+    }
+
   /* Now the final memory allocation.  */
   if (result == tmpbuf)
     {
diff --git a/tests/test-striconveh.c b/tests/test-striconveh.c
index d4c45383ad..71d0871e44 100644
--- a/tests/test-striconveh.c
+++ b/tests/test-striconveh.c
@@ -606,7 +606,7 @@ main ()
           ASSERT (length == 0);
           if (o)
             {
-              ASSERT (offsets[0] == 0);
+              ASSERT (offsets[0] == (size_t)(-1));
               ASSERT (offsets[1] == MAGIC);
               free (offsets);
             }
@@ -1359,7 +1359,7 @@ main ()
           ASSERT (length == 0);
           if (o)
             {
-              ASSERT (offsets[0] == 0);
+              ASSERT (offsets[0] == (size_t)(-1));
               ASSERT (offsets[1] == MAGIC);
               free (offsets);
             }
@@ -1388,7 +1388,6 @@ main ()
       ASSERT (offsets[i] == (i <= 3 ? i :
                              i == 9 ? 5 :
                              i >= 13 && i <= 19 ? i - 7 :
-                             i == 25 ? 14 :
                              (size_t)(-1)));
     ASSERT (offsets[28] == MAGIC);
     free (offsets);
@@ -1442,7 +1441,6 @@ main ()
     for (size_t i = 0; i < 15; i++)
       ASSERT (offsets[i] == (i <= 1 ? i :
                              i == 6 ? 2 :
-                             i == 7 ? 3 :
                              (size_t)(-1)));
     ASSERT (offsets[15] == MAGIC);
     free (offsets);
diff --git a/tests/test-striconveha.c b/tests/test-striconveha.c
index 52d8434b15..1626db9204 100644
--- a/tests/test-striconveha.c
+++ b/tests/test-striconveha.c
@@ -297,7 +297,7 @@ main ()
           ASSERT (length == 0);
           if (o)
             {
-              ASSERT (offsets[0] == 0);
+              ASSERT (offsets[0] == (size_t)(-1));
               ASSERT (offsets[1] == MAGIC);
               free (offsets);
             }
@@ -393,7 +393,6 @@ main ()
                                            i == 7 ? 6 :
                                            i == 9 ? 9 :
                                            i == 11 ? 12 :
-                                           i == 13 ? 15 :
                                            (size_t)(-1)));
                   ASSERT (offsets[16] == MAGIC);
                   free (offsets);
diff --git a/tests/uniconv/test-u16-conv-from-enc.c b/tests/uniconv/test-u16-conv-from-enc.c
index ef19ed98ec..49ffc8bbba 100644
--- a/tests/uniconv/test-u16-conv-from-enc.c
+++ b/tests/uniconv/test-u16-conv-from-enc.c
@@ -205,7 +205,6 @@ main ()
                                            i == 7 ? 2 :
                                            i == 9 ? 3 :
                                            i == 11 ? 4 :
-                                           i == 13 ? 5 :
                                            (size_t)(-1)));
                   ASSERT (offsets[16] == MAGIC);
                   free (offsets);
diff --git a/tests/uniconv/test-u16-conv-to-enc.c b/tests/uniconv/test-u16-conv-to-enc.c
index 5a5abd5e8d..15700da2c9 100644
--- a/tests/uniconv/test-u16-conv-to-enc.c
+++ b/tests/uniconv/test-u16-conv-to-enc.c
@@ -162,7 +162,7 @@ main ()
           ASSERT (length == strlen (""));
           if (o)
             {
-              ASSERT (offsets[0] == 0);
+              ASSERT (offsets[0] == (size_t)(-1));
               ASSERT (offsets[1] == MAGIC);
               free (offsets);
             }
diff --git a/tests/uniconv/test-u32-conv-from-enc.c b/tests/uniconv/test-u32-conv-from-enc.c
index 597005dad6..70c8005a2a 100644
--- a/tests/uniconv/test-u32-conv-from-enc.c
+++ b/tests/uniconv/test-u32-conv-from-enc.c
@@ -205,7 +205,6 @@ main ()
                                            i == 7 ? 2 :
                                            i == 9 ? 3 :
                                            i == 11 ? 4 :
-                                           i == 13 ? 5 :
                                            (size_t)(-1)));
                   ASSERT (offsets[16] == MAGIC);
                   free (offsets);
diff --git a/tests/uniconv/test-u8-conv-from-enc.c b/tests/uniconv/test-u8-conv-from-enc.c
index c3f679bacb..cd98f82564 100644
--- a/tests/uniconv/test-u8-conv-from-enc.c
+++ b/tests/uniconv/test-u8-conv-from-enc.c
@@ -191,7 +191,6 @@ main ()
                                            i == 7 ? 6 :
                                            i == 9 ? 9 :
                                            i == 11 ? 12 :
-                                           i == 13 ? 15 :
                                            (size_t)(-1)));
                   ASSERT (offsets[16] == MAGIC);
                   free (offsets);
diff --git a/tests/uniconv/test-u8-conv-to-enc.c b/tests/uniconv/test-u8-conv-to-enc.c
index 362a280531..f02d9181e9 100644
--- a/tests/uniconv/test-u8-conv-to-enc.c
+++ b/tests/uniconv/test-u8-conv-to-enc.c
@@ -166,7 +166,7 @@ main ()
           ASSERT (length == strlen (""));
           if (o)
             {
-              ASSERT (offsets[0] == 0);
+              ASSERT (offsets[0] == (size_t)(-1));
               ASSERT (offsets[1] == MAGIC);
               free (offsets);
             }
-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.