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

Discussion in 'C Programming' started by simonl, Oct 9, 2008.

  1. simonl

    simonl Guest

    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?
     
    simonl, Oct 9, 2008
    #1
    1. Advertisements

  2. simonl

    Ian Collins Guest

    One the heap? If your application is threaded and writes to myBuf are
    unguarded, anything might happen. myBuf isn't static, its global.
     
    Ian Collins, Oct 9, 2008
    #2
    1. Advertisements

  3. 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
     
    Wolfgang Draxinger, Oct 9, 2008
    #3
  4. simonl

    Richard Bos Guest

    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
     
    Richard Bos, Oct 9, 2008
    #4
  5. simonl

    simonl Guest

    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?
     
    simonl, Oct 9, 2008
    #5
  6. I seems not.
     
    Ben Bacarisse, Oct 9, 2008
    #6
  7. 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?
    I don't like the sound of that, but I know nothing about it.
     
    Ben Bacarisse, Oct 9, 2008
    #7
  8. simonl

    Chris Dollin Guest

    (fx:snip)
    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;
    Not static variables; they are implicitly initialised to
    suitable zeros. So, if they're pointers, they get initialsed
    the null pointer.
     
    Chris Dollin, Oct 9, 2008
    #8
  9. simonl

    viza Guest

    Since:

    if( NULL == ptr )

    is always exactly equivalent to:

    if( ! ptr )

    I prefer:

    if( !! ptr )

    to

    if( NULL != ptr )

    the two are equivalent.
     
    viza, Oct 9, 2008
    #9
  10. simonl

    Richard Bos Guest

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

    if (ptr)

    Richard
     
    Richard Bos, Oct 9, 2008
    #10
  11. simonl

    viza Guest

    Hi

    yes.

    (I had the idea that the argument of if() was converted to int, which
    would invoke undefined behaviour).
     
    viza, Oct 9, 2008
    #11
  12. simonl

    James Kuyper Guest

    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.

    ....
    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:
     
    James Kuyper, Oct 9, 2008
    #12
  13. simonl

    James Kuyper Guest

    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.
     
    James Kuyper, Oct 9, 2008
    #13
  14. simonl

    James Kuyper Guest

    viza wrote:
    ....
    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.
     
    James Kuyper, Oct 9, 2008
    #14
  15. 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.
    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.
     
    Keith Thompson, Oct 9, 2008
    #15
  16. [...]

    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.)
     
    Keith Thompson, Oct 9, 2008
    #16
  17. Have you tried Googling Smartheap?
     
    Keith Thompson, Oct 9, 2008
    #17
  18. 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
     
    Wolfgang Draxinger, Oct 9, 2008
    #18
  19. 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.
    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.
     
    Nate Eldredge, Oct 10, 2008
    #19
  20. simonl

    James Kuyper Guest

    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.
    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.
    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.
     
    James Kuyper, Oct 10, 2008
    #20
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.