String buffer overruns?

N

Nephi Immortal

I am curious to ask a question. Poor buffer handling is implicated
in many security issues that involve buffer overruns. All string
buffers always include null terminator. What happen if you omit null
terminator in source buffer?

const int null_term = 1;
const int nChars = 11;

// null terminator is included automatically by C++ Compiler
char name[ nChars + null_term ] = “Hello World”;

name[ nChars ] = 0xFF; // omit null terminator

std::string str( name );

The valid characters and garbled characters in name are copied into
str until null terminator is found somewhere. It is possible to
trigger error message like denied security alert because data is not
authorized to be read in read-only memory somewhere.

std::string str( name, nChars ); // is better than string str( name )

The std::string( char* ) constructor function should be removed from C+
+ Standard Library. Why do C++ Standard Library leave it alone in
order to have legacy compatibility with C strings?

The C++ Standard Library recommends to use std::string( char*, size )
constructor function instead.

The string class uses dynamic memory allocation. Do C++ Standard
Library offer fixed string buffers which string buffers are pushed
into the stack? Of course, fixed string is less flexible unless large
source buffer does not fit into small destination buffer and extra
characters are truncated if memory reallocation is not used.
 
J

Juha Nieminen

Nephi Immortal said:
The std::string( char* ) constructor function should be removed from C+
+ Standard Library. Why do C++ Standard Library leave it alone in
order to have legacy compatibility with C strings?

How many times have I found it useful to do something like this?

std::string s = "Hello";

Or something like this?

foo("Hello"); // foo() takes a const std::string& as parameter

I'd say about a million times. How many times have I made the mistake of
initializing a std::string with a non-null-terminated char array? Well,
that would be approximately zero times.

I prefer having the huge convenience constructor even at the minuscule
risk of a bug that has yet to happen to me.
 
G

Goran

        I am curious to ask a question.  Poor buffer handling is implicated
in many security issues that involve buffer overruns.  All string
buffers always include null terminator.  What happen if you omit null
terminator in source buffer?

const int null_term = 1;
const int nChars = 11;

// null terminator is included automatically by C++ Compiler
char name[ nChars + null_term ] = “Hello World”;

name[ nChars ] = 0xFF; // omit null terminator

std::string str( name );

        The valid characters and garbled characters in name are copied into
str until null terminator is found somewhere.  It is possible to
trigger error message like denied security alert because data is not
authorized to be read in read-only memory somewhere.

std::string str( name, nChars ); // is better than string str( name )

The std::string( char* ) constructor function should be removed from C+
+ Standard Library.

I disagree. When done right, things work correctly.
 Why do C++ Standard Library leave it alone in
order to have legacy compatibility with C strings?

Having good compatibility with C was always important in C++. So "C
way" of doing strings is supported. I see nothing wrong with that.

In general, yes, C++ coding can be unsafe. However, there are tools
that pinpoint errors pretty early in development cycle. I see nothing
wrong in that approach.
The string class uses dynamic memory allocation.  Do C++ Standard
Library offer fixed string buffers which string buffers are pushed
into the stack?

AFAIK, no. Implementation is free to use "short string optimization".

Since you are asking about buffer overruns, careful: in practice,
overrunning a stack buffer is typically more dangerous than over-
running a heap one ;-).

Goran.
 
J

Juha Nieminen

Juha Nieminen said:
How many times have I found it useful to do something like this?

std::string s = "Hello";

Or something like this?

foo("Hello"); // foo() takes a const std::string& as parameter

Or consider the convenience of this:

int main(int argc, char* argv[])
{
std::vector<std::string> parameters(argv+1, argv+argc);
...
}

What would be your suggestion to make that "safer"?
 
M

Marcel Müller

I am curious to ask a question. Poor buffer handling is implicated
in many security issues that involve buffer overruns. All string
buffers always include null terminator. What happen if you omit null
terminator in source buffer?
The std::string( char* ) constructor function should be removed from C+
+ Standard Library. Why do C++ Standard Library leave it alone in
order to have legacy compatibility with C strings?

First of all there is no std::string( char* ) constructor. There is a
std::string( const char* ) constructor. And this is an important difference.

It is quite difficult to get an accidental string buffer overrun bug
without using char* for strings, because you need to have the
terminating null removed at the first place. Most compilers will at
least warn about that.

Not using char* is mostly sufficient to avoid buffer overruns.
Of course, with invalid data you might still cause memory allocation
errors or excessive memory usage. But while this might be enough for a
DOS attack, it will not help for intrusion attacks.

The C++ Standard Library recommends to use std::string( char*, size )
constructor function instead.

This won't help in any way, because to adapt C strings you need to write
std::string(name, strlen(name)) which shares the same problem, of
course. Writing
const char name[] = "Hello World";
std::string(name, 200);
is entirely wrong, because now you have again a buffer overrun, but this
time in the source buffer. All you can do is
std::string(name, strlen(name, 200));

The string class uses dynamic memory allocation. Do C++ Standard
Library offer fixed string buffers which string buffers are pushed
into the stack? Of course, fixed string is less flexible unless large
source buffer does not fit into small destination buffer and extra
characters are truncated if memory reallocation is not used.

Well, going back to COBOL is definitely not what I like.
How many problems came from too small, fixed string buffers. Long names
of foreign people do not fit into the fields. Moving an application to
an active directory is prevented because the user name with domain does
no longer fit in the fields. An application, originally designed for
local files, seems to work with URLs too - fine. Unfortunately they are
truncated. :-(


Marcel
 
G

gwowen

// null terminator is included automatically by C++ Compiler
char name[ nChars + null_term ] = “Hello World”;

const char* name = "Hello World"; // no need to count chars...
name[ nChars ] = 0xFF; // omit null terminator

Won't compile! Win!
 
N

Nephi Immortal

// null terminator is included automatically by C++ Compiler
char name[ nChars + null_term ] = “Hello World”;

const char* name = "Hello World"; // no need to count chars...
name[ nChars ] = 0xFF; // omit null terminator

Won't compile!  Win!

No way! Will compile sucessfully. You lose!

I wish to do

string str = "anything";

if str does not have sufficient memory, it would be str = "" unless
exception is removed to replace with assertion.

if( !str )
assert(...);
else
cout << str << endl;

You need to implement and add operator!() to string.
 
K

Kevin McCarty

const char* name = "Hello World"; // no need to count chars...
name[ nChars ] = 0xFF; // omit null terminator
Won't compile!  Win!

No way!  Will compile sucessfully.  You lose!

Er, no:

1) if you do 'const char * name = "Hello World";'
then there is no need for 'nChars' variable. Without it, the line
above indeed won't compile,

2) even if for some reason you did have such a variable, the line
still won't compile because it would be attempting to modify the
character referenced by name[nChars], whose type is *const* char.

I wish to do

string str = "anything";

What's stopping you?

if str does not have sufficient memory, it would be str = "" unless
exception is removed to replace with assertion.

Not quite. Yes, insufficient memory means that you would have an
std::bad_alloc exception thrown. But if you catch the exception,
'str' is not "" ... in fact, you cannot refer to 'str' at all: it is
not in scope after the catch block, since it must of necessity be
declared inside a 'try' block in order to be able to catch the
exception coming from the constructor.

An alternative which does pretty much what your previously-posted code
does would be to allocate it with new:

string * strP = 0;
try { strP = new string("anything"); }
catch (const std::bad_alloc &) { assert(...); }

// you can use a reference for syntactic sugar if you like
string & str = *strP;
cout << str << endl;

// clean-up
delete strP;


In C++ 2011 it would be preferable to declare strP as a
std::unique_ptr<string> of course so no clean-up step was needed.


- Kevin B. McCarty
 
G

gwowen

size_t nChars = 12;
const char* name = "Hello World"; // no need to count chars...
name[ nChars ] = 0xFF; // omit null terminator
Won't compile!  Win!

No way!  Will compile sucessfully.  You lose!

Your compiler allows you to modify a const object? Your compiler is
broken.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,967
Messages
2,570,148
Members
46,694
Latest member
LetaCadwal

Latest Threads

Top