Setjmp/Longjmp: Why Is This Throwing a Segfault

SetJmp/LongJmp: Why is this throwing a segfault?

You can only longjmp() back up the call stack. The call to longjmp(b_buf, 1) is where things start to go wrong, because the stack frame referenced by b_buf no longer exists after the longjmp(a_buf).

From the documentation for longjmp:

The longjmp() routines may not be called after the routine which called the setjmp() routines returns.

This includes "returning" through a longjmp() out of the function.

Different behavior of setjmp/longjmp in release & debug

The code in question causes undefined behaviour. The program is incorrect, there is no expected behaviour. You should not expect nor be surprised by any particular output or other behaviour.

setjmp longjmp with Stack

I can't see anything obviously wrong with your code, but it is not an MVCE, so it is tough to tell -- there may be something off in your scheduler or your push and pop functions.

One thing that seems questionable is the tests in ult_yield and ult_read:

if(runqueue->start == NULL && blockedqueue->start == NULL) ...

These should both be:

if (runqueue->start == NULL) {
printf("Scheduler queue corrupted");
abort(); }

since when these functions are called, runqueue->start MUST point at the current thread's tcb/queue node.

Your valgrind error looks like it is trying to longjmp through an invalid jmp_buf, so try to backtrack to see where it came from and how it got into that state.

You should also probably unset the signal handler at the end of ult_spawn (or use SA_RESETHAND), lest a spurious SIGUSR1 from somewhere cause corruption of things.

Is longjmp supposed to restore the stack?

That's the expected behavior:

Upon return to the scope of setjmp, all accessible objects,
floating-point status flags, and other components of the abstract
machine have the same values as they had when std::longjmp was
executed, except for the non-volatile local variables in setjmp's
scope, whose values are indeterminate if they have been changed since
the setjmp invocation
.

The value of a when executing longjmp is 15, so that is a value one could expect to see (it's indeterminate in general). The jmp_buf only stores the point of execution. Not the state of every variable in the program.

memory leak between ljmp and setjmp

There isn't really a way to do this with stock malloc. There is no portable facility that allows you to trace what allocations have been done since a certain point in time.

What you could do is something like this: Create a function that wraps malloc and adds each newly allocated memory region into a linked-list. When you need to clear memory leaks, traverse this list and free all the chunks you find there. Of course, this only works if you can guarantee that no pointers into the allocated regions remain.

It might be a good idea to rework your control flow so no memory allocations happen during the time where you want to abort control.

C++: Safe to use longjmp and setjmp?

setjmp()/longjmp() completely subvert stack unwinding and therefore exception handling as well as RAII (destructors in general).

From 18.7/4 "Other runtime support" in the standard:

If any automatic objects would be destroyed by a thrown exception transferring
control to another (destination) point in the program, then a call to longjmp(jbuf, val) at the throw point that transfers control to the same (destination) point has undefined behavior.

So the bottom line is that setjmp()/longjmp() do not play well in C++.

setjmp / longjmp does not jump where I think it should

First, the behavior of your calls to setjmp is not defined by the C standard because they violate the constraints in C 2018 7.13.1.1 4 and 5:

An invocation of the setjmp macro shall appear only in one of the following contexts:

— the entire controlling expression of a selection or iteration statement;

— one operand of a relational or equality operator with the other operand an integer constant expression, with the resulting expression being the entire controlling expression of a selection or iteration statement;

— the operand of a unary ! operator with the resulting expression being the entire controlling expression of a selection or iteration statement; or

— the entire expression of an expression statement (possibly cast to void).

If the invocation appears in any other context, the behavior is undefined.

For example, in if( ( sjr = setjmp( *s_pEnvA ) ) == 0 ), the setjmp invocation is not the entire controlling expression, it is not an operand of a relational or equality operator (<, <=, >, >=, ==, or !=), it is not the operand of !, and it is not the entire expression of an expression statement.

Second, longjmp can only jump up the call stack, to functions that are still executing. Once a call to a function stops executing (as when it returns), you cannot jump back into that call. Your code saves the context in routineB, then jumps to routineA (which ends execution of routineB), then attempts to jump to the saved context. But C 2018 7.13.2.1 2, about longjmp says:

… if the function containing the invocation of the setjmp macro has terminated execution in the interim,… the behavior is undefined.

Can addresses of unmodified locals wind up corrupted in setjmp/longjmp?

Is there a reason the helper call is "embedded" into the controlling expression of if through ?: operator? This is actually a violation of the language requirements that says

7.13.1.1 The setjmp macro

4 An invocation of the setjmp macro shall appear only in one of the
following contexts:

— the entire controlling expression of a selection
or iteration statement;

— one operand of a relational or equality
operator with the other operand an integer constant expression, with
the resulting expression being the entire controlling expression of
a selection or iteration statement;

— the operand of a unary !
operator with the resulting expression being the entire controlling
expression of a selection or iteration statement; or

— the entire
expression of an expression statement (possibly cast to void).

5 If
the invocation appears in any other context, the behavior is
undefined.

The whole point of that requirement is to make sure the "unpredictable" return from setjmp, triggered by longjmp, should not land in the middle of an expression evaluation, i.e. in an unsequenced context. In your specific example it is rather obvious that from the point of view of abstract C language, variable error cannot possibly be changed by setjmp call, which opens the door for many optimizations.

It is hard to say what happened here, since helper receives a pointer &error, not error's direct value. At the surface everything seems fine from practical point of view. But formally the behavior is undefined.

In your case, you should not try to fix thing by making variables volatile, but rather should simplify the context the setjmp is used in to conform with the above requirements. Something along the lines of

if (setjmp(buf) != 0) {
helper(&error);
...
return;
}


Related Topics



Leave a reply



Submit