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.
Related
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.
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.
In a sequence of function calls, such as
main() --> A() --> B()-->C();
when a called function finishes, it normally returns to the calling function, e.g. C() returns to B(), which returns to A(), etc.
i'm wondering if it is also possible to return directly to an earlier function in the calling sequence
so C() return to main() and skip B() and A()
if it is possible how can i do it ? and can you explain how it work and when it used in reality.
this is my code
#include <stdio.h>
int A(void);
int B(void);
void main(void )
{
A();
}
int A()
{
printf("enter A()\n");
B();
printf("exit A()\n");
}
int B()
{
printf("enter B()\n");
printf("exit B()\n");
}
and i want to skip return to function A() from B() so that the printf("exit A()\n"); will not execute and give me this result:
enter A()
enter B()
exit B()
before everything check out #PeterCordes is answers the question in more than a way
okey let's start :
this type of stuff can be done using something known as long jump so your code after editing will look like this :
#include <stdio.h>
#include <setjmp.h>//c standard library header
jmp_buf env; // for saving longjmp environment
main()
{
int r, a=100;
printf("call setjmp to save environment\n");
if ((r=setjmp(env)) == 0){
A();
printf("normal return\n");
}
else
printf("back to main() via long jump, r=%d a=%d\n", r, a);
}
int A()
{
printf("enter A()\n");
B();
printf("exit A()\n");
}
int B()
{
printf("enter B()\n");
printf("long jump? (y|n) ");
if (getchar()=='y')
longjmp(env, 1234);
printf("exit B()\n");
}
let's understand what just happened
In the above program, setjmp() saves the current execution environment in a jmp_buf
structure and returns 0.
The program proceeds to call A(), which calls B().
While
in the function B(), if the user chooses not to return by long jump, the functions will
show the normal return sequence.
If the user chooses to return by longjmp(env,1234),
execution will return to the last saved environment with a nonzero value.
In
this case, it causes B() to return to main() directly, bypassing A().
The principle of
long jump is very simple. When a function finishes, it returns by the
(caller_EIP, caller_EBP)
in the current stack frame,
If we replace (caller_EIP, caller_EBP) with
(saved_EIP, saved_EBP) of an earlier function
in the calling sequence, execution would return to that function directly.
In addition
to the (saved_EIP, saved_EBP), setjmp() may also save CPU’s general registers and the
original ESP, so that longjmp() can restore the complete environment of the returned
function.
Long jump can be used to abort a function in a calling sequence, causing
execution to resume from a known environment saved earlier.
Although rarely used
in user mode programs, it is a common technique in systems programming.
For example,
it may be used in a signal catcher to bypass a user mode function that caused
an exception or trap error.
you can check this is also good
Signal takes a callback function as one of its arguments. To have variable behaviour I want to create a function within a function. This is my attempt so far:
typedef void (*sighandler_t)(int);
sighandler_t f(int pid) {
void sigintHandler(int sig) {
printf("Process %d", pid);
}
return sigintHandler
}
int main(void) {
...
if (signal(SIGTSTP, *f(1)) == SIG_ERR) {
...
}
...
}
But, every time I send a SIGTSTP (Ctrl-z), I get a seg fault.
As a sidenote: any tips on how to debug seg faults in general would be really appreciated!
Your code compiles because it's syntactically correct and you're using compiler extensions; however, there are some fundamental issues with your code that might be leading to your segfault.
First, your signal handler code:
typedef void (*sighandler_t)(int);
sighandler_t f(int pid) {
void sigintHandler(int sig) {
printf("Process %d", pid);
}
return sigintHandler;
}
This is not standard C and even requires the -ftrampolines flag be specified on some versions of gcc to actually compile.
Your signal handler function itself has a few issues that need to be resolved:
sigintHandler is a nested function, thus when your signal handler function f returns by return sigintHandler;, you're returning a function pointer.
In your code, this compiles correctly because you have typedef void (*sighandler_t)(int);, which defines a function pointer type that can point to functions that have a void return type and take an int as a parameter, which your sigintHandler is defined as.
Instead, your signal handler function could be written simply as:
void sigintHandler(int sig) {
printf("Signal %d\n", sig);
}
In your main function, you have the following:
if (signal(SIGTSTP, *f(1)) == SIG_ERR) {
// ....
}
Here it should be noted this as well has some issues. First, the signal function takes as its first parameter the signal number (usually a macro defined in the signal.h header) and as it's second argument a pointer to a function defined as void func_name(int sig).
To this, you are calling the function instead of passing it as a pointer.
*f(1) actually makes a call to f passing 1 as its parameter; instead, you would change it to the following:
if (signal(SIGTSTP, f) == SIG_ERR) {
// ....
}
But this should emit a warning/error since f is defined as returning a function pointer instead of void.
So to change the code to be compliant, you could just do the following:
#include <stdio.h>
#include <signal.h>
void sigintHandler(int sig) {
printf("Signal %d", sig);
}
int main(void) {
// ...
if (signal(SIGTSTP, sigintHandler) == SIG_ERR) {
// ...
}
// ...
return 0;
}
You stated however:
To have variable behaviour ...
This depends on what kind of variable nature you're intending, but if it's variable functions based on the signal, you can do something like the following:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void sig_stop(int sig) {
printf("Process %d stop\n", getpid());
}
void sig_int(int sig) {
printf("Process %d interrupt\n", getpid());
}
int main(void) {
// ...
if (signal(SIGTSTP, sig_stop) == SIG_ERR) {
// ...
}
if (signal(SIGINT, sig_int) == SIG_ERR) {
// ...
}
// ...
return 0;
}
Or you could use a switch statement:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void sigHandler(int sig) {
printf("Process %d received %d\n", getpid(), sig);
switch (sig) {
case SIGTSTP:
// do stop code
break;
case SIGINT:
// do interupt code
break;
}
}
int main(void) {
// ...
if (signal(SIGTSTP, sigHandler) == SIG_ERR) {
// ...
}
if (signal(SIGINT, sigHandler) == SIG_ERR) {
// ...
}
// ...
return 0;
}
any tips on how to debug seg faults in general would be really appreciated!
First, understand what a segmentation fault is; then you can use a debugger like gdb to step through your code or inspect crash dumps to see where specifically the segfault is happening.
Hope that can help.
aint sure about what you are asking, but i can help you understand your segmentation fault.
when you call a function there are several things that are done.
push function arguments to stack
push return address to stack
jump to function address
function body
pop return address from stack
pop function arguments from stack.
when 1,2, 6 are done by the Calling scope.
As you know, signal is a void arguments hence the call (1) would push 0 arguments to the stack.
while the return (6) would pop your "unexistant" int from the stack and corrupt it.
somewhat solution
you can't have signal function with parameters,
what you can is:
you can read your global variables within the signal function. and therefore read the current state of the program.
you can sys_call to get your process_id, thread_id.
i wouldn't recommend but you can read your stack further to the previous scope and get it's local variables. With BIG Note that it won't be the function you set the signal at... but the function that was running at the moment of the signal.
a piece of code here
jmp_buf mark;
int Sub_Func()
{
int be_modify, jmpret;
be_modify = 0;
jmpret = setjmp( mark );
if( jmpret == 0 )
{
// sth else here
}
else
{
// error handle
switch (jmpret)
{
case 1:
printf( "Error 1\n");
break;
case 2:
printf( "Error 2\n");
break;
case 3:
printf( "Error 3\n");
break;
default :
printf( "Unknown Error");
break;
}
printf("after switch\n");
}
return jmpret;
}
void main( void )
{
Sub_Func();
// the longjmp after setjmp
longjmp(mark, 1);
}
the result is:
Error 1
after switch
Segmentation fault
I know the reason maybe longjmp jump back to the previous stack. but I'm not sure about the detail, and what kind of value has been stored in 'mark', can anyone explain that?
setjmp() and longjmp() work by recording a stack frame position. If you record the stack frame in Sub_Func() but return from the function before calling longjmp(), the stack frame is no more valid.
longjmp() is meant to be called in the same function than setjmp() (subfunction is ok).
You are trying to longjmp back to a deeper function. You can only longjmp back to a shallower function.
So if A calls setjmp, then calls B, then B can longjmp back into A.
But if A calls b, B calls setjmp, B returns to A, A cannot longjmp back to B.
You have invoked undefined behavior by violating the following (7.13.2.1):
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 execution217) 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.
217) 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 short, longjmp cannot be used to jump to a setjmp point in a function which has already returned.