Search code examples
cgccmemorystacklow-level

Why a string disappear from the stack when exiting from a function


I'm experimenting with the stack in C to test some things and learn more in the language. I know that a function has a stack frame created by moving the rsp/rbp pointer.

What I don't understand is that when I'm creating a char[] in a function, exiting from it, and accessing the pointer in another stack frame, it seems like the data has disappeared, but the compiled code doesn't seems to have anything that clears it.

Example C code:

#include <string.h>
#include <stdio.h>

char *save_ptr = 0;

void print_ptr(char *ptr)
{
        char increase_stack[200];

        printf("%p\n", increase_stack);        // Prints 0x1fff0000a0
        printf("%s\n", (char *) 0x1fff0000a0); // Prints nothing
        printf("%s\n", increase_stack);        // Prints nothing
        printf("%s\n", save_ptr);              // Prints nothing
}

char *create_str(void)
{
        char mstring[200];

        save_ptr = mstring;
        memmove(mstring, "1234567890\0", 11);
        printf("%p\n", mstring);   // Prints 0x1fff0000a0
        printf("%s\n", mstring);   // Prints 1234567890
        return mstring;
}

int main(void)
{
        char *test = create_str();

        // test is 0  beacause of GCC

        printf("%p\n", test);       // Prints 0
        test = save_ptr;
        printf("%p\n", test);       // Prints 0x1fff0000a0
        print_ptr(test);
}

So, the thing I don't understand is that, even though the address is still the same (0x1fff0000a0) and I didn't write anything there, it seems like the data has disappeared between the call.

The compiled assembly also doesn't seems to write to this address (see compiled code at: https://godbolt.org/z/jdrov6Exz). For compilation I used the following flags: -g -Og -fno-isolate-erroneous-paths-attribute -fno-isolate-erroneous-paths-attribute

Can someone explain why I don't see "1234567890" written by print_ptr()?


Solution

  • I know that a function has a stack frame created by moving the rsp/rbp pointer.

    You don't know that from anything in the specifications for the C language, and C does not depend on it. If you're studying C then you should focus on the concepts of C, not on language implementation details.

    What I don't understand is that when I'm creating a char[] in a function, exiting from it, and accessing the pointer in another stack frame, it seems like the data has disapeared, but the compiled code doesn't seems to have anything that clears it.

    The most relevant C language concepts here are storage duration and lifetime, which are closely related. Any object declared inside a block, without the keyword static, has automatic storage duration. Its lifetime ends when execution of the innermost containing block terminates. In your example, that happens when the create_str function returns. After that point, the array no longer exists as far as C is concerned, any pointer (in)to it becomes "indeterminate", and C expressly denies defining what happens when you try to access it.

    So,

    Why a string disappear from the stack when exiting from a function[?]

    It disappears, in the sense that its lifetime expires, because C says it does. What actually happens when you later attempt to access it is a matter of your particular C implementation, as influenced by details of your program. It might crash. It might present arbitrary data, possibly even the data the array contained just before the end of its lifetime. It might, in principle, do anything within the computer's capabilities.

    Again, I would encourage a C language student not to focus on exactly how this undefined behavior manifests for a particular program, or why it manifests that way. That you don't need to do that sort of thing is one of the main points of programming in a high-level language. What you should take away is that it is unsafe to attempt to access objects after the end of their lifetimes, unless you have some relevant behavioral guarantee coming from outside the C language.

    But if you insist on thinking about the implementation details, then ...

    the thing I don't understand is that, even if the address is style 0x1fff0000a0, it seems like the data has disapeared between the call, enven tho the address is the same and I didn't wrote anything to it.

    ... on a machine such as you assume, there probably isn't any explicit clearing of the memory that was occupied by the array during its lifetime. But that memory is in the stack's space, where it may be reused by other functions. In particular, one or both of the printf calls in your program may reuse it, directly or indirectly, leaving who-knows-what behind in that space. And even your print_ptr function may do, though I suspect that in practice, that particular function wouldn't.