[committed] libstdc++: Replace operator>>(istream&, char*) [LWG 2499]

Jonathan Wakely jwakely@redhat.com
Wed Aug 5 21:25:00 GMT 2020


P0487R1 resolved LWG 2499 for C++20 by removing the operator>> overloads
that have high risk of buffer overflows. They were replaced by
equivalents that only accept a reference to an array, and so can
guarantee not to write past the end of the array.

In order to support both the old and new functionality, this patch
introduces a new overloaded __istream_extract function which takes a
maximum length. The new operator>> overloads use the array size as the
maximum length. The old overloads now use __builtin_object_size to
determine the available buffer size if available (which requires -O2) or
use numeric_limits<streamsize>::max()/sizeof(char_type) otherwise. This
is a change in behaviour, as the old overloads previously always used
numeric_limits<streamsize>::max(), without considering sizeof(char_type)
and without attempting to prevent overflows.

Because they now do little more than call __istream_extract, the old
operator>> overloads are very small inline functions. This means there
is no advantage to explicitly instantiating them in the library (in fact
that would prevent the __builtin_object_size checks from ever working).
As a result, the explicit instantiation declarations can be removed from
the header. The explicit instantiation definitions are still needed, for
backwards compatibility with existing code that expects to link to the
definitions in the library.

While working on this change I noticed that src/c++11/istream-inst.cc
has the following explicit instantiation definition:
  template istream& operator>>(istream&, char*);
This had no effect (and so should not have been present in that file),
because there was an explicit specialization declared in <istream> and
defined in src/++98/istream.cc. However, this change removes the
explicit specialization, and now the explicit instantiation definition
is necessary to ensure the symbol gets defined in the library.

libstdc++-v3/ChangeLog:

	* config/abi/pre/gnu.ver (GLIBCXX_3.4.29): Export new symbols.
	* include/bits/istream.tcc (__istream_extract): New function
	template implementing both of operator>>(istream&, char*) and
	operator>>(istream&, char(&)[N]). Add explicit instantiation
	declaration for it. Remove explicit instantiation declarations
	for old function templates.
	* include/std/istream (__istream_extract): Declare.
	(operator>>(basic_istream<C,T>&, C*)): Define inline and simply
	call __istream_extract.
	(operator>>(basic_istream<char,T>&, signed char*)): Likewise.
	(operator>>(basic_istream<char,T>&, unsigned char*)): Likewise.
	(operator>>(basic_istream<C,T>&, C(7)[N])): Define for LWG 2499.
	(operator>>(basic_istream<char,T>&, signed char(&)[N])):
	Likewise.
	(operator>>(basic_istream<char,T>&, unsigned char(&)[N])):
	Likewise.
	* include/std/streambuf (basic_streambuf): Declare char overload
	of __istream_extract as a friend.
	* src/c++11/istream-inst.cc: Add explicit instantiation
	definition for wchar_t overload of __istream_extract. Remove
	explicit instantiation definitions of old operator>> overloads
	for versioned-namespace build.
	* src/c++98/istream.cc (operator>>(istream&, char*)): Replace
	with __istream_extract(istream&, char*, streamsize).
	* testsuite/27_io/basic_istream/extractors_character/char/3.cc:
	Do not use variable-length array.
	* testsuite/27_io/basic_istream/extractors_character/char/4.cc:
	Do not run test for C++20.
	* testsuite/27_io/basic_istream/extractors_character/char/9555-ic.cc:
	Do not test writing to pointers for C++20.
	* testsuite/27_io/basic_istream/extractors_character/char/9826.cc:
	Use array instead of pointer.
	* testsuite/27_io/basic_istream/extractors_character/wchar_t/3.cc:
	Do not use variable-length array.
	* testsuite/27_io/basic_istream/extractors_character/wchar_t/4.cc:
	Do not run test for C++20.
	* testsuite/27_io/basic_istream/extractors_character/wchar_t/9555-ic.cc:
	Do not test writing to pointers for C++20.
	* testsuite/27_io/basic_istream/extractors_character/char/lwg2499.cc:
	New test.
	* testsuite/27_io/basic_istream/extractors_character/char/lwg2499_neg.cc:
	New test.
	* testsuite/27_io/basic_istream/extractors_character/char/overflow.cc:
	New test.
	* testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499.cc:
	New test.
	* testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499_neg.cc:
	New test.

