libstdc++/88812 C++20 LWG 2499/P0487R1 - Fixing operator>>(basic_istream&, CharT*)

Message ID 20200111012208.GH490107@redhat.com
State New
Headers show
Series
  • libstdc++/88812 C++20 LWG 2499/P0487R1 - Fixing operator>>(basic_istream&, CharT*)
Related show

Commit Message

Jonathan Wakely Jan. 11, 2020, 1:22 a.m.
Here's a patch to replace the unsafe, unbounded operator>> with the
new one that only allows reading into a fixed-size array.

I think I'll sit on this until stage1 though.

The other options are to bump the libstdc++.so version and add it now,
or not bump the version and also add the new symbol on gcc-9-branch.

Patch

diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index edf4485e607..00cae801dfc 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2297,6 +2297,9 @@  GLIBCXX_3.4.28 {
     _ZNSt3pmr25monotonic_buffer_resourceD[0125]Ev;
     _ZT[ISV]NSt3pmr25monotonic_buffer_resourceE;
 
+    # std::__extract_to_array(std:istream&, char*, streamsize)
+    _ZSt18__extract_to_arrayRSiPc[ilx];
+
 } GLIBCXX_3.4.27;
 
 # 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 c82da56b9f9..fb4a73a8539 100644
--- a/libstdc++-v3/include/bits/istream.tcc
+++ b/libstdc++-v3/include/bits/istream.tcc
@@ -958,9 +958,23 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return __in;
     }
 
+#if __cplusplus <= 201703L
   template<typename _CharT, typename _Traits>
     basic_istream<_CharT, _Traits>&
     operator>>(basic_istream<_CharT, _Traits>& __in, _CharT* __s)
