5

I have been professionally coding in C for a while but am still stumped by some pointer related questions. I would really appreciate SO community's help in understanding below problem.

Following code crashed and generated core file.

void func1()    // Frame 1 in GDB stack trace.  
{ 
    UTYPE  *ptr;  // pointer to user defined type  
    ...

    // data is of type UTYPE and has valid contents.
    // lets say its address is 0x100 
    ptr = &data;     --- (1)  
    ...

    func2(ptr);      --- (2) 
    ...
} 

void func2(UTYPE *inp)    // Frame 0 in GDB stack trace.  
{
    if(!inp)         --- (3) 
        return; 
    ...

    // another_ptr is of UTYPE * which is a NULL.  
    inp = another_ptr;   ---- (4)  

    /* Did not check for NULL and dereference inp and CRASH */    ---- (5) 
} 

Simplified backtrace from GDB:

Frame 0: 
    func2(inp = 0x0) 
    // crash at line (5) due to dereference 

Frame 1: 
    func1: func2(0x0)  
    // `ptr` at line (2) is 0x0. Why is this so? 

Why is ptr shown as 0x0 (NULL) in Frame 1?

When func2() is called, its call stack looks as follows:

  | //local vars  | 
  |               | 
  | another_ptr = |
  |      NULL     |
  +---------------+
  | return addr   |
  +---------------+
  | input args    |
  | copy of ptr   |
  |   contents    |
  |     0x100     |

For func1(), its call stack should look like:

  |               | 
  | ptr = 0x100   |
  |               |
  +---------------+
  | return addr   |
  +---------------+
  | input args    |
  |  none in this |
  |  func         |

When inp becomes NULL in func2() in line (4), how is it reflected in func1()?

4
  • I like your representation. Commented Jan 30, 2014 at 19:52
  • 1
    That's odd. I'd expect the backtrace for Frame 1 to say func1() at thelinenumberoflocationlabeled(2) and not display the call to func2. Are you using the bt command or something else? Commented Jan 30, 2014 at 20:03
  • @MarkPlotnick I am using bt command. It prints all stack frames and not just Frame 1. My representation of GDB backtrace above is just to help explain my question. Please let me know if something is amiss. Commented Jan 30, 2014 at 20:06
  • OK. My gdb's bt command displays a Frame 0 with a call to func2() and a Frame 1 with a call to func1. Its display of Frame 1 does not show a call to func2 as yours does. I'm just having trouble reproducing your exact problem. Commented Jan 30, 2014 at 20:12

2 Answers 2

2

When a function is called in C, the parameters are copied into registers or pushed onto the stack. The called function can reuse those registers and stack locations for any purpose. Often, but not always, a parameter is kept in the same register or stack location for the entire lifetime of the function call.

On a 32-bit system, the first argument to a function - in your case, inp - is often located on the stack, 12 bytes away from the location that the stack frame's base pointer points to. See stackoverflow.com: what exactly is program stack's growth direction.

When gdb does a backtrace, the only guidance it has from the compiler is something like "the first argument to func2 is named inp and is a 4-byte value of type *UTYPE located at a 12-byte offset from the %ebp register".

If, somewhere in func2, you alter inp, as you do at location (4), then any backtrace from that point on may very well show the altered value of inp, in your case, 0. The value that inp had when func2 was entered is lost forever, unless the compiler has been clever enough to include guidance like "the first argument to func2 is named inp and is a 4-byte value of type *UTYPE and its value upon entry to func2 can be found by unwinding the stack to the previous frame and looking at the value of ptr, which is located at a -4-byte offset from the %ebp register." The newer versions of the DWARF debugging format can specify things like this, I believe.

I cannot explain why your gdb's backtrace shows ptr in func1's frame as having the value 0. Setting inp to NULL should have no effect on ptr's value nor on gdb's ability to show ptr's value.

Sign up to request clarification or add additional context in comments.

1 Comment

This explanation brings a bit more clarity. Thanks.
2

GDB constructs the call stack from the stack, and since inp, which is a parameter for func2, is 0, GDB assumes that the passed parameter is 0, and so it says that func2 was called with 0.

Stack at crash time is something like that:

another_ptr = 0  ( func2 local variable )
return address to func1                   
inp = 0          ( func2 parameter )
ptr              ( func1 local variable )

5 Comments

Oh! In that case, the info in older frames are inaccurate, especially pointer variables.
In above stack representation, when inp becomes 0, why is that reflected on ptr in GDB? They are in separate function stacks, right?
func2(0x0) doesn't mean that ptr is null, it refers to func2 parameter (inp) as well. Both function frames are on the same stack (as any other function frame in the same thread... )
Ok. Thanks for the explanation.
bt full also says ptr is 0x0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.