Tested powerpc64le-linux. Committed to trunk.

Martin, Jakub, could you please double-check the usage of
__builtin_object_size? (line 805 in libstdc++-v3/include/std/istream)
Do you see any problems with using it here? If it can't tell the size
then we just assume it's larger than the string to be extracted, which
is what the old code did anyway. I do have an idea for stronger
checking in debug mode, which I'll post in a minute.


-------------- next part --------------
commit 17abcc773415848dce593016512636cda3de20d5
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Aug 5 22:17:18 2020

    libstdc++: Replace operator>>(istream&, char*) [LWG 2499]
    
    P0487R1 resolved LWG 2499 for C++20 by removing the operator>> overloads
    that have high risk of buffer overflows. They were replaced by
    equivalents that only accept a reference to an array, and so can
    guarantee not to write past the end of the array.
    
    In order to support both the old and new functionality, this patch
    introduces a new overloaded __istream_extract function which takes a
    maximum length. The new operator>> overloads use the array size as the
    maximum length. The old overloads now use __builtin_object_size to
    determine the available buffer size if available (which requires -O2) or
    use numeric_limits<streamsize>::max()/sizeof(char_type) otherwise. This
    is a change in behaviour, as the old overloads previously always used
    numeric_limits<streamsize>::max(), without considering sizeof(char_type)
    and without attempting to prevent overflows.
    
    Because they now do little more than call __istream_extract, the old
    operator>> overloads are very small inline functions. This means there
    is no advantage to explicitly instantiating them in the library (in fact
    that would prevent the __builtin_object_size checks from ever working).
    As a result, the explicit instantiation declarations can be removed from
    the header. The explicit instantiation definitions are still needed, for
    backwards compatibility with existing code that expects to link to the
    definitions in the library.
    
    While working on this change I noticed that src/c++11/istream-inst.cc
    has the following explicit instantiation definition:
      template istream& operator>>(istream&, char*);
    This had no effect (and so should not have been present in that file),
    because there was an explicit specialization declared in <istream> and
    defined in src/++98/istream.cc. However, this change removes the
    explicit specialization, and now the explicit instantiation definition
    is necessary to ensure the symbol gets defined in the library.
    
    libstdc++-v3/ChangeLog:
    
            * config/abi/pre/gnu.ver (GLIBCXX_3.4.29): Export new symbols.
            * include/bits/istream.tcc (__istream_extract): New function
            template implementing both of operator>>(istream&, char*) and
            operator>>(istream&, char(&)[N]). Add explicit instantiation
            declaration for it. Remove explicit instantiation declarations
            for old function templates.
            * include/std/istream (__istream_extract): Declare.
            (operator>>(basic_istream<C,T>&, C*)): Define inline and simply
            call __istream_extract.
            (operator>>(basic_istream<char,T>&, signed char*)): Likewise.
            (operator>>(basic_istream<char,T>&, unsigned char*)): Likewise.
            (operator>>(basic_istream<C,T>&, C(7)[N])): Define for LWG 2499.
            (operator>>(basic_istream<char,T>&, signed char(&)[N])):
            Likewise.
            (operator>>(basic_istream<char,T>&, unsigned char(&)[N])):
            Likewise.
            * include/std/streambuf (basic_streambuf): Declare char overload
            of __istream_extract as a friend.
            * src/c++11/istream-inst.cc: Add explicit instantiation
            definition for wchar_t overload of __istream_extract. Remove
            explicit instantiation definitions of old operator>> overloads
            for versioned-namespace build.
            * src/c++98/istream.cc (operator>>(istream&, char*)): Replace
            with __istream_extract(istream&, char*, streamsize).
            * testsuite/27_io/basic_istream/extractors_character/char/3.cc:
            Do not use variable-length array.
            * testsuite/27_io/basic_istream/extractors_character/char/4.cc:
            Do not run test for C++20.
            * testsuite/27_io/basic_istream/extractors_character/char/9555-ic.cc:
            Do not test writing to pointers for C++20.
            * testsuite/27_io/basic_istream/extractors_character/char/9826.cc:
            Use array instead of pointer.
            * testsuite/27_io/basic_istream/extractors_character/wchar_t/3.cc:
            Do not use variable-length array.
            * testsuite/27_io/basic_istream/extractors_character/wchar_t/4.cc:
            Do not run test for C++20.
            * testsuite/27_io/basic_istream/extractors_character/wchar_t/9555-ic.cc:
            Do not test writing to pointers for C++20.
            * testsuite/27_io/basic_istream/extractors_character/char/lwg2499.cc:
            New test.
            * testsuite/27_io/basic_istream/extractors_character/char/lwg2499_neg.cc:
            New test.
            * testsuite/27_io/basic_istream/extractors_character/char/overflow.cc:
            New test.
            * testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499.cc:
            New test.
            * testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499_neg.cc:
            New test.

diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index 17aff5d907b..b6ce76c1f20 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2304,6 +2304,11 @@ GLIBCXX_3.4.29 {
     # std::from_chars
     _ZSt10from_charsPKcS0_R[def]St12chars_format;
 
+    # std::__istream_extract(istream&, char*, streamsize)
+    _ZSt17__istream_extractRSiPc[ilx];
+    # std::__istream_extract(wistream&, wchar_t*, streamsize)
+    _ZSt17__istream_extractIwSt11char_traitsIwEEvRSt13basic_istreamIT_T0_EPS3_[ilx];
+
 } GLIBCXX_3.4.28;
 
 # Symbols in the support library (libsupc++) have their own tag.
diff --git a/libstdc++-v3/include/bits/istream.tcc b/libstdc++-v3/include/bits/istream.tcc
index 0289867c50b..b8f530f6ef5 100644
--- a/libstdc++-v3/include/bits/istream.tcc
+++ b/libstdc++-v3/include/bits/istream.tcc
@@ -986,8 +986,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     }
 
   template<typename _CharT, typename _Traits>
-    basic_istream<_CharT, _Traits>&
-    operator>>(basic_istream<_CharT, _Traits>& __in, _CharT* __s)
+    void
+    __istream_extract(basic_istream<_CharT, _Traits>& __in, _CharT* __s,
+		      streamsize __num)
     {
       typedef basic_istream<_CharT, _Traits>		__istream_type;
       typedef basic_streambuf<_CharT, _Traits>          __streambuf_type;
@@ -1003,9 +1004,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  __try
 	    {
 	      // Figure out how many characters to extract.
-	      streamsize __num = __in.width();
-	      if (__num <= 0)
-		__num = __gnu_cxx::__numeric_traits<streamsize>::__max;
+	      streamsize __width = __in.width();
+	      if (0 < __width && __width < __num)
+		__num = __width;
 
 	      const __ctype_type& __ct = use_facet<__ctype_type>(__in.getloc());
 
@@ -1042,7 +1043,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	__err |= ios_base::failbit;
       if (__err)
 	__in.setstate(__err);
-      return __in;
     }
 
   // 27.6.1.4 Standard basic_istream manipulators
@@ -1075,11 +1075,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   extern template class basic_istream<char>;
   extern template istream& ws(istream&);
   extern template istream& operator>>(istream&, char&);
-  extern template istream& operator>>(istream&, char*);
   extern template istream& operator>>(istream&, unsigned char&);
   extern template istream& operator>>(istream&, signed char&);
-  extern template istream& operator>>(istream&, unsigned char*);
-  extern template istream& operator>>(istream&, signed char*);
 
   extern template istream& istream::_M_extract(unsigned short&);
   extern template istream& istream::_M_extract(unsigned int&);  
@@ -1101,7 +1098,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   extern template class basic_istream<wchar_t>;
   extern template wistream& ws(wistream&);
   extern template wistream& operator>>(wistream&, wchar_t&);
-  extern template wistream& operator>>(wistream&, wchar_t*);
+  extern template void __istream_extract(wistream&, wchar_t*, streamsize);
 
   extern template wistream& wistream::_M_extract(unsigned short&);
   extern template wistream& wistream::_M_extract(unsigned int&);  
diff --git a/libstdc++-v3/include/std/istream b/libstdc++-v3/include/std/istream
index 407c1ccda49..cb8e9f87c90 100644
--- a/libstdc++-v3/include/std/istream
+++ b/libstdc++-v3/include/std/istream
@@ -762,41 +762,52 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return (__in >> reinterpret_cast<char&>(__c)); }
   //@}
 
