libstdc++/1057: Buffer overflow in basic_streambuf<>::xsputn and sputc

peturr@usa.net peturr@usa.net
Wed Dec 13 14:06:00 GMT 2000


>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:


More information about the Gcc-bugs mailing list