After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 53933 - g_strlcat returns incorrect result when size is 0
g_strlcat returns incorrect result when size is 0
Status: RESOLVED FIXED
Product: glib
Classification: Platform
Component: general
1.3.x
Other Linux
: Normal normal
: ---
Assigned To: gtkdev
gtkdev
Depends on:
Blocks:
 
 
Reported: 2001-04-30 17:53 UTC by Mark Murnane
Modified: 2011-02-18 15:47 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Diff file for testglib (545 bytes, patch)
2001-04-30 18:21 UTC, Mark Murnane
none Details | Review
Diff file for gstrfuncs.c. Modifies g_strlcat to check size. (612 bytes, patch)
2001-04-30 18:22 UTC, Mark Murnane
none Details | Review
Simple testcase to exercise g_strlcat with parameter 3 = 0 (235 bytes, text/plain)
2001-04-30 18:32 UTC, Mark Murnane
  Details
patch (1.02 KB, patch)
2002-11-26 21:06 UTC, Matthias Clasen
none Details | Review

Description Mark Murnane 2001-04-30 17:53:00 UTC
As part of the tests run during testglib, g_strlcat is exercised with a
number of parameters.  The second last of these tests g_strlcat with two
strings, one of 'Y\0' and the other of "123".  

On Solaris 8, this test fails the assertion check.  The reason for this is
the presence of native strlcat on Solaris 8 which returns a result of 4.

The implementation provided in glib for platforms, such as Linux, without
a native strlcat returns 3 in the circumstances outlined above.
Comment 1 Mark Murnane 2001-04-30 18:21:49 UTC
Created attachment 507 [details] [review]
Diff file for testglib
Comment 2 Mark Murnane 2001-04-30 18:22:43 UTC
Created attachment 508 [details] [review]
Diff file for gstrfuncs.c.  Modifies g_strlcat to check size.
Comment 3 Mark Murnane 2001-04-30 18:30:51 UTC
Running on Redhat Linux 7.1 Kernel 2.4.2-2 i686.
C library version:  libc.so.6


When compiling glib on Solaris 8 I found that it core dumped due to an
assertion failing at line 1017.  The assertion was testing g_strlcat
for a return value of 3, but on Solaris it was returning 4.  Also,
according to the strlcat documentation on Solaris this is the correct
value.


Check on Linux and found no problem.  Linux uses the glib
implementation of g_strlcat.  This is based on OpenBSD so I check the
OpenBSD man pages at:
 http://www.openbsd.org/cgi-bin/man.cgi?query=strlcat&apropos=0&sektion=0&manpath=OpenBSD+Current&arch=i386&format=html

The return values are specified as follows:

>RETURN VALUES
>The strlcpy() and strlcat() functions return the total length of the
>string they tried to create.  For strlcpy() that means the length of
>src.
>For strlcat() that means the initial length of dst plus the length of
>src. While this may seem somewhat confusing it was done to make
>truncation detection simple.

According to this, g_strlcat should indeed return 4 for the testcase
used in testglib.c

g_strlcat can remedy this by checking the third parameter, size, to
see if it is 0 before any processing.  In that case it simply returns
the length of the two strings passed in as parameters.

Two diff files are attached with changes for g_strlcat and the tests
in testglib.c.

Also attached is a small test file to exercise this portion of g_strlcat.

Comment 4 Mark Murnane 2001-04-30 18:32:00 UTC
Created attachment 510 [details]
Simple testcase to exercise g_strlcat with parameter 3 = 0
Comment 5 Owen Taylor 2001-05-07 14:38:36 UTC
Hmm, reading the OpenBSD man page doesn't exactly make things
clear to me. I think essentially, the case where 
strlen(dest) >= size is undefined.

So, my instinct here is to simply remove the test case.

The other approach would be to find an OpenBSD system and
see what it returns; I don't want to simply change the check
and have it break on OpenBSD....
Comment 6 Matthias Clasen 2002-11-26 15:31:27 UTC
Move some forgotten unclosed bugs from earlier 2.0.x milestones to 2.0.10
Comment 7 Matthias Clasen 2002-11-26 15:50:11 UTC
Looking at testglib.c, there are some more strlcat tests which 
seem to assert the 
wrong return value, e.g.

  *string = 'Y';
  *(string + 1)= '\0';
  g_assert 
(g_strlcpy(string, "Hello" , 0) == 5);

which should return initial length of 
dest + length of src == 6.