+
+  template<typename _CharT, typename _Traits>
+    void
+    __istream_extract(basic_istream<_CharT, _Traits>&, _CharT*, streamsize);
+
+  void __istream_extract(istream&, char*, streamsize);
+
   //@{
   /**
    *  @brief  Character string extractors
    *  @param  __in  An input stream.
-   *  @param  __s  A pointer to a character array.
+   *  @param  __s  A character array (or a pointer to an array before C++20).
    *  @return  __in
    *
    *  Behaves like one of the formatted arithmetic extractors described in
-   *  std::basic_istream.  After constructing a sentry object with good
-   *  status, this function extracts up to @c n characters and stores them
-   *  into the array starting at @a __s.  @c n is defined as:
+   *  `std::basic_istream`.  After constructing a sentry object with good
+   *  status, this function extracts up to `n` characters and stores them
+   *  into the array `__s`.  `n` is defined as:
    *
-   *  - if @c width() is greater than zero, @c n is width() otherwise
-   *  - @c n is <em>the number of elements of the largest array of *
-   *  - @c char_type that can store a terminating @c eos.</em>
-   *  - [27.6.1.2.3]/6
+   *  - 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`).
    *
    *  Characters are extracted and stored until one of the following happens:
-   *  - @c n-1 characters are stored
+   *  - `n - 1` characters are stored
    *  - EOF is reached
    *  - the next character is whitespace according to the current locale
-   *  - the next character is a null byte (i.e., @c charT() )
+   *  - the next character is a null byte (i.e., `charT()`)
    *
-   *  @c width(0) is then called for the input stream.
+   *  `width(0)` is then called for the input stream.
    *
    *  If no characters are extracted, sets failbit.
   */
-  template<typename _CharT, typename _Traits>
-    basic_istream<_CharT, _Traits>&
-    operator>>(basic_istream<_CharT, _Traits>& __in, _CharT* __s);
 
-  // Explicit specialization declaration, defined in src/istream.cc.
-  template<>
-    basic_istream<char>&
-    operator>>(basic_istream<char>& __in, char* __s);
+#if __cplusplus <= 201703L
+  template<typename _CharT, typename _Traits>
+    inline basic_istream<_CharT, _Traits>&
+    operator>>(basic_istream<_CharT, _Traits>& __in, _CharT* __s)
+    {
+      streamsize __n = __builtin_object_size(__s, 2) / sizeof(_CharT);
+      if (__n == 0)
+	__n = __gnu_cxx::__numeric_traits<streamsize>::__max / sizeof(_CharT);
+      std::__istream_extract(__in, __s, __n);
+      return __in;
+    }
 
   template<class _Traits>
     inline basic_istream<char, _Traits>&
@@ -807,6 +818,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline basic_istream<char, _Traits>&
     operator>>(basic_istream<char, _Traits>& __in, signed char* __s)
     { return (__in >> reinterpret_cast<char*>(__s)); }
