Subject: pkg/20155: glib-1.2.x: Documentation violation in g_{v,}snprintf
To: None <gnats-bugs@gnats.netbsd.org>
From: Christian Biere <christianbiere@gmx.de>
List: netbsd-bugs
Date: 02/01/2003 22:25:29
>Number:         20155
>Category:       pkg
>Synopsis:       glib-1.2.x: Documentation violation in g_{v,}snprintf
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    pkg-manager
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sat Feb 01 13:26:02 PST 2003
>Closed-Date:
>Last-Modified:
>Originator:     Christian Biere
>Release:        NetBSD 1.6K
>Organization:
>Environment:

>Description:
See:
http://developer.gnome.org/doc/API/glib/glib-string-utility-functions.html

The documentation states that g_snprintf() and g_vsnprintf() return "the
length of the output string". This depends on whether the used platform
has snprintf() and vsnprintf(). In case the platform (e.g., NetBSD) has
these functions defined both glib functions will directly return any
non-negative value. This means on any platform having snprintf() and
vsnprintf() conforming to ISO C99, the glib functions will return the
number of characters that would have been written to the output buffer,
if it had been large enough.
Depending on how programs use the return values of g_{v,}snprintf() this
might result in buffer overflows and/or memory corruption. E.g,
pkgsrc/net/gtk-gnutella should be recompiled after fixing glib.

There's a further problem which isn't part of the fix: Both glib
functions return an (g)int but use (g)ulong to limit the size of the
output buffer. This causes only problems with very large strings.
However,  it isn't clever idea to use anything but size_t or at least
use gulong for the return value, too.

>How-To-Repeat:

foo.c:
#include <glib.h>

const gchar *str = "0123456789";

int main(int argc, char *argv[]){
	static gchar buf[8];
	gint ret;

	ret = g_snprintf(buf, sizeof(buf), "%s", str);
	g_message("ret=%d buf=\"%s\"", ret, buf);
	return 0;
}
---------

$ gcc -Wall `glib-config --cflags --libs` -o foo foo.c
$ ./foo
Message: ret=10 buf="0123456"

The expected result for ret is 7 because this is the length of the
string in buf.

>Fix:

--Multipart_Sat__1_Feb_2003_22:25:29_+0100_081aba00
Content-Type: text/plain;
 name="gutils.c.udif"
Content-Disposition: attachment;
 filename="gutils.c.udif"
Content-Transfer-Encoding: 7bit

--- gutils.c	2000/08/09 18:12:31	1.1
+++ gutils.c	2003/02/01 20:38:25
@@ -149,6 +149,11 @@
       str[n-1] = '\0';
       retval = strlen (str);
     }
+  else if (retval >= n) 
+    {
+      str[n-1] = '\0';
+      retval = n - 1;
+    }
 
   return retval;
 #else	/* !HAVE_VSNPRINTF */
@@ -191,6 +196,11 @@
     {
       str[n-1] = '\0';
       retval = strlen (str);
+    }
+  else if (retval >= n)
+    {
+      str[n-1] = '\0';
+      retval = n - 1;
     }
 
   return retval;

--Multipart_Sat__1_Feb_2003_22:25:29_+0100_081aba00--
>Release-Note:
>Audit-Trail:
>Unformatted:
 This is a multi-part message in MIME format.
 
 --Multipart_Sat__1_Feb_2003_22:25:29_+0100_081aba00
 Content-Type: text/plain; charset=US-ASCII
 Content-Transfer-Encoding: 7bit