Home > front end >  Does setjmp literally save the state of the program?
Does setjmp literally save the state of the program?

Time:06-17

So, if I were to malloc 500 bytes, then use setjmp to save the state, then free the 500 bytes, then longjmp, would I be able to access those 500 bytes? For example:

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

jmp_buf buf;

struct str {
   char s[200];
   int a;
};

int main() {
    struct *str = (struct str *)malloc(sizeof(str));
    (*str).s = "the string";
    (*str).a = 31;
    setjmp(buf);
    printf("%s %d\n", (*str).s, (*str).a);
    free(str);
    longjmp(buf, 1);
    printf("%s %d\n", (*str).s, (*str).a);
    return 0;
}

What will be the output of the second printf? Also, what's the meaning of the second argument in longjmp? I saw some people use large integers as the second argument.

CodePudding user response:

Does setjmp literally save the state of the program?

No, setjmp() saves some CPU register values as well as some context to allow the program to resume execution at the same place under a very strict set of conditions.

Note that you do not allocate enough memory for a struct str: you pass sizeof(str), which is the size of the str pointer. You should write this instead:

    struct *str = malloc(sizeof(*str));

Futhermore, you cannot initialize str->s with an assignment, you should define the str member as a const char * instead of an array.

So, if I were to malloc 500 bytes, then use setjmp to save the state, then free the 500 bytes, then longjmp, would I be able to access those 500 bytes?

No, your program has undefined behavior because the memory block allocated by malloc and pointed to by str has been freed, thus str must not be dereferenced any longer.

Note that the last 2 statements in the main function will never execute because longjmp() does not return, it makes the program resume execution at the place where setjmp() returns and it makes setjmp return the value of the second argument passed to longjmp() (and the value 1 if 0 is passed for this argument).

Here is a modified version for illustration:

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

jmp_buf buf;

struct str {
   const char *s;
   int a;
};

int main() {
    struct str *str = malloc(sizeof(*str));
    str->s = "the string";
    str->a = 31;
    int res = setjmp(buf);
    if (res == 0) {
        printf("res: %d, str: %p, s: %s, a: %d\n",
               res, (void *)str, str->s, str->a);
        free(str);
        str = NULL;
        printf("str: %p\n", (void *)str);
        longjmp(buf, 0);
    } else {
        printf("res: %s, str: %p\n", res, (void *)str);
        if (res < 10)
            longjmp(buf, res   1);
    }
    return 0;
}

Output (compiled with optimisations):

res: 0, str: 0x7fe44de00000, s: the string, a: 31
str: 0x0
res: 1, str: 0x7fe44de00000
res: 2, str: 0x7fe44de00000
res: 3, str: 0x7fe44de00000
res: 4, str: 0x7fe44de00000
res: 5, str: 0x7fe44de00000
res: 6, str: 0x7fe44de00000
res: 7, str: 0x7fe44de00000
res: 8, str: 0x7fe44de00000
res: 9, str: 0x7fe44de00000
res: 10, str: 0x7fe44de00000

Output (compiled with -O0):

res: 0, str: 0x7ff4885000e0, s: the string, a: 31
str: 0x0
res: 1, str: 0x0
res: 2, str: 0x0
res: 3, str: 0x0
res: 4, str: 0x0
res: 5, str: 0x0
res: 6, str: 0x0
res: 7, str: 0x0
res: 8, str: 0x0
res: 9, str: 0x0
res: 10, str: 0x0

As you can see, what gets preserved by setjmp() is difficult to predict so this function must be used with extreme caution in circumstances where the programmer is well aware of the compiler behavior and only for very specific needs.

CodePudding user response:

It is vaguely specified what exactly gets stored by setjmp, the standard just says:

The environment of a call to the setjmp macro consists of information sufficient for a call to the longjmp function to return execution to the correct block and invocation of that block, were it called recursively. It does not include the state of the floating-point status flags, of open files, or of any other component of the abstract machine.

In practice this likely means: program counter, stack pointer, registers involved in calling convention, fundamental condition code registers. It does not include the heap as it sorts below "any other component of the abstract machine", but also because common sense tells us that storing the whole heap on the stack is senseless.

As for your code example, it is gibberish pseudo code and it uses setjmp in undefined behavior ways - any code using that function must always check the result.

Generally, these functions are some of the worst spaghetti programming horrors ever designed and should only be used for debugging program flow purposes, never for production code.

  • Related