+#else
+  // _GLIBCXX_RESOLVE_LIB_DEFECTS
+  // 2499. operator>>(istream&, char*) makes it hard to avoid buffer overflows
+  template<typename _CharT, typename _Traits, size_t _Num>
+    inline basic_istream<_CharT, _Traits>&
+    operator>>(basic_istream<_CharT, _Traits>& __in, _CharT (&__s)[_Num])
+    {
+      static_assert(_Num <= __gnu_cxx::__numeric_traits<streamsize>::__max);
+      std::__istream_extract(__in, __s, _Num);
+      return __in;
+    }
+
+  template<class _Traits, size_t _Num>
+    inline basic_istream<char, _Traits>&
+    operator>>(basic_istream<char, _Traits>& __in, unsigned char (&__s)[_Num])
+    { return __in >> reinterpret_cast<char(&)[_Num]>(__s); }
+
+  template<class _Traits, size_t _Num>
+    inline basic_istream<char, _Traits>&
+    operator>>(basic_istream<char, _Traits>& __in, signed char (&__s)[_Num])
+    { return __in >> reinterpret_cast<char(&)[_Num]>(__s); }
+#endif
   //@}
 
   /**
diff --git a/libstdc++-v3/include/std/streambuf b/libstdc++-v3/include/std/streambuf
index f8e4cb9879c..3e512364b86 100644
--- a/libstdc++-v3/include/std/streambuf
+++ b/libstdc++-v3/include/std/streambuf
@@ -166,9 +166,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 					       void>::__type
         advance(istreambuf_iterator<_CharT2>&, _Distance);
 
-      template<typename _CharT2, typename _Traits2>
-        friend basic_istream<_CharT2, _Traits2>&
-        operator>>(basic_istream<_CharT2, _Traits2>&, _CharT2*);
+      friend void __istream_extract(istream&, char*, streamsize);
 
       template<typename _CharT2, typename _Traits2, typename _Alloc>
         friend basic_istream<_CharT2, _Traits2>&
diff --git a/libstdc++-v3/src/c++11/istream-inst.cc b/libstdc++-v3/src/c++11/istream-inst.cc
index 2ecfd512b73..2262db6f0cc 100644
--- a/libstdc++-v3/src/c++11/istream-inst.cc
+++ b/libstdc++-v3/src/c++11/istream-inst.cc
@@ -38,9 +38,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template istream& operator>>(istream&, char&);
   template istream& operator>>(istream&, unsigned char&);
   template istream& operator>>(istream&, signed char&);
+
+#if ! _GLIBCXX_INLINE_VERSION
+  // XXX GLIBCXX_ABI Deprecated
   template istream& operator>>(istream&, char*);
   template istream& operator>>(istream&, unsigned char*);
   template istream& operator>>(istream&, signed char*);
+#endif
 
   template istream& operator>>(istream&, _Setfill<char>);
   template istream& operator>>(istream&, _Setiosflags);
@@ -67,7 +71,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template class basic_istream<wchar_t>;
   template wistream& ws(wistream&);
   template wistream& operator>>(wistream&, wchar_t&);
+  template void __istream_extract(wistream&, wchar_t*, streamsize);
+
+#if ! _GLIBCXX_INLINE_VERSION
+  // XXX GLIBCXX_ABI Deprecated
   template wistream& operator>>(wistream&, wchar_t*);
+#endif
 
   template wistream& operator>>(wistream&, _Setfill<wchar_t>);
   template wistream& operator>>(wistream&, _Setiosflags);
diff --git a/libstdc++-v3/src/c++98/istream.cc b/libstdc++-v3/src/c++98/istream.cc
index d2c6794be73..7a48779d337 100644
--- a/libstdc++-v3/src/c++98/istream.cc
+++ b/libstdc++-v3/src/c++98/istream.cc
@@ -204,9 +204,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return *this;
     }
 
-  template<>
-    basic_istream<char>&
-    operator>>(basic_istream<char>& __in, char* __s)
+    void
+    __istream_extract(istream& __in, char* __s, streamsize __num)
     {
       typedef basic_istream<char>       	__istream_type;
       typedef __istream_type::int_type		__int_type;
@@ -223,9 +222,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  __try
 	    {
 	      // Figure out how many characters to extract.
-	      streamsize __num = __in.width();
-	      if (__num <= 0)
-		__num = __gnu_cxx::__numeric_traits<streamsize>::__max;
+	      streamsize __width = __in.width();
+	      if (0 < __width && __width < __num)
+		__num = __width;
 
 	      const __ctype_type& __ct = use_facet<__ctype_type>(__in.getloc());
 
@@ -282,7 +281,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	__err |= ios_base::failbit;
       if (__err)
 	__in.setstate(__err);
-      return __in;
     }
 
 #ifdef _GLIBCXX_USE_WCHAR_T
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/3.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/3.cc
index 2b7f086cbfb..32020b1ba00 100644
--- a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/3.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/3.cc
@@ -39,7 +39,7 @@ void test01()
 
   // template<_CharT, _Traits>
   //  basic_istream& operator>>(istream&, _CharT*)
-  int n = 20;
+  const int n = 20;
   char array1[n];
   typedef std::ios::traits_type ctraits_type;
 
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/4.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/4.cc
index c7c5401da38..9e427cc045f 100644
--- a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/4.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/4.cc
@@ -19,6 +19,7 @@
 
 // 27.6.1.2.3 basic_istream::operator>>
 
+// { dg-do run { target { ! c++20 } } }
 // { dg-require-fileio "" }
 
 #include <istream>
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/9555-ic.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/9555-ic.cc
index c38b7ea023d..d5d86c635df 100644
--- a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/9555-ic.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/9555-ic.cc
@@ -60,9 +60,11 @@ int main()
   testthrow(c);
   testthrow(uc);
   testthrow(sc);
+#if __cplusplus <= 201703L
   testthrow(cp);
   testthrow(scp);
   testthrow(ucp);
+#endif
 
   return 0;
 }
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/9826.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/9826.cc
index 07ee2511058..bc77f7bfb54 100644
--- a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/9826.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/9826.cc
@@ -39,7 +39,7 @@ void test02()
   sstr >> str;
 
   // 2
-  pod_char*  chr = 0;
+  pod_char  chr[1];
   sstr >> chr;
 
   // 3
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/lwg2499.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/lwg2499.cc
new file mode 100644
index 00000000000..d77b7114583
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/lwg2499.cc
@@ -0,0 +1,80 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+// LWG 2499
+// operator>>(basic_istream&, CharT*) makes it hard to avoid buffer overflows
+
+#include <sstream>
+#include <testsuite_hooks.h>
+
+template<typename T>
+void
+test(std::basic_istream<char, T>& in)
+{
+  char pc[3];
+  in >> pc;
+  VERIFY( in.good() );
+  VERIFY( pc[0] == 'a' && pc[1] == 'b' && pc[2] == '\0' );
+
+  signed char sc[4];
+  in >> sc;
+  VERIFY( in.good() );
+  VERIFY( sc[0] == 'c' && sc[1] == 'd' && sc[2] == 'e' && sc[3] ==  '\0' );
+
+  unsigned char uc[4];
+  in >> uc;
+  VERIFY( in.good() );
+  VERIFY( uc[0] == 'f' && uc[1] == 'g' && uc[2] == 'h' && uc[3] ==  '\0' );
+
+  pc[2] = '#';
+  in >> pc;
+  VERIFY( in.good() );
+  VERIFY( pc[0] == 'i' && pc[1] == '\0' && pc[2] == '#' );
+
+  in >> pc;
+  VERIFY( in.good() );
+  VERIFY( pc[0] == 'j' && pc[1] == 'k' && pc[2] == '\0' );
+
+  pc[2] = '#';
+  in >> pc;
+  VERIFY( in.eof() );
+  VERIFY( pc[0] == 'l' && pc[1] == '\0' && pc[2] == '#' );
+}
+
+void
+test01()
+{
+  std::istringstream in("abcdefghi jk l");
+  test(in);
+}
+
+void
+test02()
+{
+  struct CT : std::char_traits<char> { };
+  std::basic_istringstream<char, CT> in("abcdefghi jk l");
+  test(in);
+}
+
+int main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/lwg2499_neg.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/lwg2499_neg.cc
new file mode 100644
index 00000000000..2c2bd521d5c
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/lwg2499_neg.cc
@@ -0,0 +1,45 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+// LWG 2499
+// operator>>(basic_istream&, CharT*) makes it hard to avoid buffer overflows
+
+#include <istream>
+
+void
+test01(std::istream& in, char* pc, signed char* sc, unsigned char* uc)
+{
+  in >> pc; // { dg-error "here" }
+  in >> sc; // { dg-error "here" }
+  in >> uc; // { dg-error "here" }
+}
+
+struct CT : std::char_traits<char> { };
+
+void
+test02(std::basic_istream<char, CT>& in, char* pc, signed char* sc,
+       unsigned char* uc)
+{
+  in >> pc; // { dg-error "here" }
+  in >> sc; // { dg-error "here" }
+  in >> uc; // { dg-error "here" }
+}
+
+// { dg-excess-errors "" }
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/overflow.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/overflow.cc
new file mode 100644
index 00000000000..1141a41b208
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/char/overflow.cc
@@ -0,0 +1,64 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-O2 -std=gnu++98" }
+// { dg-do run }
+
+// This test checks that operator>> will avoid a buffer overflow when
+// reading into a buffer with a size that is known at compile time.
+
+// Since C++20 this is guaranteed (see LWG 2499), for previous standards
+// we try to check the buffer size as an extension (which depends on -O2).
+
+#include <sstream>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+  std::istringstream in("foolish child");
+  char pc[5];
+  in >> pc;
+  VERIFY( in.good() );
+  VERIFY( std::string(pc) == "fool" );
+}
+
+void
+test02()
+{
+  std::istringstream in("foolish");
+  signed char sc[5];
+  in >> sc;
+  VERIFY( in.good() );
+  VERIFY( std::string((const char*)sc) == "fool" );
+}
+
+void
+test03()
+{
+  std::istringstream in("foolish");
+  unsigned char uc[5];
+  in >> uc;
+  VERIFY( in.good() );
+  VERIFY( std::string((const char*)uc) == "fool" );
+}
+
+int
+main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/3.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/3.cc
index 0ba58f74ac4..5ee3ee16692 100644
--- a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/3.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/3.cc
@@ -37,7 +37,7 @@ void test01()
 
   // template<_CharT, _Traits>
   //  basic_istream& operator>>(istream&, _CharT*)
-  int n = 20;
+  const int n = 20;
   wchar_t array1[n];
   typedef std::wios::traits_type ctraits_type;
 
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/4.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/4.cc
index 6c3ee647349..8414d62eee3 100644
--- a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/4.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/4.cc
@@ -20,6 +20,7 @@
 // 27.6.1.2.3 basic_istream::operator>>
 
 // { dg-options "-DMAX_SIZE=466" { target simulator } }
+// { dg-do run { target { ! c++20 } } }
 // { dg-require-fileio "" }
 
 #ifndef MAX_SIZE
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/9555-ic.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/9555-ic.cc
index c4935b7c6ca..1a9f9aa56ba 100644
--- a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/9555-ic.cc
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/9555-ic.cc
@@ -54,7 +54,9 @@ int main()
   wchar_t* cp = &c;
 
   testthrow(c);
+#if __cplusplus <= 201703L
   testthrow(cp);
+#endif
 
   return 0;
 }
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499.cc
new file mode 100644
index 00000000000..e1d42b4bc42
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499.cc
@@ -0,0 +1,70 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+// LWG 2499
+// operator>>(basic_istream&, CharT*) makes it hard to avoid buffer overflows
+
+#include <sstream>
+#include <testsuite_hooks.h>
+
+template<typename T>
+void
+test(std::basic_istream<wchar_t, T>& in)
+{
+  wchar_t wc[3];
+  in >> wc;
+  VERIFY( in.good() );
+  VERIFY( wc[0] == L'a' && wc[1] == L'b' && wc[2] == L'\0' );
+
+  wc[2] = L'#';
+  in >> wc;
+  VERIFY( in.good() );
+  VERIFY( wc[0] == L'c' && wc[1] == L'\0' && wc[2] == L'#' );
+
+  in >> wc;
+  VERIFY( in.good() );
+  VERIFY( wc[0] == L'd' && wc[1] == L'\0' && wc[2] == L'#' );
+
+  wc[2] = L'#';
+  in >> wc;
+  VERIFY( in.eof() );
+  VERIFY( wc[0] == L'e' && wc[1] == L'\0' && wc[2] == L'#' );
+}
+
+void
+test01()
+{
+  std::wistringstream in(L"abc d e");
+  test(in);
+}
+
+void
+test02()
+{
+  struct WT : std::char_traits<wchar_t> { };
+  std::basic_istringstream<wchar_t, WT> in(L"abc d e");
+  test(in);
+}
+
+int main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499_neg.cc b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499_neg.cc
new file mode 100644
index 00000000000..676cdee8297
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499_neg.cc
@@ -0,0 +1,40 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+// LWG 2499
+// operator>>(basic_istream&, CharT*) makes it hard to avoid buffer overflows
+
+#include <istream>
+
+void
+test01(std::wistream& in, wchar_t* wc)
+{
+  in >> wc; // { dg-error "here" }
+}
+
+struct WT : std::char_traits<wchar_t> { };
+
+void
+test02(std::basic_istream<wchar_t, WT>& in, wchar_t* wc)
+{
+  in >> wc; // { dg-error "here" }
+}
+
+// { dg-excess-errors "" }


More information about the Gcc-patches mailing list