Created attachment 5204 [details] test case glibc checked out at 2011-01-20 approx. 20:00 UTC build/host/target triplet (the same): x86_64-unknown-linux-gnu configure options: only --prefix uname -a: Linux smut 2.6.35.10-74.fc14.x86_64 #1 SMP Thu Dec 23 16:04:50 UTC 2010 x86_64 x86_64 x86_64 GNU/Linux gcc: gcc version 4.5.1 20100924 (Red Hat 4.5.1-4) (GCC) ld: GNU ld version 2.20.51.0.7-6.fc14 20100318 Running strtod() on a string that consists only of "I" or "N" causes a read past the end of the string somewhere within strncasecmp_l(). More precisely, this is reported by valgrind and I don't understand the assembler code but it seems real to me (see the log below, namely the second read). A test case it attached. Compiled gcc -o strtod strtod.c and executed valgrind ./strtod produces ==15770== Memcheck, a memory error detector ==15770== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==15770== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info ==15770== Command: ./strtod ==15770== p's address: 0x4f98040 ==15770== Invalid read of size 8 ==15770== at 0x4C8D40F: __GI___strncasecmp_l (strcmp.S:213) ==15770== by 0x4C45598: ____strtod_l_internal (strtod_l.c:553) ==15770== by 0x4005D3: main (in /home/yeti/C/strtod) ==15770== Address 0x4f98040 is 0 bytes inside a block of size 2 alloc'd ==15770== at 0x4A05E46: malloc (vg_replace_malloc.c:195) ==15770== by 0x4C87331: strdup (strdup.c:43) ==15770== by 0x4005A5: main (in /home/yeti/C/strtod) ==15770== ==15770== Invalid read of size 8 ==15770== at 0x4C8D417: __GI___strncasecmp_l (strcmp.S:215) ==15770== by 0x4C45598: ____strtod_l_internal (strtod_l.c:553) ==15770== by 0x4005D3: main (in /home/yeti/C/strtod) ==15770== Address 0x4f98048 is 6 bytes after a block of size 2 alloc'd ==15770== at 0x4A05E46: malloc (vg_replace_malloc.c:195) ==15770== by 0x4C87331: strdup (strdup.c:43) ==15770== by 0x4005A5: main (in /home/yeti/C/strtod) ==15770== ==15770== ==15770== HEAP SUMMARY: ==15770== in use at exit: 0 bytes in 0 blocks ==15770== total heap usage: 1 allocs, 1 frees, 2 bytes allocated ==15770== ==15770== All heap blocks were freed -- no leaks are possible ==15770== ==15770== For counts of detected and suppressed errors, rerun with: -v ==15770== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 4 from 4) (similarly for "N" in the source). The string in the test case is dynamically allocated as a static string probably appears in a sufficiently long readable section and produces no error. It boils down to STRNCASECMP (cp, L_("inf"), 3) and STRNCASECMP (cp, L_("nan"), 3) in stdlib/strtod_l.c but somehow I am not able to reproduce it using strncasecmp().
This is no bug. The code reads 8 bytes at a time even though the string end might be reached earlier. valgrind needs to be taught that.
It apparently reads 8 bytes at a time *and then* another 8 bytes, i.e. it reads 14 bytes after the string end in the example. It might be still correct but this seems a bit too much.
glibc code is reading past the string end: it is a bug. ----------- strtod.cpp --------- #include <stdio.h> #include <stdlib.h> #include <string> #include <gnu/libc-version.h> int main() { printf("GLIBC: %s\n", gnu_get_libc_version ()); std::string s = "item 1"; const char *test = s.c_str(); double d = strtod(test, 0); printf("%f\n", d); return 0; } ------------------------------------ ==14729== Memcheck, a memory error detector ==14729== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al. ==14729== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info ==14729== Command: ./strtod ==14729== GLIBC: 2.15 ==14729== Invalid read of size 8 ==14729== at 0x53D71BF: __GI___strncasecmp_l (strcmp.S:213) ==14729== by 0x538973F: ____strtod_l_internal (strtod_l.c:574) ==14729== by 0x4008BA: main (in /home/shockenhull/tmp/glibc_bugs/strtod) ==14729== Address 0x5a03058 is 24 bytes inside a block of size 31 alloc'd ==14729== at 0x4C2B1C7: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==14729== by 0x4ED0A88: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16) ==14729== by 0x4ED2494: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16) ==14729== by 0x4ED25E2: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16) ==14729== by 0x40088D: main (in /home/shockenhull/tmp/glibc_bugs/strtod) ==14729== ==14729== Invalid read of size 8 ==14729== at 0x53D71C7: __GI___strncasecmp_l (strcmp.S:215) ==14729== by 0x538973F: ____strtod_l_internal (strtod_l.c:574) ==14729== by 0x4008BA: main (in /home/shockenhull/tmp/glibc_bugs/strtod) ==14729== Address 0x5a03060 is 1 bytes after a block of size 31 alloc'd ==14729== at 0x4C2B1C7: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==14729== by 0x4ED0A88: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16) ==14729== by 0x4ED2494: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16) ==14729== by 0x4ED25E2: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16) ==14729== by 0x40088D: main (in /home/shockenhull/tmp/glibc_bugs/strtod) ==14729== 0.000000 ==14729== ==14729== HEAP SUMMARY: ==14729== in use at exit: 0 bytes in 0 blocks ==14729== total heap usage: 1 allocs, 1 frees, 31 bytes allocated ==14729== ==14729== All heap blocks were freed -- no leaks are possible ==14729== ==14729== For counts of detected and suppressed errors, rerun with: -v ==14729== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2)
strcasecmp is using an alignd 8-byte read which will never cross a page boundary. valgrind need to be fixed to ignore this.