Chapter Six Question Two

Linux Programming Interface by Michael Kerrisk

Write a program to see what happens if we try to longjmp() into a function that has already returned.

Implementation

Note: I am not a professional C programmer, this code is not peer reviewed and I only provide this as a learning tool for myself


/**
 * Chapter 6 Question 2 Linux Programming Interface
 *
 * Write a program to see what happens if we try to longjmp() into a function that has already returned.
 *
 * @author Michael Trottier
 * @version 07/02/2018
 */
#include <stdio.h>
#include <setjmp.h>

static jmp_buf env;

int chapterSixQuestionTwo() {
    fprintf(stdout, "We are in the chapterSixQuestionTwo function.\n");
    // Do some garbage stuff
    int x = 0;

    /*
     * Calling setjmp() establishes a target for a later jump performed by longjmp(). This target is exactly the point
     * in the program where the setjmp() call occurred. From a programming point of view, after the longjmp(), it
     * looks exactly as though we have just returned from the setjmp() call for a second time. The way in which we
     * distinguish the second “return” from the initial return is by the integer value returned by setjmp(). The
     * initial setjmp() returns 0, while the later “faked” return supplies whatever value is specified in the val
     * argument of the longjmp() call. By using different values for the val argument, we can distinguish jumps to
     * the same target from different points in the program.
     */
    int setJumpStatus = setjmp(env);

    if (0 == setJumpStatus) {
        fprintf(stdout, "The setjmp system call was successful!\n");
    }

    if (1 == setJumpStatus) {
        fprintf(stdout, "This is the first time we jumped to the function!\n");
    }

    if (2 == setJumpStatus) {
        fprintf(stdout, "This is the second time we jumped to the function!\n");
    }

    x = setJumpStatus;
    return x;
}

/*
 * One restriction of C’s goto is that it is not possible to jump out of the current function into another function.
 * The restriction that a goto can’t be used to jump between functions in C exists because all C functions reside at
 * the same scope level (i.e., there is no nesting of function declarations in standard C, although gcc does permit
 * this as an extension). Thus, given two functions, X and Y, the compiler has no way of knowing whether a stack frame
 * for function X might be on the stack at the time Y is invoked, and thus whether a goto from function Y to function
 * X would be possible. In languages such as Pascal, where function declarations can be nested, and a goto from a
 * nested function to a function that encloses it is permitted, the static scope of a function allows the compiler
 * to determine some information about the dynamic scope of the function. Thus, if function Y is lexically nested
 * within function X, then the compiler knows that a stack frame for X must already be on the stack at the time Y
 * is invoked, and can generate code for a goto from function Y to somewhere within function X.
 */
int main() {
    fprintf(stdout, "We are in main!\n");

    chapterSixQuestionTwo();

    /*
     * Abusing longjmp()
     *
     * If the env buffer is declared global to all functions (which is typical), then it is possible to execute the
     * following sequence of steps:
     *   1. Call a function x() that uses setjmp() to establish a jump target in the global variable env.
     *   2. Return from function x().
     *   3. Call a function y() that does a longjmp() using env.
     *
     * This is a serious error. We can’t do a longjmp() into a function that has already returned. Considering what
     * longjmp() tries to do to the stack—it attempts to unwind the stack back to a frame that no longer exists—leads
     * us to realize that chaos will result. If we are lucky, our program will simply crash. However, depending on the
     * state of the stack, other possibilities include infinite call-return loops and the program behaving as though it
     * really did return from a function that was not currently executing. (In a multithreaded program, a similar abuse
     * is to call longjmp() in a different thread from that in which setjmp() was called.)
     */
    longjmp(env, 1);
}