An Important Note Regarding Non-Local Goto

Before we can demonstrate the way of performing a non-local goto, we have to keep in mind an important fact. That is, the jump buffer to which a longjmp() transfers its control must be valid. We shall use examples to illustrate what valid means and provide a conclusion at the end of this page.

Before executing longjmp()
After executing longjmp()

Look at the above program. Function A() sets a return point in Buf, then calls function B(), which, in turn, calls C(). When the execution flow is in C() and before the execution of the longjmp(), the configuration of the stack is shown in the top figure. At this moment, because A() is still active, the return point Buf is still intact. As a result, the longjmp() is meaningful. After the execution of longjmp() that brings the control back to A(), since we are now in A(), the stack top pointer points to the next location of A()'s activation record. Consequently, all local data of B() and C() are no more valid, because they are beyond the stack top pointer even though they did not return! This means that the local data of B() and C() are lost.

Based on this understanding, let us look at the following program.

Before executing return
After executing return

The above program calls B(), which, in turn, calls C(). In C(), a jump buffer Buf is setup. The top diagram shows the configuration of the stack before the execution of return. After the execution of return, eventually the control goes back to A(). Once in A(), the activation records of B() and C() are gone, and, as a result, the information stored in jump buffer Buf is no more valid. More precisely, the environment described by Buf does not exists. Consequently, the longjmp() in A() will not be successful. This is equivalent to have a pointer to a freed variable.

Let us take a look at one more example, which is a combination of the above two. The following program appears capable of jumping between two functions. More precisely, A() sets a return point Buf1 and calls C() (well, indirectly, of course) for C() to build a return point Buf2. Now, C() uses a longjmp() to return to A()'s Buf1. Then, A() uses a longjmp() to return to C()'s Buf2. If we replace the printf statement by a longjmp() call, we can jump back to A() again. Thus, it looks like that we can jump back and forth between A() and C() without going through the intermediate function B(). Does this work?

Before executing longjmp()
After executing longjmp()

No, this does not work. As in the first example, longjmp(Buf1,1) works properly because its "environment" (i.e., A()'s activation record) is still there. Once this longjmp() brings the control back to A(), the activation records of B() and C() disappear, and, consequently, the target of longjmp(Buf2,1) disappears. Therefore, longjmp(Buf2,1) cannot be executed properly.

After three examples, you perhaps have an idea for what valid means. When a longjmp() call is about to execute, the environment (i.e., activation record) that contains the target must be in existence. Therefore, it is likely you can longjmp() from deep function call to an early function; but, it is impossible to return to a deep function call from an early function call, even though a jump buffer has been set. Thus, whether the jump buffer exists and is valid is not the major issue. The major issue is if the content stored in the jump buffer is valid.