Why does setjmp/longjmp - c

I want to use setjmp/longjmp to reuse some code inside the main function (NOTE: this is only an exercise and not something I ever seriously plan on doing in the real world).
The following code is what I've came up with:
#include <stdio.h>
#include <setjmp.h>
jmp_buf jmp_body, jmp_ret;
void func(void)
{
if (setjmp(jmp_ret) == 0)
longjmp(jmp_body, 1);
}
int main()
{
int x = 0;
if (setjmp(jmp_body) == 1) {
printf("Body %d\n", ++x);
longjmp(jmp_ret, 1);
}
func();
func();
func();
return 0;
}
The way I expected this code to work is the following:
The main() function is going to remember where the 'body' part is and skip it using if (setjmp(jmp_body) == 1).
The func() call is going to temporarily jump to the body using longjmp(jmp_body) after remembering where the body is supposed to return using if (setjmp(jmp_ret) == 0)
The body is going to execute and jump back to the func() call using longjmp(jmp_ret, 1)
The func() is just going to return to main() as expected.
Therefore, what I expected the code to print is the following:
Body 1
Body 2
Body 3
Instead, it loops forever continually executing the body which indicates to me the func() call isn't returning where it's supposed to and instead might be returning above itself executing itself over and over again.
In comparison, the following code prints just what I expected:
#include <stdio.h>
#include <setjmp.h>
jmp_buf jmp_body, jmp_ret;
int main()
{
int x = 0;
if (setjmp(jmp_body) == 1) {
printf("Body %d\n", ++x);
longjmp(jmp_ret, 1);
}
if (setjmp(jmp_ret) == 0)
longjmp(jmp_body, 1);
if (setjmp(jmp_ret) == 0)
longjmp(jmp_body, 1);
if (setjmp(jmp_ret) == 0)
longjmp(jmp_body, 1);
return 0;
}
What is it about putting if (setjmp(jmp_ret) == 0) longjmp(jmp_body, 1) inside a function call that makes the original approach invalid?

TL/DR - you can't jump back into a function you jumped out of.
7.13.2.1 The longjmp function
...
2 The longjmp function restores the environment saved by the most recent invocation of
the setjmp macro in the same invocation of the program with the corresponding
jmp_buf argument. If there has been no such invocation, or if the invocation was from
another thread of execution, or if the function containing the invocation of the setjmp
macro has terminated execution248) in the interim, or if the invocation of the setjmp
macro was within the scope of an identifier with variably modified type and execution has
left that scope in the interim, the behavior is undefined.
248) For example, by executing a return statement or because another longjmp call has caused a
transfer to a setjmp invocation in a function earlier in the set of nested calls.
C 2011 Online Draft
When you execute longjmp(jump_body, 1); in func, you invalidate jump_ret.
longjmp isn't bidirectional - it unwinds the stack as though any of the function calls between the setjmp and longjmp never happened.

You tried to longjmp back down the stack from main() to func(). This is not defined. Since longjmp is itself a function, you most likely ended up longjmping into func with the return address being the longjmp call itself thus an infinite loop.

Related

How to use longjmp from main function to other functions?

#include <setjmp.h>
#include <stdio.h>
jmp_buf jmp;
int test() {
setjmp(jmp);
puts("Birds are crying");
return 0;
}
int main() {
longjmp(jmp,1);
return 0;
}
Above code doesn't work and crashes, why?
I am using the GCC compiler on Windows 10 Pro.
Your usage of longjmp() is invalid, take a look at the documentation:
longjmp() restores the environment saved by the last call of setjmp()
You need to call setjmp() first in order to "set" where to jump before calling longjump(). That's why your code doesn't work. It's undefined behavior at best.
In other words, you cannot just use longjmp() as a simple "jump to global label". It has a different purpose.
after putting test(); call before the line longjmp it works and print it twice but why does it still crashes?
Let's again take a look at the documentation:
setjmp() saves the stack context/environment in env for later use by longjmp(). The stack context will be invalidated if the function which called setjmp() returns.
You cannot longjmp() to a buffer that was previously set by a function that has now returned. It's invalid. Your usage of setjmp()/longjmp() is really not what those functions are meant for.
Jumping out to another function isn't really the purpose of setjmp()/longjmp(), their purpose is to save the context and "jump back" to a function that has still not returned (see below example). It's not simple to come up with a meaningful example usage of such functions, since they are meant for advanced usage.
Here's a correct usage example (while still not that meaningful) from Wikipedia:
#include <stdio.h>
#include <setjmp.h>
static jmp_buf buf;
void second() {
printf("second\n"); // prints
longjmp(buf,1); // jumps back to where setjmp was called - making setjmp now return 1
}
void first() {
second();
printf("first\n"); // does not print
}
int main() {
if (!setjmp(buf))
first(); // when executed, setjmp returned 0
else // when longjmp jumps back, setjmp returns 1
printf("main\n"); // prints
return 0;
}

