This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Patch] Avoid overflowing stringbuf::setbuf buffers


Hi,

the issue here is ultimately that in general the constructor
string::string(const charT*, size_type n, ...) doesn't guarantee
that the capacity of the constructed string is == n. In our
current implementation this actually happens for short strings
(~< 128) but not for larger strings (see _S_create).

Thus, a stringbuf buffer can be overflowed (see testcase), and
this is a *bad* thing even if the standard doesn't say much
about stringbuf::setbuf.

The below is the minimal, ABI-preserving, patch I could come
up with, regtested (x86/x86_64-linux) and performance tested:
actually, thanks to the overflow tweak, for this testcase, on
my usual P4-2400:

#include <sstream>

int main()
{
 char array[32];

 for (int i = 0; i < 10000000; ++i)
   {
     std::stringbuf sbuf;
     sbuf.sputn(array, sizeof(array));
   }
}

current
-------
6.820u 0.010s 0:06.84 99.8%     0+0k 0+0io 196pf+0w

current + patch
---------------
6.440u 0.020s 0:06.46 100.0%    0+0k 0+0io 193pf+0w

Nevertheless, for v7 we may reconsider redesigning a bit the
various stringbuf helper functions.

Will wait 'til tomorrow morning.

Paolo.

////////////////
2004-10-06  Paolo Carlini  <pcarlini@suse.de>

	* include/std/std_sstream.h (_M_sync): When the caller is
	setbuf, don't trust _M_string.capacity() to be the size of
	the buffer area, use _M_string.size() in this case.
	* testsuite/27_io/basic_stringbuf/setbuf/char/4.cc: New.
	* testsuite/27_io/basic_stringbuf/setbuf/wchar_t/4.cc: Likewise.

	* include/bits/sstream.tcc (overflow): Avoid calling string::assign
	unnecessarily when the current _M_string is empty.
diff -urN libstdc++-v3-orig/include/bits/sstream.tcc libstdc++-v3/include/bits/sstream.tcc
--- libstdc++-v3-orig/include/bits/sstream.tcc	2004-10-03 13:33:53.000000000 +0200
+++ libstdc++-v3/include/bits/sstream.tcc	2004-10-05 15:36:12.000000000 +0200
@@ -109,7 +109,8 @@
 	  const __size_type __len = std::min(__opt_len, __max_size);
 	  __string_type __tmp;
 	  __tmp.reserve(__len);
-	  __tmp.assign(_M_string.data(), this->epptr() - this->pbase());
+	  if (this->pbase())
+	    __tmp.assign(this->pbase(), this->epptr() - this->pbase());
 	  _M_string.swap(__tmp);
 	  _M_sync(const_cast<char_type*>(_M_string.data()),
 		  this->gptr() - this->eback(), this->pptr() - this->pbase());
diff -urN libstdc++-v3-orig/include/std/std_sstream.h libstdc++-v3/include/std/std_sstream.h
--- libstdc++-v3-orig/include/std/std_sstream.h	2004-10-01 11:22:49.000000000 +0200
+++ libstdc++-v3/include/std/std_sstream.h	2004-10-05 18:24:24.000000000 +0200
@@ -259,7 +259,13 @@
 	  this->setg(__base, __base + __i, __end);
 	if (__testout)
 	  {
-	    this->setp(__base, __base + _M_string.capacity());
+	    // If __base comes from setbuf we cannot trust capacity()
+	    // to match the size of the buffer area: in general, after
+	    // Step 1 above, _M_string.capacity() >= __n.
+	    if (__base == _M_string.data())
+	      this->setp(__base, __base + _M_string.capacity());
+	    else
+	      this->setp(__base, __end);
 	    this->pbump(__o);
 	    // egptr() always tracks the string end. When !__testin,
 	    // for the correct functioning of the streambuf inlines
diff -urN libstdc++-v3-orig/testsuite/27_io/basic_stringbuf/setbuf/char/4.cc libstdc++-v3/testsuite/27_io/basic_stringbuf/setbuf/char/4.cc
--- libstdc++-v3-orig/testsuite/27_io/basic_stringbuf/setbuf/char/4.cc	1970-01-01 01:00:00.000000000 +0100
+++ libstdc++-v3/testsuite/27_io/basic_stringbuf/setbuf/char/4.cc	2004-10-05 18:01:41.000000000 +0200
@@ -0,0 +1,58 @@
+// 2004-10-05  Paolo Carlini  <pcarlini@suse.de>
+
+// Copyright (C) 2004 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 2, 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 COPYING.  If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// 27.8.1.4 Overridden virtual functions
+
+#include <sstream>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+  using namespace std;
+  bool test __attribute__((unused)) = true;
+
+  const unsigned max_size = 1 << 18;
+
+  char ref[max_size];
+  std::memset(ref, '\0', max_size);
+
+  char src[max_size * 2];
+  std::memset(src, '\1', max_size * 2);
+
+  for (unsigned i = 128; i <= max_size; i *= 2)
+    {
+      char* dest = new char[i * 2];
+      std::memset(dest, '\0', i * 2);
+
+      stringbuf sbuf;
+      sbuf.pubsetbuf(dest, i);
+
+      sbuf.sputn(src, i * 2);
+      VERIFY( !std::memcmp(dest + i, ref, i) );
+      
+      delete[] dest;
+    }
+}
+
+int main()
+{
+  test01();
+  return 0;
+}
diff -urN libstdc++-v3-orig/testsuite/27_io/basic_stringbuf/setbuf/wchar_t/4.cc libstdc++-v3/testsuite/27_io/basic_stringbuf/setbuf/wchar_t/4.cc
--- libstdc++-v3-orig/testsuite/27_io/basic_stringbuf/setbuf/wchar_t/4.cc	1970-01-01 01:00:00.000000000 +0100
+++ libstdc++-v3/testsuite/27_io/basic_stringbuf/setbuf/wchar_t/4.cc	2004-10-05 18:00:24.000000000 +0200
@@ -0,0 +1,58 @@
+// 2004-10-05  Paolo Carlini  <pcarlini@suse.de>
+
+// Copyright (C) 2004 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 2, 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 COPYING.  If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// 27.8.1.4 Overridden virtual functions
+
+#include <sstream>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+  using namespace std;
+  bool test __attribute__((unused)) = true;
+
+  const unsigned max_size = 1 << 18;
+
+  wchar_t ref[max_size];
+  std::wmemset(ref, L'\0', max_size);
+
+  wchar_t src[max_size * 2];
+  std::wmemset(src, L'\1', max_size * 2);
+
+  for (unsigned i = 128; i <= max_size; i *= 2)
+    {
+      wchar_t* dest = new wchar_t[i * 2];
+      std::wmemset(dest, L'\0', i * 2);
+
+      wstringbuf sbuf;
+      sbuf.pubsetbuf(dest, i);
+
+      sbuf.sputn(src, i * 2);
+      VERIFY( !std::wmemcmp(dest + i, ref, i) );
+      
+      delete[] dest;
+    }
+}
+
+int main()
+{
+  test01();
+  return 0;
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]