Why is it unsafe to return a value in the function stack

I came across the next paragraph while reading bruce eckel .., where he tried to explain why its unsafe for a function to return a value on the stack

Now imagine what happens if a regular function tries to return values ​​on the stack. you can touch any part of the stack above the return address, so the function will need to pop values ​​below the return address. But when the assembler language is returned, the stack pointer must point to the return address (or directly below it, depending on your computer), so before RETURN, the function must move the stack pointer up, thereby clearing all local variables . If you try to return values ​​on the stack below the return address, you become vulnerable at the moment because an interruption may occur. ISR will follow the stack pointer down to save its return address and its local variables and overwrite the return value

Do you want to help me understand bold italic text?

+4
source share
4 answers

Suppose your application has the following call stack:

  • Basic procedure
  • Local variables Function1
  • Local variables Function2 <- STACK POINTER

In this case, the main calls to function1, and function1 calls function2.

Now suppose that function2 calls function3, and the return value of function3 is returned to the stack:

  • Basic procedure
  • Local variables Function1
  • Local variables Function2
  • Function3 local variables including return value <- STACK POINTER

Function 3 stores the return value on the stack, and then returns. The returned tool again decreases the stack pointer, so the stack becomes the following:

  • Basic procedure
  • Local variables Function1
  • Local variables Function2 <- STACK POINTER

It is clear that the frame of the function3 frame no longer exists here.

Well, actually I lied a little. The stack frame still exists:

  • Basic procedure
  • Local variables Function1
  • Local variables Function2 <- STACK POINTER
  • Function3 local variables including return value

Thus, to get the return value, it seems safe to access the stack.

But, if there is an AFTER interrupt function returned, but before function2 receives the return value from the stack, we get the following:

  • Basic procedure
  • Local variables Function1
  • Local variables Function2
  • Local variables of the interrupt function <- STACK POINTER

And now the stack frame is really overwritten, and the return value that we desperately need is gone.

That is why returning the return value on the stack is unsafe.

The problem is similar to the one shown in this simple C code snippet:

char *buf = (char *)malloc(100*sizeof(char *)); strcpy (buf, "Hello World"); free (buf); printf ("Buffer is %s\n",buf); 

In most cases, the memory used for buf will still contain the contents of "Hello World", but it can go horribly wrong if someone can allocate memory after calling free, but before calling printf. One such example is in multi-threaded applications (and we have already encountered this problem internally), as shown here:

 THREAD 1: THREAD 2: --------- --------- char *buf = (char *)malloc(100); strcpy (buf, "Hello World"); free (buf); char *mybuf = (char *)malloc(100); strcpy (mybuf, "This is my string"); printf ("Buffer is %s\n",buf); 

printf is Thread 1, now you can print "Hello World", or it can print "This is my line." Everything can happen.

+2
source

It is simply trying to explain why you should not return a pointer or reference to a local variable. Because it disappears as soon as the function returns!

What happens at the hardware level is not so important, even if it can explain why the value sometimes seems there, and sometimes not.

+2
source

When you call a function that has pass-by-stack arguments, these arguments are pushed onto the stack. When a function returns, this bit of used stack memory is freed. Immediately after this, it is unsafe to access what was in these stack values, because something else might overwrite them.

Say we are on a processor where the stack pointer is stored in the SP register and it grows up.

  • Your code intercepts and comes to a function call. At this point we will say that the SP is 100.
  • A function is called, and your function takes two single-byte arguments. These two bytes of arguments get on the stack, and ... and this is the important part - the address of the code from which you called the function (say, 4 bytes). Now SP is 106. The return address is SP = 100, and your two bytes are 104 and 105.
  • Let's say a function modifies one of these arguments (SP = 105) as a way to return the changed value
  • The function returns, the stack returns back to where it was (SP = 100), and continues.

  • In an ideal world, the system has nothing else, and your program has absolute control over the processor ... Until you do something else that requires a stack, this SP = 105 value will remain there forever.

  • However, with interruptions, there is no guarantee that something else will not appear. let's say hardware interrupt hits your application. This means an immediate transition to the interrupt service routine, so the current address where the processor was when the interrupt hit hits the stack (4 bytes) now SP is 103. Let it be said that this ISR calls other routines, which means more return addresses go to stack. So now SP is 107 ... your original value of 105 has not been overwritten.

  • Ultimately, these ISRs will return, control will return to your code, and SP will return to 100 again ... your application is trying to get this SP = 105 value, blissfully not realizing that it received ISR corruption, and now you are working with bad data.

+1
source

The most important part of this paragraph:

If you are trying to push values ​​onto the stack below the return address (...)

In other words, do not return pointers to data that is valid only within this function.

This was probably something you had to worry about before C standardized how the function returned the structure by value. Now, this is part of the C99 standard (6.8.6.4), and you should not worry about that.

And return by value is now fully supported in C ++. Otherwise, many details of the STL implementation simply will not work as expected.

+1
source

Source: https://habr.com/ru/post/1342178/


All Articles