diff --git a/libstdc++-v3/include/std/istream b/libstdc++-v3/include/std/istream index b506c4f7504..416ef556fa1 100644 --- a/libstdc++-v3/include/std/istream +++ b/libstdc++-v3/include/std/istream @@ -784,7 +784,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * - if `width()` is greater than zero, `n` is `min(width(), n)` * - otherwise `n` is the number of elements of the array * - (before C++20 the pointer is assumed to point to an array of - * - the largest possible size for an array of `char_type`). + * the largest possible size for an array of `char_type`). * * Characters are extracted and stored until one of the following happens: * - `n - 1` characters are stored @@ -802,19 +802,40 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION inline basic_istream<_CharT, _Traits>& operator>>(basic_istream<_CharT, _Traits>& __in, _CharT* __s) { +#ifdef __OPTIMIZE__ + // Function inlining might make the buffer size known, allowing us to + // prevent overflow. size_t __n = __builtin_object_size(__s, 0); - if (__builtin_expect(__n < sizeof(_CharT), false)) + if (__n < sizeof(_CharT)) { // There is not even space for the required null terminator. __glibcxx_assert(__n >= sizeof(_CharT)); + // No point calling __istream_extract, but still need to reset width. __in.width(0); __in.setstate(ios_base::failbit); } - else + else if (__n != (size_t)-1) { - if (__n == (size_t)-1) - __n = __gnu_cxx::__numeric_traits::__max; - std::__istream_extract(__in, __s, __n / sizeof(_CharT)); + __n /= sizeof(_CharT); + streamsize __w = __in.width(); + std::__istream_extract(__in, __s, __n); + if (__in.good() && (__w <= 0 || __n < __w)) + { + // Stopped extracting early to avoid overflowing the buffer, + // but might have stopped anyway (and set eofbit) if at EOF. + const typename _Traits::int_type __c = __in.rdbuf()->sgetc(); + const bool __eof = _Traits::eq_int_type(__c, _Traits::eof()); + if (__builtin_expect(__eof, true)) // Assume EOF, not overflow. + __in.setstate(ios_base::eofbit); + } + } + else +#endif // __OPTIMIZE + { + // Buffer size is unknown, have to assume it's huge. + streamsize __n = __gnu_cxx::__numeric_traits::__max; + __n /= sizeof(_CharT); + std::__istream_extract(__in, __s, __n); } return __in; } diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/pr106248.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/pr106248.cc new file mode 100644 index 00000000000..6d89a0e5fef --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/pr106248.cc @@ -0,0 +1,40 @@ +// { dg-do run } + +#include +#include + +void +test_pr106248() +{ + char buf[5] = {'x', 'x', 'x', 'x', 'x'}; + std::string s(" four"); + std::istringstream in(s); + in >> buf; +#if __cplusplus >= 202002L + // Extraction stops because buffer is full. + VERIFY( in.good() ); +#else + // PR libstdc++/106248 + // Extraction stops because all input has been consumed and eofbit is set. + VERIFY( in.eof() ); +#endif + // Extracted string must be null-terminated. + VERIFY( buf[4] == '\0' ); + VERIFY( std::string(buf) == "four" ); + + in.clear(); + in.str(s); + for (int i = 0; i < 5; ++i) + s[i] = 'x'; + + in.width(5); + in >> buf; + // Extraction stops due to field width, eofbit not set. + VERIFY( in.good() ); + VERIFY( std::string(buf) == "four" ); +} + +int main() +{ + test_pr106248(); +} diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/pr106248.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/pr106248.cc new file mode 100644 index 00000000000..7c226600b9e --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/pr106248.cc @@ -0,0 +1,40 @@ +// { dg-do run } + +#include +#include + +void +test_pr106248() +{ + wchar_t buf[5] = {L'x', L'x', L'x', L'x', L'x'}; + std::wstring s(L" four"); + std::wistringstream in(s); + in >> buf; +#if __cplusplus >= 202002L + // Extraction stops because buffer is full. + VERIFY( in.good() ); +#else + // PR libstdc++/106248 + // Extraction stops because all input has been consumed and eofbit is set. + VERIFY( in.eof() ); +#endif + // Extracted string must be null-terminated. + VERIFY( buf[4] == L'\0' ); + VERIFY( std::wstring(buf) == L"four" ); + + in.clear(); + in.str(s); + for (int i = 0; i < 5; ++i) + s[i] = L'x'; + + in.width(5); + in >> buf; + // Extraction stops due to field width, eofbit not set. + VERIFY( in.good() ); + VERIFY( std::wstring(buf) == L"four" ); +} + +int main() +{ + test_pr106248(); +}