problem with setjmp and longjmp to switch between 2 functions

I am trying to implement a code which continuously switched between functions fun() and main() which do nothing but to print on screen infinitely. I am trying to switch by setjmp and longjmp and using SIGALRM signal in C.
But when I run it, it just works once and then doesn't switch.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <setjmp.h>
jmp_buf b1, b2;
int cur = 0;
void handlesig(int sig) {
if(!cur) {
cur = 1;
setjmp(b2);
longjmp(b1, 1);
}
else {
cur = 0;
setjmp(b1);
longjmp(b2, 1);
}
}
void fun() {
while(1) {
printf("I am in function fun()\n");
for(int x = 0; x < 100000000; x++);
}
}
int main() {
signal(SIGALRM, handlesig);
ualarm(900000, 900000); //send SIGALRM after each 900000 microseconds
if(!setjmp(b1))
fun(); //will be run when setjmp returns 0
while(1) {
printf("I am in function main()\n"); //will be run when setjmp returns 1
for(int x = 0; x < 100000000; x++);
}
return 0;
}
I am not getting what is the problem with this code.
Your program has undefined behavior because the lifetime of the block where setjmp was called on b1 or b2 in the signal handler ends as soon as longjmp is called (in the very next line). The next time you call longjmp trying to return to a jmp_buf that is no longer valid, the behavior is undefined, and this manifests as the state being utterly corrupted.
You can write a hack to work around this by using sigaltstack and SA_ONSTACK flag for the signal handler to have multiple stacks, so that even though the jmp_buf is formally invalid, it's in practice not clobbered. But this is not a valid program, just one which happens to work in practice on some systems (not all). Ultimately, there is no (valid/reliable) way to do what you're asking for with setjmp and longjmp; context switching requires a strictly stronger primitive than what they provide.

Why a segmentation fault occurs calling a function inside setjmp()?

I do not understand why in the function middleFunc(), a segmentation fault is raisen when entry_point(arg) is invoked inside the if ( setjmp(middle) ) statement.
#include <stdio.h>
#include <setjmp.h>
jmp_buf start,middle,end;
void finalFunc(void *v)
{
printf("hello\n");
return ;
}
void middleFunc(void (*entry_point)(void *), void *arg)
{
//just debug : this does not cause segmentation fault
entry_point(arg);
if ( setjmp(middle) ){
//this casues the segmentation fault
entry_point(arg);
//once the entry point (finalFunc) is executed go to jmp_buffer end
longjmp(end,1);
}
else {
longjmp(start,1);
}
}
int main(){
if (setjmp(end)){
//exit since finalFunc has been executed
return 0;
}
if (setjmp(start)){
//the middleFunc has previously set the jmp_buffer middle
longjmp(middle,1);
}
else{
int x = 1;
middleFunc(finalFunc,(void*)&x);
}
}
In your code the behavior is undefined. You are not allowed to long-jump to middle after middleFunc finished execution (either by normal completion or by another longjmp).
7.13.2.1 The longjmp function
2 The longjmp function restores the environment saved by the most recent invocation of the setjmp macro in the same invocation of the program with the corresponding jmp_buf argument. If there has been no such invocation, [...] or if the function containing the invocation of the setjmp macro has terminated execution248) in the interim [...] the behavior is undefined.
248) For example, by executing a return statement or because another longjmp call has caused a transfer to a setjmp invocation in a function earlier in the set of nested calls.
In your code middleFunc sets up middle and after that immediately exits to main by doing longjmp(start,1). After that jump middle is no longer valid. You are no longer allowed to jump to middle from anywhere. setjmp/longjmp mechanism only supports jumps up the call stack. You cannot do side-jumps or down-jumps. Only up-jumps are supported.
From the practical point of view, you are attempting to jump into a "dead" function invocation and somehow expecting that function parameter values are still valid (like, preserved from the previous invocation or something). But they are not. setjmp/longjmp do not preserve/restore parameter values. Value of entry_point in that "dead" invocation is probably some garbage. When you attempt to make a call through entry_point, the code coredumps.
P.S. It is true that side-jumping with setjmp/longjmp is sometimes used to implement co-routines. However, such usage falls outside the boundaries of standard library specification. And in any case such usage will never expect preservation of parameter values.

reinversion of control using longjmp

I'm trying to implement reinversion of control in c using longjmp, currently i have this code:
#include <stdio.h>
#include <setjmp.h>
jmp_buf env;
int arg;
typedef void (*fptr)(int);
fptr callback;
void cb(int a)
{
arg = a;
longjmp(env, 1);
}
#define cont1(f, x) do { if(!setjmp(env)) { f(x, &cb); return; } } while(0)
void callback_func(int num, fptr cb)
{
printf("in a func, num = %d\n", num);
callback = cb;
}
void task1()
{
printf("before continuation\n");
cont1(callback_func, 7);
printf("after continuation\n");
}
void task2()
{
printf("in thread 2\n");
(*callback)(5);
}
int main()
{
task1();
task2();
printf("arg = %d\n", arg);
return 0;
}
And my question is: doesn't this invoke undefined behavior or could cause any problem in real world use and if yes, then is there any better way to do this?
This program has undefined behavior. See C11 7.13.2.1 The longjmp function (emphasis mine):
If there has been no such invocation, or if the invocation was from another thread of execution, or if the function containing the invocation of the setjmp macro has terminated execution248 in the interim, or if the invocation of the setjmp macro was within the scope of an identifier with variably modified type and execution has left that scope in the interim, the behavior is undefined.
248) For example, by executing a return statement or because another longjmp call has caused a transfer to a setjmp invocation in a function earlier in the set of nested calls.
At the time you cb calls longjmp, the function which invoked setjmp to fill env, task1, has returned. Thus calling longjmp is undefined.
The C language has no means to do what you're trying to do except for (now with C11) threads, using condition variables to control which thread runs.

What does setjmp call do in this example

I am trying to understand setjmp in the following code:
http://androidxref.com/4.2.2_r1/xref/frameworks/base/core/jni/android/graphics/YuvToJpegEncoder.cpp#24
What does this line setjpg() mean?
32 if (setjmp(sk_err.fJmpBuf)) {
33 return false;
34 }
setjmp establishes a return-point that you can return to from deep in the call stack using longjmp. It's usually considered bad style (akin to goto).
The first time it is called, it returns 0. Then if a longjmp happens back to the jump-point, it will be as if setjmp returned with 1. longjmp can also send a different return code, but if you try to send 0, the result will be 1.
#include <setjmp.h>
jmp_buf j;
main(){
if(setjmp(j)){
printf("boo!\n");
return 0;
}
myfunc();
printf("5");
}
myfunc(){
printf("1");
myotherfunc();
}
myotherfunc(){
printf("2");
myfriendsfunc();
}
myfriendsfunc(){
printf("3");
longjmp(j, 0);
printf("4");
}
output:
$ ./jmp
123boo!
setjmp saves the context for a future call to longjmp. It returns zero when it is directly called (so, here, execution continues with jpeg_create_compress). When longjmp is called later (it must be before the current function returns, but it can be at any arbitrary call depth), execution will directly return to the setjmp call, and resume as if setjmp returned whatever you gave to longjmp. Presumably, in this case, longjmp will be called with a non-zero value, hence the method will directly return false.
Looking at the context, it looks like it is used for error handling. I assume longjmp can be called during the following compress. Think of it like low-level exceptions.

Resources