Why doesn't that assert trigger on Solaris 8 ?
Comment 8 Matthias Clasen 2002-11-26 20:11:21 UTC
Hmm, I believe there is a indeed a problem with g_strcat. It is even
documented to behave differently from what the OpenBSD man page says.
In gstrfuncs.c I find:

  Returns size of attempted result, which is
  MIN (dest_size, strlen (original dest)) + strlen (src),
  so if retval >= dest_size, truncation occurred.

I think we must either fix our implementation and tests to follow the
OpenBSD man page or always use our implementation and never wrap a
system strlcat, since it will probably have the OpenBSD semantics.

I vote for the first alternative.
Comment 9 Matthias Clasen 2002-11-26 20:27:46 UTC
As always, I spoke too soon...

Reading the complete section of the OpenBSD man page about return
values reveals that:

     Note however, that if strlcat() traverses size characters 
     without finding a NUL, the length of the string is considered 
     to be size and the destination string will not be NUL-terminated
     (since there was no space for the NUL).  This keeps strlcat()
     from running off the end of a string.  In practice this should
     not happen (as it means that either size is incorrect or that 
     dst is not a proper ``C'' string).  The check exists to 
     prevent potential security problems in incorrect code.

This is exactly what g_strlcat implements and what the tests check.
I conclude that the Solaris 8 implementation of strlcat fails to 
implement this paragraph - maybe we should put some of the testglib.c
tests into a configure test to check for a good strlcat before
wrapping it ?
Another idea is to issue a warning if the case above is met (since
it means a programming error: either the size is incorrect or the 
dst is not a proper C string)
Comment 10 Matthias Clasen 2002-11-26 20:52:56 UTC
Here is what David Wheeler, the auther of g_strlcpy and g_strlcat has
to say on the issue (found in 
http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO)

Also, strlcat(3) has slightly varying semantics when the provided size
is 0 or if there are no NIL characters in the destination string dst
(inside the given number of characters). In OpenBSD, if the size is 0,
then the destination string's length is considered 0. Also, if size is
nonzero, but there are no NIL characters in the destination string (in
the size number of characters), then the length of the destination is
considered equal to the size. These rules make handling strings
without embedded NILs consistent. Unfortunately, at least Solaris
doesn't (at this time) obey these rules, because they weren't
specified in the original documentation. I've talked to Todd Miller,
and he and I agree that the OpenBSD semantics are the correct ones
(and that Solaris is incorrect). The reasoning is simple: under no
condition should strlcat or strlcpy ever examine characters in the
destination outside of the range of size; such access might cause core
dumps (from accessing out-of-range memory) and even hardware
interactions (through memory-mapped I/O). Thus, given:

  a = strlcat ("Y", "123", 0);

The correct answer is 3 (0+3=3), but Solaris will claim the answer is
4 because it incorrectly looks at characters beyond the "size" length
in the destination. For now, I suggest avoiding cases where the size
is 0 or the destination has no NIL characters. Future versions of glib
will hide this difference and always use the OpenBSD semantics.

Curious that he talks about future versions of glib, but I think he
points in the direction I mentioned earlier: we should use

strlcat ("Y", "123", 0) == 3 

as autoconf test for a strlcat with the OpenBSD semantics 

Comment 11 Matthias Clasen 2002-11-26 21:06:13 UTC
Here is a configure.in patch (which I have not tested on either
OpenBSD or Solaris 8). 
Comment 12 Matthias Clasen 2002-11-26 21:06:59 UTC
Created attachment 12581 [details] [review]
patch
Comment 13 Owen Taylor 2002-12-03 19:34:53 UTC
Looks, basically OK, but it seems to me that the check
could be simplified to.

+int main() {
+  char p[10] = "hi";
+  if (strlcat (p, "bye", 0) != 3) 
+    return 1;
+  return 0;
+}], glib_ok=yes, glib_ok=no)

with your current version, you seem to be missing a stdlib.h
include. Also, AC_TRY_RUN() is a bit of a problem for cross
compilation, so:

 - You should provide a false (glib_ok=no makes a lot of sense)
 - You probably should enclose the check in a AC_CACHE_CHECK

   AC_CACHE_CHECK([for a working strlcat()], [glib_cv_have_strlcpy],
     [AC_TRY_RUN (..., 
                  glib_cv_have_strlcpy=yes, 
                  glib_cv_have_strlcpy=no,
                  glib_cv_have_strlcpy=no)])

   Bug 77565 is full of patches to fix cross compilation, and
   I don't want to _regress_ here.