public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
From: Giuliano Belinassi <giulianob@gcc.gnu.org>
To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org
Subject: [gcc(refs/users/giulianob/heads/autopar_rebase2)] libstdc++: Avoid overflow in istream::get(streambuf&) [LWG 3464]
Date: Tue, 18 Aug 2020 01:01:22 +0000 (GMT)	[thread overview]
Message-ID: <20200818010122.4659E398680F@sourceware.org> (raw)

https://gcc.gnu.org/g:76bc35dced1f6b0d4c1774e8cd0e23724d6d9a40

commit 76bc35dced1f6b0d4c1774e8cd0e23724d6d9a40
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Mon Jul 20 20:06:46 2020 +0100

    libstdc++: Avoid overflow in istream::get(streambuf&) [LWG 3464]
    
    Similar to the recent changes to basic_istream::ignore, this change
    ensures that _M_gcount doesn't overflow when extracting characters and
    inserting them into another streambuf.
    
    The solution used here is to use unsigned long long for the count. We
    assume that the number of characters extracted won't exceed the maximum
    value for that type, but even if it does we avoid any undefined
    behaviour.
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/istream.tcc
            (basic_istream::get(__streambuf_type&, char_type): Use unsigned
            long long for counter and check if it would overflow _M_gcount.
            * testsuite/27_io/basic_istream/get/char/lwg3464.cc: New test.
            * testsuite/27_io/basic_istream/get/wchar_t/lwg3464.cc: New test.

Diff:
---
 libstdc++-v3/include/bits/istream.tcc              |  9 ++-
 .../27_io/basic_istream/get/char/lwg3464.cc        | 91 ++++++++++++++++++++++
 .../27_io/basic_istream/get/wchar_t/lwg3464.cc     | 91 ++++++++++++++++++++++
 3 files changed, 190 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/include/bits/istream.tcc b/libstdc++-v3/include/bits/istream.tcc
index 5983e51873f..0289867c50b 100644
--- a/libstdc++-v3/include/bits/istream.tcc
+++ b/libstdc++-v3/include/bits/istream.tcc
@@ -375,17 +375,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      __streambuf_type* __this_sb = this->rdbuf();
 	      int_type __c = __this_sb->sgetc();
 	      char_type __c2 = traits_type::to_char_type(__c);
+	      unsigned long long __gcount = 0;
 
 	      while (!traits_type::eq_int_type(__c, __eof)
 		     && !traits_type::eq_int_type(__c, __idelim)
 		     && !traits_type::eq_int_type(__sb.sputc(__c2), __eof))
 		{
-		  ++_M_gcount;
+		  ++__gcount;
 		  __c = __this_sb->snextc();
 		  __c2 = traits_type::to_char_type(__c);
 		}
 	      if (traits_type::eq_int_type(__c, __eof))
 		__err |= ios_base::eofbit;
+	      // _GLIBCXX_RESOLVE_LIB_DEFECTS
+	      // 3464. istream::gcount() can overflow
+	      if (__gcount <= __gnu_cxx::__numeric_traits<streamsize>::__max)
+		_M_gcount = __gcount;
+	      else
+		_M_gcount = __gnu_cxx::__numeric_traits<streamsize>::__max;
 	    }
 	  __catch(__cxxabiv1::__forced_unwind&)
 	    {
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/get/char/lwg3464.cc b/libstdc++-v3/testsuite/27_io/basic_istream/get/char/lwg3464.cc
new file mode 100644
index 00000000000..6123ca5b713
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/get/char/lwg3464.cc
@@ -0,0 +1,91 @@
+// 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-do run { target { ! lp64 } } }
+
+#include <istream>
+#include <streambuf>
+#include <limits>
+#include <testsuite_hooks.h>
+
+typedef char C;
+
+struct buff : std::basic_streambuf<C>
+{
+  typedef std::streamsize		  streamsize;
+  typedef std::numeric_limits<streamsize> limits;
+
+  buff() : count(0), buf() { }
+
+  int_type underflow()
+  {
+    // Number of characters left until we overflow the counter
+    const streamsize headroom = limits::max() - count;
+
+    if (headroom == 0)
+      return traits_type::eof();
+
+    if (bufsz < headroom)
+      count += bufsz;
+    else
+      count = limits::max();
+
+    this->setg(buf + 1, buf + 1, buf + bufsz);
+
+    return buf[0];
+  }
+
+  int_type overflow(int_type c)
+  {
+    if (traits_type::eq_int_type(c , traits_type::eof()))
+      return c;
+    this->setp(buf, buf + bufsz - 1);
+    return traits_type::not_eof(c);
+  }
+
+  streamsize count;
+
+  static const streamsize bufsz = (2048 << limits::digits10) + 1;
+  char_type buf[bufsz];
+};
+
+void
+test01()
+{
+  // Not possible to overflow 64-bit streamsize in reasonable time.
+  if (std::numeric_limits<std::streamsize>::digits > 32)
+    return;
+
+  std::basic_istream<C> in(new buff);
+  buff out;
+  in.get(out);
+  VERIFY( in.gcount() == std::numeric_limits<std::streamsize>::max() );
+
+  delete in.rdbuf(new buff);
+
+  in.get();
+  VERIFY( in.gcount() == 1 );
+
+  in.get(out, 'a');
+  VERIFY( in.gcount() == std::numeric_limits<std::streamsize>::max() );
+}
+
+int
+main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/get/wchar_t/lwg3464.cc b/libstdc++-v3/testsuite/27_io/basic_istream/get/wchar_t/lwg3464.cc
new file mode 100644
index 00000000000..6df244cc32d
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_istream/get/wchar_t/lwg3464.cc
@@ -0,0 +1,91 @@
+// 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-do run { target { ! lp64 } } }
+
+#include <istream>
+#include <streambuf>
+#include <limits>
+#include <testsuite_hooks.h>
+
+typedef wchar_t C;
+
+struct buff : std::basic_streambuf<C>
+{
+  typedef std::streamsize		  streamsize;
+  typedef std::numeric_limits<streamsize> limits;
+
+  buff() : count(0), buf() { }
+
+  int_type underflow()
+  {
+    // Number of characters left until we overflow the counter
+    const streamsize headroom = limits::max() - count;
+
+    if (headroom == 0)
+      return traits_type::eof();
+
+    if (bufsz < headroom)
+      count += bufsz;
+    else
+      count = limits::max();
+
+    this->setg(buf + 1, buf + 1, buf + bufsz);
+
+    return buf[0];
+  }
+
+  int_type overflow(int_type c)
+  {
+    if (traits_type::eq_int_type(c , traits_type::eof()))
+      return c;
+    this->setp(buf, buf + bufsz - 1);
+    return traits_type::not_eof(c);
+  }
+
+  streamsize count;
+
+  static const streamsize bufsz = (2048 << limits::digits10) + 1;
+  char_type buf[bufsz];
+};
+
+void
+test01()
+{
+  // Not possible to overflow 64-bit streamsize in reasonable time.
+  if (std::numeric_limits<std::streamsize>::digits > 32)
+    return;
+
+  std::basic_istream<C> in(new buff);
+  buff out;
+  in.get(out);
+  VERIFY( in.gcount() == std::numeric_limits<std::streamsize>::max() );
+
+  delete in.rdbuf(new buff);
+
+  in.get();
+  VERIFY( in.gcount() == 1 );
+
+  in.get(out, 'a');
+  VERIFY( in.gcount() == std::numeric_limits<std::streamsize>::max() );
+}
+
+int
+main()
+{
+  test01();
+}


                 reply	other threads:[~2020-08-18  1:01 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200818010122.4659E398680F@sourceware.org \
    --to=giulianob@gcc.gnu.org \
    --cc=gcc-cvs@gcc.gnu.org \
    --cc=libstdc++-cvs@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).