Subscribe to jjl's Blog        RSS Feed
-----

Returning pointers to temporary stack memory

Icon Leave Comment
Returning an address to temporary stack memory seems to a bug that I see twice a week here at DIC. I am going to blame this bug on the fact that most beginners don’t know how a run time stack works.

The run time stack should be thought of a growing and shrinking block of memory (it’s really just a pointer moving around), used to store active function memory : parameters, local variables, saved register states, address to return to, and the return value. C/C++ and all other languages that I can think of use this idea of a run time stack; however there were older languages that used fixed stack memory (making recursion impossible).
When you call a function in C/C++ from main, a "Stack Frame" is allocated above main. As depicted below.

Posted Image

When the function “add” is called, the stack pointer is moved up a stack frame to the “add” memory address. All variables and parameters within the “add” function are referenced from this stack pointer. After “add” returns, the stack pointer is moved to the return address stored in “add”s stack frame, in this case it happens to back to main and execution begins on the assignment line of “ans = add(a, c”. The variable “ans” is assigned to the return value of “add”, which is stored just above main’s stack frame. The image below shows the movement of the stack pointer after the function “add” returns.

Posted Image

None of the above has touched on return temporary memory addresses, for the return variable of “add” is copied.

Let’s now get into the good stuff.

Say we modify the function “add” to return a pointer to the variable “total”, rather than a copy of its value. The image below depicts the scenario below:

Posted Image

The code above, believe or not, actually works, that’s why this bug is so dangerous. The content data of the return variable total still exists after the function ends. Deleting memory on a stack is expensive, rather than removing the stack frame “add”, the pointer is just moved back down to the caller function. (main in this case). The problem is that the memory of “adds” stack frame is now free to be used by subsequent stack frames for future calls. Once another function is called, the memory that “ans” points to is wiped out by the new stack frame that has just been added above main. The code below shows what happens when a pointer is pointing to temporary memory on a previous stack frame and another function call is made.

Posted Image

Before the function call to “sub”, “ans” pointed to memory that looked and seemed valid and actually contained the expected value; however, once another stack frame is added, the memory that “ans” used to pointed to is wiped out and filled will data that is not predictable.

The point is to realize that returning pointers to temporary stack memory is not safe. Just because it seems to work for the simple example that you experiment with doesn’t mean that it will be safe for subsequent function calls. The code below is somewhat of a summary of all that has been mentioned above. Note that add and sub have very similar function signatures. Try and guess what the output is and then compile the code below and see if the output is what you expected.

#include <stdio.h>

int sub(int a, int c) {
   int total = a - c;
   return total;
}

int *add(int a, int c) {
   int total = a + c;
   return &total;
}

int main(int argc, char **argv) {
   int a, c, *ans, ans2;
   a = 10;
   c = 20;
   ans = add(a, c);
   ans2 = sub(a, c);

   printf("*ans = %d\nans2 = %d\n", *ans, ans2);
   
   return 0;
} 

0 Comments On This Entry

 

Trackbacks for this entry [ Trackback URL ]

There are no Trackbacks for this entry

August 2014

S M T W T F S
     12
3456789
10111213141516
17181920212223
24252627 28 2930
31      

Recent Entries

Search My Blog

0 user(s) viewing

0 Guests
0 member(s)
0 anonymous member(s)

Categories

Never add '.' to your path :(

So today was bad ... I was working in an Ubuntu environment and I was sick of typing the prefix "./" in front of my program name to run it in the terminal. I decided that I would add "." to my end of my path, that way all system calls still had precedence and I didn't have to type the annoying "./" before my program name to run it.

Everything was perfect, I was living it up and saving valuable key strokes until unix took revenge. I decided to run the "
rm" program like so

rm -r *.



I needed to recursively remove all files from a directory, but since the "." was included in my path. "rm" also looked at the ".." folder, which then started removing files from all other places of my home directory :/ Luckily I had backed up my home folder not too long ago, but let this be a valuable lesson to all - don't include "." in your path.