basic_istream constructor dereferences unitialized streambuf

Sun Studio C++ 11, Solaris 10 SPARC.

The Rogue Wave C++ standard library documentation describes how

to connect iostream and streambuf objects:

http://www.roguewave.com/support/docs/sourcepro/edition9/html/stdlibug/39-3.htm l

So I make a little program out of the example from the

Rogue Wave documentation:

#include <iostream>

class DerivedStreamBuf : public std::streambuf {

// ...

};

class DerivedOutputStream : public std::ostream {

public:

DerivedOutputStream():

std::ios(0), std::ostream(&dsb) {}//1

// ...

private:

DerivedStreamBuf dsb;

// ...

};

int main(int argc, char *argv[])

{

DerivedOutputStream derivedOutputStream;

std::cout << (derivedOutputStream.fail() ? "fail" : "ok" ) << std::endl;

std::streambuf* buf = derivedOutputStream.rdbuf();

buf->sputc('X');

return 0;

}

Compile and run it with Sun Studio C++ 11:

% CC -g -o buf buf.cpp

% buf

fail

zsh: segmentation fault (core dumped) buf

% dbx buf core

Reading buf

core file header read successfully

Reading ld.so.1

Reading libCstd.so.1

Reading libCrun.so.1

Reading libm.so.2

Reading libc.so.1

Reading libCstd_isa.so.1

Reading libc_psr.so.1

program terminated by signal SEGV (no mapping at the fault address)

0xefa7b06c: sputc+0x0004:ld[%o0 + 52], %o1

Current function is main

22buf->sputc('X');

(dbx) print buf

buf = (nil)

(dbx) where

[1] std::basic_streambuf<char,std::char_traits><char> >::sputc(0x0, 0x58,

0xefb7c064,

0x22a48, 0x0, 0x58000000), at 0xefa7b06c

=>[2] main(argc = 1, argv = 0xffbfe674), line 22 in "buf.cpp"

(dbx) quit

% CC -V

CC: Sun C++ 5.8 Patch 121017-10 2007/02/21

%

I think this is a bug in the Rogue Wave C++ standard library

that is included with Sun Studio 11 C++. The problem is the

basic_istream constructor dereferences the unitialized

streambuf object, from /opt/SC11.0/SUNWspro/prod/include/CC/Cstd/istream.cc:

template<class charT, class traits>

basic_istream<charT, traits>::

basic_istream(basic_streambuf<charT, traits> *sb)

: __chcount(0)

{

if ( sb )

{

if ( sb->which_open_mode() & ios_base::in ) // <=== dereferences streambuf!!!

this->init(sb);

else

this->init(0);

}

else

this->init(0);

}

From the link given above:

In the first case where the stream object does not contain a buffer object,

the C++ standard mandates that no parent class constructors or destructors

(ios, istream, or ostream) access the stream buffer. This restriction is

important, since a derivation such as the following is otherwise unsafe:

class DerivedStreamBuf : public std::streambuf {

// ...

};

class DerivedOutputStream : public std::ostream {

public:

DerivedOutputStream():

std::ios(0), std::ostream(&dsb) {}//1

// ...

private:

DerivedStreamBuf dsb;

// ...

};

Thanks, Mark

[3332 byte] By [markw22a] at [2007-11-27 2:29:00]
# 1

Hi

The link that you posted says

"In the first case where the stream object does not contain a buffer object, the C++ standard mandates that no parent class constructors or destructors (ios, istream, or ostream) access the stream buffer. This restriction is important, since a derivation such as the following is otherwise unsafe:"

i.e., the example shows how *not* to do it.

Perhaps if you change the constructor to

DerivedOutputStream():

std::ios(0), std::ostream() { rdbuf(&dsb); }

then it'll work. Or at least not crash.

Paul

Paul_Floyda at 2007-7-12 2:41:39 > top of Java-index,Development Tools,Solaris and Linux Development Tools...
# 2

Hello Paul,

Thanks. You have a different interpretation to what the

Rogue Wave documentation is saying.

Your suggestion of using the std::basic_istream() default

constructor works with Sun Studio C++ 11 and 12 on Solaris.

However it does not compile with some versions of gcc, as the

default std::basic_istream constructor is not present in the

ISO/IEC 14882 First edition 1998-09-01 Programming languages - C++

standard. The relevant sections of the standard are below.

I'm only pointing out that this difference creates a porting

issue to Solaris Sun Studio C++ 11 (which might be encountered

by other developers). In the tntnet cxxtools

library where this porting issue occurred, the author

has worked around the issue in a very similar way to your

suggestion, calling the

basic_streambuf<charT,traits>* sb) constructor with

a zero pointer:

[code]

class iostream : public std::iostream, public Stream

{

public:

explicit iostream(const Server& server, unsigned bufsize = 256, int timeout = -1)

: std::iostream(0),

Stream(server),

m_buffer(*this, bufsize, timeout)

{

rdbuf(&m_buffer);

}

// ...

private:

streambuf m_buffer;

};

[/code]

Thanks, Mark

[code]

27.6.1.1.1 basic_istream constructors [lib.istream.cons]

explicit basic_istream(basic_streambuf<charT,traits>* sb);

1 Effects: Constructs an object of class basic_istream, assigning initial values to the base class by calling

basic_ios::init(sb) (27.4.4.1).

2 Postcondition: gcount() == 0

virtual ~basic_istream();

3 Effects: Destroys an object of class basic_istream.

4 Notes: Does not perform any operations of rdbuf().

27.4.4.1 basic_ios constructors [lib.basic.ios.cons]

explicit basic_ios(basic_streambuf<charT,traits>* sb);

1 Effects: Constructs an object of class basic_ios, assigning initial values to its member objects by calling

init(sb).

basic_ios();

2 Effects: Constructs an object of class basic_ios (27.4.2.7) leaving its member objects uninitialized.

The object must be initialized by calling its init member function. If it is destroyed before it has been

initialized the behavior is undefined

void init(basic_streambuf<charT,traits>* sb);

3 Postconditions: The postconditions of this function are indicated in Table 89:

Table 89 - ios_base() effects

Element | Value

rdbuf() | sb

tie()| 0

rdstate()| goodbit if sb is not a null pointer, otherwise badbit.

exceptions()| goodbit

flags() | skipws | dec

width() | 0

precision()| 6

fill()| widen(' ');

getloc() | a copy of the value returned by locale()

iarray| a null pointer

parray| a null pointer

[/code]

Message was edited by:

markw22

Removed:

"My interpretation of the section 27.6.1.1.1 Note 4 "Does not

perform any operations of rdbuf()" is that this implies that

no operations are performed on sb."

-> Oops, I misread that, as the note is referring to the

destructor.

markw22a at 2007-7-12 2:41:39 > top of Java-index,Development Tools,Solaris and Linux Development Tools...