https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127666

            Bug ID: 127666
           Summary: std::from_chars(first, last, long double&) reads past
                    'last' for "inf"/"nan" (strlen on the unterminated
                    input)
           Product: gcc
           Version: 16.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: lewis at sophists dot com
  Target Milestone: ---

Host: x86_64-pc-linux-gnu
Known to fail: 11.4.0, 14.2.0, 15.2.0, 16.0

For long double, std::from_chars reads beyond the end of its input when the
text is an
infinity or NaN. The result is correct, but the input need not be
NUL-terminated, and
for "inf" in a 3-byte buffer the read runs off the allocation:

    #include <charconv>
    #include <cstdlib>
    #include <cstring>

    int main()
    {
        char* p = static_cast<char*>(std::malloc(3));   // "inf", no NUL
terminator
        std::memcpy(p, "inf", 3);
        long double v;
        auto r = std::from_chars(p, p + 3, v);
        std::free(p);
        return r.ec == std::errc{} && r.ptr == p + 3 ? 0 : 1;
    }

    $ g++ -std=c++17 -g -fsanitize=address pr.cpp && ./a.out
    ERROR: AddressSanitizer: heap-buffer-overflow ... READ of size 4
        #0 strlen
        #1 (libstdc++.so)
        #2 (libstdc++.so)
        #3 main pr.cpp:11

float and double are not affected. Under valgrind memcheck, the same read shows
up as
"Conditional jump or move depends on uninitialised value(s)" in strlen, called
from
std::(anonymous namespace)::pattern via from_chars_strtod<long double>. That is
how we
found it: the bytes after the text in our buffer were uninitialized.

The cause is in src/c++17/floating_from_chars.cc, pattern(), in the
infinity/NaN branch:

    buf.assign(first, 0, len);

'first' is a const char*, and basic_string has no assign(const char*,
size_type,
size_type), so this resolves to assign(const basic_string&, size_type,
size_type) with
a temporary string built from 'first' - a strlen that ignores 'last' - of which
only
len characters are then kept. The intended call is presumably:

    buf.assign(first, len);

as elsewhere in the same function (buf.assign(first, last)). The line is
unchanged on
master.

(NOTE - report significantly clarified by Claude Opus 5.5).

Reply via email to