diff --git a/libstdc++-v3/include/std/istream b/libstdc++-v3/include/std/istream
index b506c4f7504b90084221751b6e0287735d4baa05..416ef556fa1703a27103302249ddae79f0b30ed6 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 if (__n != (size_t)-1)
+	{
+	  __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
 	{
-	  if (__n == (size_t)-1)
-	    __n = __gnu_cxx::__numeric_traits<streamsize>::__max;
-	  std::__istream_extract(__in, __s, __n / sizeof(_CharT));
+	  // Buffer size is unknown, have to assume it's huge.
+	  streamsize __n = __gnu_cxx::__numeric_traits<streamsize>::__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 0000000000000000000000000000000000000000..6d89a0e5fefc1cf5f52f302b019d2f4dfcc7f704
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/pr106248.cc
@@ -0,0 +1,40 @@
+// { dg-do run }
+
+#include <sstream>
+#include <testsuite_hooks.h>
+
+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 0000000000000000000000000000000000000000..7c226600b9e20b644bd139be07b29336a94988fd
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/pr106248.cc
@@ -0,0 +1,40 @@
+// { dg-do run }
+
+#include <sstream>
+#include <testsuite_hooks.h>
+
+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();
+}