bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

%.1s format with vasnprintf reads more than one byte from argument


From: Ben Pfaff
Subject: %.1s format with vasnprintf reads more than one byte from argument
Date: Sun, 22 Feb 2009 22:10:56 -0800
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.2 (gnu/linux)

C89 and C99 are pretty clear that specifying a precision on a %s
directive to printf prevents reading more than that many bytes
from the argument and that in that case no null terminator is
required:

     s If no l length modifier is present, the argument shall be
       a pointer to the initial element of an array of character
       type.237)  Characters from the array are written up to (but
       not including) the terminating null character.  If the
       precision is specified, no more than that many bytes are
       written.  If the precision is not specified or is greater
       than the size of the array, the array shall contain a null
       character.

But running valgrind on test-vasnprintf when USE_SNPRINTF is not
selected, when the appended patch to test-vasnprintf.c is
applied, makes it clear that vasnprintf() will read beyond the
specified precision:

    ==3968== Conditional jump or move depends on uninitialised value(s)
    ==3968==    at 0x40239D7: strlen (mc_replace_strmem.c:242)
    ==3968==    by 0x8049065: vasnprintf (vasnprintf.c:4044)
    ==3968==    by 0x8048655: my_asnprintf (test-vasnprintf.c:47)
    ==3968==    by 0x80488F6: main (test-vasnprintf.c:131)

The culprit is pretty clearly this code in lib/vasnprintf.c:

                    case 's':
[...]
                        tmp_length = strlen (a.arg[dp->arg_index].a.a_string);
                      break;

The obvious solution would be to use strnlen in place of strlen
here, but I don't know whether you would object to the additional
dependency.  Maybe you want to introduce a "local_strnlen"
function instead.

Something similar would be needed for the wide-character case
(presumably a "local_wcsnlen").

diff --git a/tests/test-vasnprintf.c b/tests/test-vasnprintf.c
index 2f3f890..50c6956 100644
--- a/tests/test-vasnprintf.c
+++ b/tests/test-vasnprintf.c
@@ -119,10 +119,24 @@ test_asnprintf ()
     }
 }
 
+static void
+test_precision (void)
+{
+  size_t length = 0;
+  char *p, *s;
+
+  p = malloc (2);
+  ASSERT (p != NULL);
+  *p = 'x';
+  s = my_asnprintf (NULL, &length, "%.1s", p);
+  ASSERT (!strcmp (s, "x"));
+}
+
 int
 main (int argc, char *argv[])
 {
   test_vasnprintf ();
   test_asnprintf ();
+  test_precision ();
   return 0;
 }

-- 
Ben Pfaff 
http://benpfaff.org




reply via email to

[Prev in Thread] Current Thread [Next in Thread]