Properly nul-terminate return values from g_convert()
authorBenjamin Otte <otte@gnome.org>
Wed, 28 Oct 2009 18:18:20 +0000 (19:18 +0100)
committerBenjamin Otte <otte@gnome.org>
Wed, 28 Oct 2009 18:26:17 +0000 (19:26 +0100)
The patch ensures that multibyte character sets are properly
nul-terminated. This is an issue because the documentation claims to
return a "nul-terminated" string and users of the API assume that means
a proper nul-termination in the resulting character set.

I looked at Pidgin and GStreamer code and found at least 3 cases where
this was happening.

This patch also reverts the documentation change from
5a633f82dbbce067c56fba0ee372fd341b76368d as that is now no longer
necessary.

glib/gconvert.c

index f2141a3..5ec8f1d 100644 (file)
 
 #include "galias.h"
 
+/* We try to terminate strings in unknown charsets with this many zero bytes
+ * to ensure that multibyte strings really are nul-terminated when we return
+ * them from g_convert() and friends.
+ */
+#define NUL_TERMINATOR_LENGTH 4
+
 GQuark 
 g_convert_error_quark (void)
 {
@@ -566,16 +572,8 @@ close_converter (GIConv cd)
  * </footnote>. 
  *
  * Return value: If the conversion was successful, a newly allocated
- *               nul-terminated<footnote id="nul-returns">
- *                <para>
- *                 Nul-terminated return values from conversion functions
- *                 are terminated by a single 0 byte only. This means that
- *                 for multibyte character sets like UTF-16, they must be
- *                 treated as not nul-terminated.
- *                </para>
- *               </footnote> 
- *               string, which must be freed with g_free(). Otherwise %NULL 
- *               and @error will be set.
+ *               nul-terminated string, which must be freed with
+ *               g_free(). Otherwise %NULL and @error will be set.
  **/
 gchar*
 g_convert_with_iconv (const gchar *str,
@@ -603,9 +601,9 @@ g_convert_with_iconv (const gchar *str,
 
   p = str;
   inbytes_remaining = len;
-  outbuf_size = len + 1; /* + 1 for nul in case len == 1 */
+  outbuf_size = len + NUL_TERMINATOR_LENGTH;
   
-  outbytes_remaining = outbuf_size - 1; /* -1 for nul */
+  outbytes_remaining = outbuf_size - NUL_TERMINATOR_LENGTH;
   outp = dest = g_malloc (outbuf_size);
 
   while (!done && !have_error)
@@ -631,7 +629,7 @@ g_convert_with_iconv (const gchar *str,
                dest = g_realloc (dest, outbuf_size);
                
                outp = dest + used;
-               outbytes_remaining = outbuf_size - used - 1; /* -1 for nul */
+               outbytes_remaining = outbuf_size - used - NUL_TERMINATOR_LENGTH;
              }
              break;
            case EILSEQ:
@@ -665,7 +663,7 @@ g_convert_with_iconv (const gchar *str,
        }
     }
 
-  *outp = '\0';
+  memset (outp, 0, NUL_TERMINATOR_LENGTH);
   
   if (bytes_read)
     *bytes_read = p - str;
@@ -727,9 +725,8 @@ g_convert_with_iconv (const gchar *str,
  * conversions<footnoteref linkend="streaming-state"/>.
  *
  * Return value: If the conversion was successful, a newly allocated
- *               nul-terminated<footnoteref linkend="nul-returns"/> string,
- *               which must be freed with g_free(). Otherwise %NULL and
- *               @error will be set.
+ *               nul-terminated string, which must be freed with
+ *               g_free(). Otherwise %NULL and @error will be set.
  **/
 gchar*
 g_convert (const gchar *str,
@@ -803,9 +800,8 @@ g_convert (const gchar *str,
  * conversions<footnoteref linkend="streaming-state"/>.
  *
  * Return value: If the conversion was successful, a newly allocated
- *               nul-terminated<footnoteref linkend="nul-returns"/> string,
- *               which must be freed with g_free(). Otherwise %NULL and
- *               @error will be set.
+ *               nul-terminated string, which must be freed with
+ *               g_free(). Otherwise %NULL and @error will be set.
  **/
 gchar*
 g_convert_with_fallback (const gchar *str,
@@ -894,8 +890,8 @@ g_convert_with_fallback (const gchar *str,
    */
   p = utf8;
 
-  outbuf_size = len + 1; /* + 1 for nul in case len == 1 */
-  outbytes_remaining = outbuf_size - 1; /* -1 for nul */
+  outbuf_size = len + NUL_TERMINATOR_LENGTH;
+  outbytes_remaining = outbuf_size - NUL_TERMINATOR_LENGTH;
   outp = dest = g_malloc (outbuf_size);
 
   while (!done && !have_error)
@@ -919,7 +915,7 @@ g_convert_with_fallback (const gchar *str,
                dest = g_realloc (dest, outbuf_size);
                
                outp = dest + used;
-               outbytes_remaining = outbuf_size - used - 1; /* -1 for nul */
+               outbytes_remaining = outbuf_size - used - NUL_TERMINATOR_LENGTH;
                
                break;
              }
@@ -988,7 +984,7 @@ g_convert_with_fallback (const gchar *str,
 
   /* Cleanup
    */
-  *outp = '\0';
+  memset (outp, 0, NUL_TERMINATOR_LENGTH);
   
   close_converter (cd);