This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
libstdc++/1057: Buffer overflow in basic_streambuf<>::xsputn and sputc
- To: gcc-gnats at gcc dot gnu dot org
- Subject: libstdc++/1057: Buffer overflow in basic_streambuf<>::xsputn and sputc
- From: peturr at usa dot net
- Date: 13 Dec 2000 22:01:47 -0000
- Reply-To: peturr at usa dot net
>Number: 1057
>Category: libstdc++
>Synopsis: Buffer overflow in basic_streambuf<>::xsputn and sputc
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: unassigned
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Wed Dec 13 14:06:00 PST 2000
>Closed-Date:
>Last-Modified:
>Originator: Petur Runolfsson
>Release: gcc version 2.97 20001211 (experimental)
>Organization:
>Environment:
i586-pc-linux-gnu with glibc 2.1.3, binutils 2.10
>Description:
basic_streambuf<> manages the put buffer incorrectly.
If a put buffer has been set with setp, changing to a
smaller buffer or turning buffering off causes a segfault
on the next write.
>How-To-Repeat:
// The following code always crashes:
#include <streambuf>
#include <string>
#include <cassert>
class setpbuf : public std::streambuf
{
public:
setpbuf()
{
char foo [32];
setp(foo, foo + 32);
setp(buffer, buffer + 4);
}
~setpbuf()
{
sync();
}
virtual int_type overflow(int_type n)
{
if (sync() != 0)
return traits_type::eof();
result += traits_type::to_char_type(n);
return n;
}
virtual int sync()
{
result.append(pbase(), pptr());
setp(buffer, buffer + 4);
return 0;
}
char buffer [4];
std::string result;
};
void test01()
{
std::string text = "abcdefghijklmn";
setpbuf sp1;
// Here xsputn writes over sp1.result
sp1.sputn(text.c_str(), text.length());
// This crashes when result is accessed
sp1.pubsync();
setpbuf sp2;
for (std::string::size_type i = 0; i < text.length(); ++i)
{
// sputc also writes over result
sp2.sputc(text[i]);
}
// Crash here
sp2.pubsync();
}
class nullsetpbuf : public std::streambuf
{
public:
nullsetpbuf()
{
char foo [64];
setp(foo, foo + 64);
setp(NULL, NULL);
}
};
void test02()
{
std::string text1 = "abcdefghijklmn";
nullsetpbuf nsp;
// Immediate crash as xsputn writes to null pointer
nsp.sputn(text1.c_str(), text1.length());
// ditto
nsp.sputc('a');
}
int main()
{
test01();
test02();
return 0;
}
>Fix:
The basic_streambuf<> members _M_mode and _M_buf_size store
incorrect information about the putbuffer. The problem can
be fixed by just removing them and using pbump, pptr(),
epptr() and pbase() instead. Here is xsputn with this
approach:
template<typename _CharT, typename _Traits>
streamsize
basic_streambuf<_CharT, _Traits>::
xsputn(const char_type* __s, streamsize __n)
{
streamsize __ret = 0;
// This covers the case __n == 0
while (__ret < __n)
{
streamsize __buffer_length = this->pptr() ?
(this->epptr() - this->pptr()) :
0;
if (__buffer_length > 0)
{
streamsize __remaining = __n - __ret;
streamsize __len = min(__buffer_length, __remaining);
traits_type::copy(this->pptr(), __s, __len);
__ret += __len;
__s += __len;
this->pbump(__len);
}
if (__ret < __n)
{
int_type __c = traits_type::to_int_type(*__s);
int_type __overfc = this->overflow(__c);
if (traits_type::eq_int_type(__c, __overfc))
{
++__ret;
++__s;
}
else
break;
}
}
return __ret;
}
>Release-Note:
>Audit-Trail:
>Unformatted: