Program layout in memory (is anything overwriting my static pointer?)

S

simonl

Hi,

I've been given the job of sorting out a crash in our product for
which we have the crash information and an avi of the event (which
can't possibly match but more of that later...) (btw this is a single
threaded VC9 / win32 app)

The call stack for the bug effectively goes

void* myBuf;

void myFunc()
{

if( myBuf )
{
(maybe some stuff happens here)
do something with myBuf.. except it's NULL in the debug
information I have
}

My question is.. can anything in the heap have overwritten myBuf with
NULL?
Another possibilty is another static variable, say a fixed size array
where we're writing outside the bounds.

My boss just told me we're using a memory manager called Smartheap,
crap knows what that's doing..

Any comments?
 
I

Ian Collins

simonl said:
Hi,

I've been given the job of sorting out a crash in our product for
which we have the crash information and an avi of the event (which
can't possibly match but more of that later...) (btw this is a single
threaded VC9 / win32 app)

The call stack for the bug effectively goes

void* myBuf;

void myFunc()
{

if( myBuf )
{
}

My question is.. can anything in the heap have overwritten myBuf with
NULL?

One the heap? If your application is threaded and writes to myBuf are
unguarded, anything might happen. myBuf isn't static, its global.
 
W

Wolfgang Draxinger

simonl said:
Hi,

I've been given the job of sorting out a crash in our product
for which we have the crash information and an avi of the event
(which can't possibly match but more of that later...) (btw
this is a single threaded VC9 / win32 app)

The call stack for the bug effectively goes

void* myBuf;

void myFunc()
{

if( myBuf )
{
(maybe some stuff happens here)
do something with myBuf.. except it's NULL in the debug
information I have
}

My question is.. can anything in the heap have overwritten
myBuf with NULL?
Another possibilty is another static variable, say a fixed size
array where we're writing outside the bounds.

My boss just told me we're using a memory manager called
Smartheap, crap knows what that's doing..

Any comments?

Testing for NULL != myBuf (note in C you should always test
against the NULL macro explicitly, it may be defined as not
being of value 0 on all systems, 0xffffffff may be as common),
just tells you if the pointer has been marked invalid/NIL
explicitly. It doesn't tell you however if it's valid.

You may have a perfectly non-NULL pointer, that's still not
pointing into valid memory.

Eventually you expect myBuf to be NULL if it's not initialized.
(Big) surprise though: Variables not being initialized
explicitly can have any value until getting a value assigned.

So add these two changes to your program, and see if it works
then:

- void* myBuf;
+ void *myBuf = NULL;

- if( myBuf )
+ if( NULL != myBuf )

And you might try test your program with a debugger, set a watch
on myBuf, to see when it changes.

Wolfgang Draxinger
 
R

Richard Bos

Wolfgang Draxinger said:
Testing for NULL != myBuf (note in C you should always test
against the NULL macro explicitly, it may be defined as not
being of value 0 on all systems, 0xffffffff may be as common),

This is nonsense. A constant integer expression with the value zero
(such as, ooh, perhaps 0) will _always_ compare equal to a null pointer,
no matter how that null pointer is represented in memory; and a boolean
test against any scalar, including pointers, _always_ happens as if it
was written to include !=0.

Richard
 
S

simonl

myBuf isn't static, its global.

OK, I'm not too clear on this. Both global and static variables in
this case have the same scope and lifetime, but are stored in
different areas of process memory?
 
B

Ben Bacarisse

simonl said:
I've been given the job of sorting out a crash in our product for
which we have the crash information and an avi of the event (which
can't possibly match but more of that later...) (btw this is a single
threaded VC9 / win32 app)

The call stack for the bug effectively goes

void* myBuf;

void myFunc()
{

if( myBuf )
{
(maybe some stuff happens here)
do something with myBuf.. except it's NULL in the debug
information I have
}

My question is.. can anything in the heap have overwritten myBuf with
NULL?
Another possibilty is another static variable, say a fixed size array
where we're writing outside the bounds.

You need to ask yourself if the debug information is reliable. I have
no reason to doubt it, but do be sure. You can waste a lot of time
using unreliable information.

If the data is sound then something has altered myBuf between testing
it and the crash point where your debug data shows it to be NULL. The
most likely suspects are, as you say, an out of bounds access to a
nearly object, but another possibility is access though an invalid
pointer. Is there a lot of code being executed between the if and the
reported crash? Can you reproduce the error or must you debug this
fro one debug data set?
My boss just told me we're using a memory manager called Smartheap,
crap knows what that's doing..

I don't like the sound of that, but I know nothing about it.
 
C

Chris Dollin

Wolfgang said:
simonl wrote:

(fx:snip)
Testing for NULL != myBuf (note in C you should always test
against the NULL macro explicitly,
False.

it may be defined as not
being of value 0 on all systems, 0xffffffff may be as common),

True but irrelevant; null pointers are /required/ to behave
like 0 in a condition, just as they are /required/ to compare
equal to null pointer constants in comparisions. If the bitwise
representation of a null pointer is 0xffffffff, then the compiler
must insert the appropriate code, just as it must for

int *mylittlenullpointer = 0;
Eventually you expect myBuf to be NULL if it's not initialized.
(Big) surprise though: Variables not being initialized
explicitly can have any value until getting a value assigned.

Not static variables; they are implicitly initialised to
suitable zeros. So, if they're pointers, they get initialsed
the null pointer.
 
V

viza

Testing for NULL != myBuf (note in C you should always test against the
NULL macro explicitly, it may be defined as not being of value 0 on all
systems, 0xffffffff may be as common)

Since:

if( NULL == ptr )

is always exactly equivalent to:

if( ! ptr )

I prefer:

if( !! ptr )

to

if( NULL != ptr )

the two are equivalent.
 
R

Richard Bos

viza said:
Since:

if( NULL == ptr )

is always exactly equivalent to:

if( ! ptr )

I prefer:

if( !! ptr )

to

if( NULL != ptr )

the two are equivalent.

And surprise, surprise, they're both also equivalent to the superior

if (ptr)

Richard
 
V

viza

Hi

And surprise, surprise, they're both also equivalent to the superior

if (ptr)

yes.

(I had the idea that the argument of if() was converted to int, which
would invoke undefined behaviour).
 
J

James Kuyper

Wolfgang said:
....
Testing for NULL != myBuf (note in C you should always test
against the NULL macro explicitly, it may be defined as not
being of value 0 on all systems, 0xffffffff may be as common),

if(myBuf) is required by the standard to be exactly equivalent to
if(myBuf!=0); whether or not you choose to take advantage of that fact
is a matter of style, it doesn't affect the required behavior of your
program.

NULL cannot legally expand to 0xffffffff. It might, when converted to a
pointer, convert to a pointer whose bit representation if stored in an
object would be 0xffffffff; however, the same is true of 0 when compared
with a pointer. Both NULL and 0 are null pointer constants; when
compared with myBuf, both are converted into null pointers. If myBuf is
a null pointer, it must compare equal to the null pointer that is the
left operand of the != operator, regardless of whether that null pointer
was created by converting NULL or converting 0.

The reason for using NULL rather than 0 in this context is
documentation, not functionality. Use of NULL rather than 0 is a strong
hint that this might be a pointer context. It is, unfortunately, not
proof that it's a pointer context. People frequently make the mistake of
using NULL in arithmetic contexts. I would use if(myBuf != NULL) only if
I felt a need to draw attention to the fact that myBuf is a pointer type.

....
Eventually you expect myBuf to be NULL if it's not initialized.
(Big) surprise though: Variables not being initialized
explicitly can have any value until getting a value assigned.

That's true only for variables with automatic storage duration. myBuf
was declared at file scope, so it automatically has static storage
duration. Objects with static storage duration that are not explicitly
initialized are implicitly zero-initialized at the start of the program.
For pointer types, zero-initialization leaves the pointer with a null
value. Therefore, the following code change doesn't have any effect on
the behavior of the code:
 
J

James Kuyper

simonl said:
OK, I'm not too clear on this. Both global and static variables in
this case have the same scope and lifetime, but are stored in
different areas of process memory?

I think I know what you mean by "global variables", but the term isn't
used by the standard, and at least one person who posts here regularly
uses a definition for "global variables" which is incompatible with C,
and complains about people who use the term in any different fashion.

The standard uses the term "file scope" to describe what I think you're
referring to, and I will use that term in my answer.

All variables with file scope also have static storage duration.
However, some variables with static storage duration have block scope
rather than file scope. Example:

int main(void)
{
static int mystat = 0;
return mystat;
}

Variables with file scope have a scope which starts after completion of
their declarators, and continues to the end of the translation unit.

Variables with block scope have a scope that starts after completion of
their declarators, and continues to the end of the block in which they
were defined.

The standard says nothing about how process memory is laid out. Many
implementations will store file scope variables in a different location
than block scope variables, even when those variables have static
storage duration. However, in most cases it shouldn't matter to you
where the objects are stored; that's an issue for the compiler and
linker to worry about, not you.
 
J

James Kuyper

viza wrote:
....
(I had the idea that the argument of if() was converted to int, which
would invoke undefined behaviour).

Incorrect. It is compared to 0; with pointers, this implies an implicit
conversion of 0 to a null pointer of the same type; not a conversion of
the pointer to int.
 
K

Keith Thompson

James Kuyper said:
Variables with file scope have a scope which starts after completion
of their declarators, and continues to the end of the translation unit.

Variables with block scope have a scope that starts after completion
of their declarators, and continues to the end of the block in which
they were defined.

The standard says nothing about how process memory is laid out. Many
implementations will store file scope variables in a different
location than block scope variables, even when those variables have
static storage duration.

Really? I would think it would make sense for all objects with static
storage duration to be stored together, whether they have file scope
or block scope.
However, in most cases it shouldn't matter to
you where the objects are stored; that's an issue for the compiler and
linker to worry about, not you.

Ah, but in this case he's trying to track down a variable that's being
accidentally clobbered, so he does have to worry about memory layout.
His program is presumably exhibiting undefined behavior; using
implementation-specific knowledge about memory layout could be a good
way to diagnose and fix the problem.
 
K

Keith Thompson

Chris Dollin said:
Wolfgang Draxinger wrote: [...]
Testing for NULL != myBuf (note in C you should always test
against the NULL macro explicitly,

False.
[...]

Actually, I agree with Wolfgang, though obviously not with the
reasoning that led to his conclusion. I prefer tests like
if (myBuf != NULL)
rather than
if (myBuf)
just because I find the former clearer, even though they're
semantically equivalent. (And I dislike "if (NULL != myBuf)";
yes, I understand why people write it that way, and I know it means
exactly the same thing.)
 
W

Wolfgang Draxinger

Richard said:
This is nonsense. A constant integer expression with the value
zero (such as, ooh, perhaps 0) will _always_ compare equal to a
null pointer, no matter how that null pointer is represented in
memory; and a boolean test against any scalar, including
pointers, _always_ happens as if it was written to include !=0.

That is not to argue about. However some systems may define
NULL != 0, even though such pointers would be not null pointers
in the sense of the C standard.

This might be neccesary on architectures where data can and is
placed perfectly well at address 0. Think about Harvard
architecture, with the heap starting at 0...
To be better safe than sorry, IMHO someone should always test
pointers against the NULL macro, to catch such custom deviations
from the standard.

Anyway _just_ testing testing against the NULL macro or being 0
won't tell, if the pointer is valid. The NULL value is merely a
tag being applied explicitly.

Wolfgang Draxinger
 
N

Nate Eldredge

Wolfgang Draxinger said:
That is not to argue about. However some systems may define
NULL != 0, even though such pointers would be not null pointers
in the sense of the C standard.

This might be neccesary on architectures where data can and is
placed perfectly well at address 0. Think about Harvard
architecture, with the heap starting at 0...
To be better safe than sorry, IMHO someone should always test
pointers against the NULL macro, to catch such custom deviations
from the standard.

This would be a pretty severe deviation, IMHO. The NULL macro is
required to expand to a null pointer constant (7.17 (3)), which is
defined in 6.3.2.3 (3) as an integer constant expression with the value
0. And 6.3.2.3 (4) requires that any two null pointers shall compare
equal.

If we had a machine where address 0 was legal, and we wanted to use
0xdeadbeef as the null pointer instead, then AIUI a conforming
implementation would still have to define NULL as 0 or (void *)0 or some
variant, and arrange that any attempt to assign the constant value 0 to
a pointer resulted in it having the physical value 0xdeadbeef, and that
the physical value 0xdeadbeef would compare equal to the constant value
0.

Do you know of an implementation that does something else? All sorts of
deviations are possible, of course, but you can't check for all of them,
and at some point you just have to trust your compiler.
Anyway _just_ testing testing against the NULL macro or being 0
won't tell, if the pointer is valid. The NULL value is merely a
tag being applied explicitly.

Of course. There are probably lots of invalid pointers other than
NULL. And even if the pointer is valid, it doesn't mean it points
somewhere reasonable.
 
J

James Kuyper

Wolfgang said:
That is not to argue about. However some systems may define
NULL != 0, even though such pointers would be not null pointers
in the sense of the C standard.

A NULL which did not compare equal to 0 would render the implementation
non-conforming. The standard only allows two possibilities for the
expansion of NULL:

1. an integer constant expression with a value of 0. Such an expression
must necessarily compare equal to 0.

2. An integer constant expression with a value of 0 converted to
(void*). When such an expression appears in a comparison with 0, it
become a null pointer with the type (void*). As a result, before the
comparison occurs, the 0 is also converted to (void*). In this context,
the result of such a comparison is a null pointer. All null pointers
must compare equal.
This might be neccesary on architectures where data can and is
placed perfectly well at address 0. Think about Harvard
architecture, with the heap starting at 0...

That's no problem. When NULL is converted to a pointer object, there's
no reason why that pointer object cannot contain a representation of a
non-zero address. However, it is a null pointer, and must therefore
compare equal to 0. On an architecture like you describe, a pointer
representing an address of 0 must NOT compare equal to 0.
To be better safe than sorry, IMHO someone should always test
pointers against the NULL macro, to catch such custom deviations
from the standard.

No, any such deviation means that the compiler should be rejected, not
that the code should be re-written. If they can't get even this
fundamental point right, there's no trusting them to get any of the more
complicated features of the language right.
 

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

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top