+#else
+  template<size_t _Num>
+    basic_istream<char>&
+    operator>>(basic_istream<char>& __in, char (&__s)[_Num])
+    {
+      static_assert(_Num <= __gnu_cxx::__numeric_traits<streamsize>::__max);
+      return std::__extract_to_array(__in, __s, _Num);
+    }
+
+  template<typename _CharT, typename _Traits, size_t _Num>
+    basic_istream<_CharT, _Traits>&
+    operator>>(basic_istream<_CharT, _Traits>& __in, _CharT (&__a)[_Num])
+#endif
     {
       typedef basic_istream<_CharT, _Traits>		__istream_type;
       typedef basic_streambuf<_CharT, _Traits>          __streambuf_type;
@@ -977,8 +991,14 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    {
 	      // Figure out how many characters to extract.
 	      streamsize __num = __in.width();
+#if __cplusplus <= 201703L
 	      if (__num <= 0)
 		__num = __gnu_cxx::__numeric_traits<streamsize>::__max;
+#else
+	      if (__num <= 0 || (size_t)__num > _Num)
+		__num = _Num;
+	      _CharT* __s = __a;
+#endif
 
 	      const __ctype_type& __ct = use_facet<__ctype_type>(__in.getloc());
 
@@ -1048,11 +1068,13 @@  _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&);
+#if __cplusplus <= 201703L
+  extern template istream& operator>>(istream&, char*);
   extern template istream& operator>>(istream&, unsigned char*);
   extern template istream& operator>>(istream&, signed char*);
+#endif
 
   extern template istream& istream::_M_extract(unsigned short&);
   extern template istream& istream::_M_extract(unsigned int&);  
@@ -1074,7 +1096,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   extern template class basic_istream<wchar_t>;
   extern template wistream& ws(wistream&);
   extern template wistream& operator>>(wistream&, wchar_t&);
+#if __cplusplus <= 201703L
   extern template wistream& operator>>(wistream&, wchar_t*);
+#endif
 
   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..47764604eff 100644
--- a/libstdc++-v3/include/std/istream
+++ b/libstdc++-v3/include/std/istream
@@ -789,6 +789,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
    *
    *  If no characters are extracted, sets failbit.
   */
+#if __cplusplus <= 201703L
   template<typename _CharT, typename _Traits>
     basic_istream<_CharT, _Traits>&
     operator>>(basic_istream<_CharT, _Traits>& __in, _CharT* __s);
@@ -807,6 +808,22 @@  _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 // C++20
+  template<typename _CharT, typename _Traits, size_t _Num>
+    basic_istream<_CharT, _Traits>&
+    operator>>(basic_istream<_CharT, _Traits>& __in, _CharT (&__s)[_Num]);
+
+  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..89ccfe27e23 100644
--- a/libstdc++-v3/include/std/streambuf
+++ b/libstdc++-v3/include/std/streambuf
@@ -53,6 +53,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     __copy_streambufs_eof(basic_streambuf<_CharT, _Traits>*,
 			  basic_streambuf<_CharT, _Traits>*, bool&);
 
+  basic_istream<char>&
+  __extract_to_array(basic_istream<char>&, char*, streamsize);
+
   /**
    *  @brief  The actual work of input and output (interface).
    *  @ingroup io
@@ -166,10 +169,6 @@  _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*);
-
       template<typename _CharT2, typename _Traits2, typename _Alloc>
         friend basic_istream<_CharT2, _Traits2>&
         operator>>(basic_istream<_CharT2, _Traits2>&,
@@ -180,6 +179,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
         getline(basic_istream<_CharT2, _Traits2>&,
 		basic_string<_CharT2, _Traits2, _Alloc>&, _CharT2);
 
+      friend basic_istream<char>&
+      __extract_to_array(basic_istream<char>&, char*, streamsize);
+
     protected:
       /*
        *  This is based on _IO_FILE, just reordered to be more consistent,
diff --git a/libstdc++-v3/src/c++98/istream.cc b/libstdc++-v3/src/c++98/istream.cc
index 79d829e23b4..27b1a3f0e64 100644
--- a/libstdc++-v3/src/c++98/istream.cc
+++ b/libstdc++-v3/src/c++98/istream.cc
@@ -192,85 +192,92 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return *this;
     }
 
+  basic_istream<char>&
+  __extract_to_array(basic_istream<char>& __in, char* __s, streamsize __maxnum)
+  {
+    typedef basic_istream<char>       	__istream_type;
+    typedef __istream_type::int_type		__int_type;
+    typedef __istream_type::char_type		__char_type;
+    typedef __istream_type::traits_type	__traits_type;
+    typedef __istream_type::__streambuf_type  __streambuf_type;
+    typedef __istream_type::__ctype_type	__ctype_type;
+
+    streamsize __extracted = 0;
+    ios_base::iostate __err = ios_base::goodbit;
+    __istream_type::sentry __cerb(__in, false);
+    if (__cerb)
+      {
+	__try
+	  {
+	    // Figure out how many characters to extract.
+	    streamsize __num = __in.width();
+	    if (__num <= 0 || __num > __maxnum)
+	      __num = __maxnum;
+
+	    const __ctype_type& __ct = use_facet<__ctype_type>(__in.getloc());
+
+	    const __int_type __eof = __traits_type::eof();
+	    __streambuf_type* __sb = __in.rdbuf();
+	    __int_type __c = __sb->sgetc();
+
+	    while (__extracted < __num - 1
+		   && !__traits_type::eq_int_type(__c, __eof)
+		   && !__ct.is(ctype_base::space,
+			       __traits_type::to_char_type(__c)))
+	      {
+		streamsize __size = std::min(streamsize(__sb->egptr()
+							- __sb->gptr()),
+					     streamsize(__num - __extracted
+							- 1));
+		if (__size > 1)
+		  {
+		    __size = (__ct.scan_is(ctype_base::space,
+					   __sb->gptr() + 1,
+					   __sb->gptr() + __size)
+			      - __sb->gptr());
+		    __traits_type::copy(__s, __sb->gptr(), __size);
+		    __s += __size;
+		    __sb->__safe_gbump(__size);
+		    __extracted += __size;
+		    __c = __sb->sgetc();
+		  }
+		else
+		  {
+		    *__s++ = __traits_type::to_char_type(__c);
+		    ++__extracted;
+		    __c = __sb->snextc();
+		  }
+	      }
+
+	    if (__traits_type::eq_int_type(__c, __eof))
+	      __err |= ios_base::eofbit;
+
+	    // _GLIBCXX_RESOLVE_LIB_DEFECTS
+	    // 68.  Extractors for char* should store null at end
+	    *__s = __char_type();
+	    __in.width(0);
+	  }
+	__catch(__cxxabiv1::__forced_unwind&)
+	  {
+	    __in._M_setstate(ios_base::badbit);
+	    __throw_exception_again;
+	  }
+	__catch(...)
+	  { __in._M_setstate(ios_base::badbit); }
+      }
+    if (!__extracted)
+      __err |= ios_base::failbit;
+    if (__err)
+      __in.setstate(__err);
+    return __in;
+  }
+
   template<>
     basic_istream<char>&
     operator>>(basic_istream<char>& __in, char* __s)
     {
-      typedef basic_istream<char>       	__istream_type;
-      typedef __istream_type::int_type		__int_type;
-      typedef __istream_type::char_type		__char_type;
-      typedef __istream_type::traits_type	__traits_type;
-      typedef __istream_type::__streambuf_type  __streambuf_type;
-      typedef __istream_type::__ctype_type	__ctype_type;
-
-      streamsize __extracted = 0;
-      ios_base::iostate __err = ios_base::goodbit;
-      __istream_type::sentry __cerb(__in, false);
-      if (__cerb)
-	{
-	  __try
-	    {
-	      // Figure out how many characters to extract.
-	      streamsize __num = __in.width();
-	      if (__num <= 0)
-		__num = __gnu_cxx::__numeric_traits<streamsize>::__max;
-
-	      const __ctype_type& __ct = use_facet<__ctype_type>(__in.getloc());
-
-	      const __int_type __eof = __traits_type::eof();
-	      __streambuf_type* __sb = __in.rdbuf();
-	      __int_type __c = __sb->sgetc();
-
-	      while (__extracted < __num - 1
-		     && !__traits_type::eq_int_type(__c, __eof)
-		     && !__ct.is(ctype_base::space,
-				 __traits_type::to_char_type(__c)))
-		{
-		  streamsize __size = std::min(streamsize(__sb->egptr()
-							  - __sb->gptr()),
-					       streamsize(__num - __extracted
-							  - 1));
-		  if (__size > 1)
-		    {
-		      __size = (__ct.scan_is(ctype_base::space,
-					     __sb->gptr() + 1,
-					     __sb->gptr() + __size)
-				- __sb->gptr());
-		      __traits_type::copy(__s, __sb->gptr(), __size);
-		      __s += __size;
-		      __sb->__safe_gbump(__size);
-		      __extracted += __size;
-		      __c = __sb->sgetc();
-		    }
-		  else
-		    {
-		      *__s++ = __traits_type::to_char_type(__c);
-		      ++__extracted;
-		      __c = __sb->snextc();
-		    }
-		}
-
-	      if (__traits_type::eq_int_type(__c, __eof))
-		__err |= ios_base::eofbit;
-
-	      // _GLIBCXX_RESOLVE_LIB_DEFECTS
-	      // 68.  Extractors for char* should store null at end
-	      *__s = __char_type();
-	      __in.width(0);
-	    }
-	  __catch(__cxxabiv1::__forced_unwind&)
-	    {
-	      __in._M_setstate(ios_base::badbit);
-	      __throw_exception_again;
-	    }
-	  __catch(...)
-	    { __in._M_setstate(ios_base::badbit); }
-	}
-      if (!__extracted)
-	__err |= ios_base::failbit;
-      if (__err)
-	__in.setstate(__err);
-      return __in;
+      return std::__extract_to_array(__in, __s,
+	  __gnu_cxx::__numeric_traits<streamsize>::__max);
     }
 
 #ifdef _GLIBCXX_USE_WCHAR_T