The stack and longjmp/setjmp

J

jacob navia

The stack is one of the central structures in C. Continuing this series
of installments, we come to the interactions of longjmp/setjmp (and also
the lowly "goto" statement) with the stack.

As you know, longjmp/setjmp allow a non-local transfer of control from
a point in the execution of the program to another, completely unrelated
point using a previously established context.

One of the problems with the couple of functions in question (setjmp
establishes this context, longjmp uses a context to return to the
point where the context was established) is a situation like this:

int fn(void)
{
char *m = malloc(BUFSIZ);
fn1(m);
free(m);
}

This looks perfectly correct, no leaks.

Well, the problem arises when fn1 makes a longjmp to somewhere else,
completely bypassing the free() instruction and making for a memory
leak.

One of the advantages of alloca() by the way, is that alloca() will NOT
produce a leak in this case. But obviously too, the call to "malloc"
is just an example of resource allocation. fopen() could have been
called instead of malloc, and we would have the same problem.

There are several solutions for this, one of them would be to indicate
a piece of code that should be executed in the case of a non local
jump across this function.

This is the solution proposed by C's cousin, C++. The run time "walks
upwards" the stack, executing all code marked for execution in this
case. This is not possible in standard C (yet) but there are proposals
that try to introduce the famous try/catch couple into C.

In any case there is absolutely NO solution for this problem within
standard C.

But even without longjmp/setjmp, a simple "goto" can make things quite
messy:

goto lab3; // invalid: going INTO scope of VLA.
{
double a[n];
a[j] = 4.4;
lab3:
a[j] = 3.3;
goto lab4; // valid: going WITHIN scope of VLA.
a[j] = 5.5;
lab4:
a[j] = 6.6;
}
goto lab4; // invalid: going INTO scope of VLA

This rules are specified by the standard to preserve the integrity
of the stack. If there is an implementation that increases/decreases
dynamically the stack, jumping into the middle of such a block
bypassing the stack adjustment can only crash the program!

Note that the problem *remains* even if you do NOT use C99 t all:
{
double a[5];
a[j] = 4.4;
lab3:
a[j] = 3.3;
goto lab4; // valid: going WITHIN scope of VLA.
a[j] = 5.5;
lab4:
a[j] = 6.6;
}

In an implementation that increases/decreases the stack pointer when
such a block is found, the goto into the middle of the block would
bypass the stack adjusting anyway, even if it is a fixed amount and
not a variable one as above.

What are the general rules that can be followed to avoid problems?

As I said above, there is NO standard solution for the longjmp/setjmp
problem within the ISO subset of the language

For the goto, the solution is quite obvious:

"Never do a goto into the middle of a block"
 
S

santosh

jacob said:
The stack is one of the central structures in C. Continuing this
series of installments, we come to the interactions of longjmp/setjmp
(and also the lowly "goto" statement) with the stack.

As you know, longjmp/setjmp allow a non-local transfer of control from
a point in the execution of the program to another, completely
unrelated point using a previously established context.

One of the problems with the couple of functions in question (setjmp
establishes this context, longjmp uses a context to return to the
point where the context was established) is a situation like this:

int fn(void)
{
char *m = malloc(BUFSIZ);
fn1(m);
free(m);
}

This looks perfectly correct, no leaks.

Well, the problem arises when fn1 makes a longjmp to somewhere else,
completely bypassing the free() instruction and making for a memory
leak.

One of the advantages of alloca() by the way, is that alloca() will
NOT produce a leak in this case.

<snip>

Do you mean to say that memory allocated with alloca will be properly
freed even if the execution jumps to another function via longjmp?
 
J

jacob navia

santosh said:
<snip>

Do you mean to say that memory allocated with alloca will be properly
freed even if the execution jumps to another function via longjmp?

Of course. Since longjmp unwinds the stack, that memory will be
automatically freed when the stack is unwound.
 
H

Harald van Dijk

<snip>

Do you mean to say that memory allocated with alloca will be properly
freed even if the execution jumps to another function via longjmp?

If he is, he's correct for at least the system I'm using, which explains
this its documentation for alloca.

Of course, as alloca is non-standard, other systems are permitted to
implement it differently. I don't know if any do, but I expect it would be
implemented the same way as VLAs, and VLAs can be leaked by longjmp.
 
E

Eric Sosman

jacob said:
[...]
As you know, longjmp/setjmp allow a non-local transfer of control from
a point in the execution of the program to another, completely unrelated
point using a previously established context.

"Completely unrelated?" There are some rather strict
requirements on the relation of the longjmp() call to the
corresponding setjmp().
 
J

jacob navia

Eric said:
jacob said:
[...]
As you know, longjmp/setjmp allow a non-local transfer of control from
a point in the execution of the program to another, completely unrelated
point using a previously established context.

"Completely unrelated?" There are some rather strict
requirements on the relation of the longjmp() call to the
corresponding setjmp().

Interesting

Can you explain what those strict requirements could be?
 
S

santosh

jacob said:
Eric said:
jacob said:
[...]
As you know, longjmp/setjmp allow a non-local transfer of control
from a point in the execution of the program to another, completely
unrelated point using a previously established context.

"Completely unrelated?" There are some rather strict
requirements on the relation of the longjmp() call to the
corresponding setjmp().

Interesting

Can you explain what those strict requirements could be?

n1256 7.13.2.1 (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 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.
 
J

jacob navia

santosh said:
jacob said:
Eric said:
jacob navia wrote:
[...]
As you know, longjmp/setjmp allow a non-local transfer of control
from a point in the execution of the program to another, completely
unrelated point using a previously established context.
"Completely unrelated?" There are some rather strict
requirements on the relation of the longjmp() call to the
corresponding setjmp().
Interesting

Can you explain what those strict requirements could be?

n1256 7.13.2.1 (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 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.

That is surely not "strong requirements"!

1: Obviously, you call setjmp THEN longjmp. Doing otherwise is ABI
(A Bad Idea)
2: The setjmp call point should be in the stack... Not very difficult
to understand.
3: To preserve the stack, you can't longjmp into the middle of a
block with variable length arrays. See my discussion of goto
in my post.

Obviously, there are *some* requirements. I wouldn't call them
"strict", but if you want, OK.

:)
 
C

CBFalconer

jacob said:
The stack is one of the central structures in C. Continuing this
series of

off-topic posts. C does not have a stack. Your C may have a stack.
installments, we come to the interactions of longjmp/setjmp (and
also the lowly "goto" statement) with the stack.

Please post these things to comp.std.c, or to comp.compilers.lcc,
or to some newsgroup where it may be topical. Not here.
 
E

Eric Sosman

jacob said:
santosh said:
jacob said:
Eric Sosman wrote:
jacob navia wrote:
[...]
As you know, longjmp/setjmp allow a non-local transfer of control
from a point in the execution of the program to another, completely
unrelated point using a previously established context.
"Completely unrelated?" There are some rather strict
requirements on the relation of the longjmp() call to the
corresponding setjmp().

Interesting

Can you explain what those strict requirements could be?

n1256 7.13.2.1 (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 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.

That is surely not "strong requirements"!

No, some people seem to treat them as mere advisories.
Others are less eager to encounter undefined behavior.
1: Obviously, you call setjmp THEN longjmp. Doing otherwise is ABI
(A Bad Idea)
2: The setjmp call point should be in the stack... Not very difficult
to understand.
3: To preserve the stack, you can't longjmp into the middle of a
block with variable length arrays. See my discussion of goto
in my post.

Obviously, there are *some* requirements. I wouldn't call them
"strict", but if you want, OK.

Well, I certainly wouldn't say they add up to "completely
unrelated ..."

There's also the problem illustrated by this code:

#include <stdio.h>
#include <setjmp.h>
jmp_buf context;

void func(int which) {
if (which == 0)
setjmp (context);
else
longjmp (context, 42);
}

int main(void) {
puts ("Hello, world!");
func (0);
puts ("Goodbye, cruel world!");
func (1);
puts ("Is there life after death?");
return 0;
}

Observe that this code satisfies all three of your
conditions, yet the behavior is undefined. (Suggested fix:
tighten up the wording of your second condition.)
 
R

Richard Tobin

santosh said:
Do you mean to say that memory allocated with alloca will be properly
freed even if the execution jumps to another function via longjmp?

That's certainly the *idea* of alloca(): it's supposed to be like
local variables. Some might say that the automatic freeing makes
alloca() (or a similar mechanism) worthwhile even if it's no faster
than malloc().

-- Richard
 
J

jacob navia

CBFalconer said:
off-topic posts. C does not have a stack. Your C may have a stack.


last time we discussed this the regulars were unable to show
a single implementation in a common machine where C does NOT
have a stack. Mainframes have a stack, as I proved them, so their
only "example" vanished.

They go on because they just do not care about arguments.

C is what the regulars tell us it is. Facts notwithstanding.
Please post these things to comp.std.c, or to comp.compilers.lcc,
or to some newsgroup where it may be topical. Not here.

Ah, longjmp/setjmp are NOT topical here? That would be news to me.

Obviously regulars can do/say anything. In any case I do not
care about your opinion in this point Mr Falconer.
 
A

ais523

santosh said:
jacob said:
[talking about setjmp/longjmp] Can you explain what those strict requirements could be?

n1256 7.13.2.1 (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 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.

Reading this again, something jumped out at me. At the moment, I have
a program which copies jmp_bufs using memcpy. However, the wording
here seems to imply that I can't longjmp from a copy of a jmp_buf,
only from the original, because it cares about what the argument to
the function is rather than the argument's value! So would a DS9K be
allowed to, for instance, declare jmp_buf to be typedef char
jmp_buf[1], then look at the address of the jmp_buf given to setjmp
and save the environment somewhere entirely different, with the
address of the jmp_buf determining where it was saved, so that a
setjmp with the same argument could restore the environment even
though the jmp_buf itself didn't store any information? As a practical
matter, is using memcpy on jmp_bufs likely to give me another jmp_buf
which has the same effect on longjmp? (One alternative I can see,
which seems to fit with the letter of the Standard as far as I can
tell, is to malloc jmp_bufs and store shallow copies of them in the
structures (i.e. pointers to the jmp_bufs) rather than the jmp_bufs
themselves, so dereferencing the pointer always gives what is truly
the same jmp_buf, rather than a copy, which seems to be what the
Standard implies.)
 
A

ais523

jacob said:
last time we discussed this the regulars were unable to show
a single implementation in a common machine where C does NOT
have a stack. Mainframes have a stack, as I proved them, so their
only "example" vanished.
I've worked on an embedded system which was being programmed in a very
C-like language, where the system had no stack on which it was
possible to store data (the stack was 8 words long, and could only
hold addresses, because the only way to push data onto it was to push
the program counter). (The language wasn't quite freestanding C89, but
only because it didn't meet some of the minimum implementation limits,
and some of the data types didn't have a large enough range without
resorting to things that couldn't be used by strictly conforming
programs (e.g. INT_MAX equalled 127 unless you changed it using
pragmas).) It basically inlined as many function calls as possible to
save stack space (and recursive functions generally just ran out of
stack if they tried to recurse too deeply). However, add in a few
pragmas to fix the data type problem, and most C89 algorithms would
work on it, as long as they fit into the available ROM and didn't use
too much RAM (which was measured in tens of bytes). Admittedly, I
often had to resort to assembly language to optimise by hand to get
things to fit, or (in some cases) because I had to force various parts
of the program to take up a certain number of clock cycles, something
which C is not designed for, but the C implementation was certainly
possible. (OK, so maybe it's not a 'common machine', but you seem to
be limiting the field too much; C appears to have been designed to be
usable on such devices to some extent, or syntax like (char volatile
*)6=0; would not be allowed (doing that sort of thing was pretty
useful on the system because I/O was memory-mapped). BTW, I don't
think the compiler implemented longjmp/setjmp anyway, but I think it
probably would have been possible in theory by exploiting unusual
hardware properties of the device to get around the fact that you
couldn't pop something off the function-return-address stack without
jumping to it.)
 
R

Richard

CBFalconer said:
off-topic posts. C does not have a stack. Your C may have a stack.


Please post these things to comp.std.c, or to comp.compilers.lcc,
or to some newsgroup where it may be topical. Not here.

I'm not sure who you think you are, but please stop polluting
interesting threads about features of 99.99% of C environments. If you
have nothing to say, say nothing.
 
K

Keith Thompson

jacob navia said:
last time we discussed this the regulars were unable to show
a single implementation in a common machine where C does NOT
have a stack. Mainframes have a stack, as I proved them, so their
only "example" vanished.

They go on because they just do not care about arguments.
[...]

It was pointed out numerous times that the term "stack" was being used
with two distinct meanings. When you refer to "The stack", you're
presumably talking about a contiguous hardware stack managed via a
stack pointer register. The "stack" that mainframes undeniably must
have is a more abstract thing, a last-in first-out data structure that
isn't necessarily contiguous.

Your insistence on ignoring this distinction is disingenuous. You can
(claim to) win any argument if you ignore enough facts.

Some mainframes do not have a contiguous hardware stack. You have not
demonstrated otherwise. Those same mainframes do have a kind of
logical stack. Nobody has claimed that they don't. You claim to have
disproven some claim that somebody else had made. That never
happened.
 
K

Kenny McCormack

Keith Thompson said:
[...]

It was pointed out numerous times that the term "stack" was being used
with two distinct meanings. When you refer to "The stack", you're
presumably talking about a contiguous hardware stack managed via a
stack pointer register. The "stack" that mainframes undeniably must
have is a more abstract thing, a last-in first-out data structure that
isn't necessarily contiguous.

Your insistence on ignoring this distinction is disingenuous. You can
(claim to) win any argument if you ignore enough facts.

Some mainframes do not have a contiguous hardware stack. You have not
demonstrated otherwise. Those same mainframes do have a kind of
logical stack. Nobody has claimed that they don't. You claim to have
disproven some claim that somebody else had made. That never
happened.

Wow. Words fail me.
 
C

christian.bau

That's certainly the *idea* of alloca(): it's supposed to be like
local variables.  Some might say that the automatic freeing makes
alloca() (or a similar mechanism) worthwhile even if it's no faster
than malloc().

I had a look at what the gcc documentation says about alloca. Quite
interesting. When you call alloca, memory is supposed to stay
allocated until the function call returns. When you use a variable
sized array, a very similar mechanism is used, but the memory only
stays allocated as long as the scope of the array stays in existence.
Now comes the crunch: If you use a variable sized array with that
compiler, and _then_ call alloca (), the memory allocated by alloca ()
disappears at the same time as the variable sized array!

I would think that many implementations have some nasty little hidden
surprises when you use alloca.
 
A

Antoninus Twink

off-topic posts. C does not have a stack. Your C may have a stack.

*Yeeawwwwwn*

Is that a broken record I hear?
Please post these things to comp.std.c, or to comp.compilers.lcc,
or to some newsgroup where it may be topical. Not here.

Please go and put your head in a gas oven.

The behavior of longjmp, setjmp and goto is 100% C.

And fix your goddamn signature.
 
A

Antoninus Twink

last time we discussed this the regulars were unable to show
a single implementation in a common machine where C does NOT
have a stack. Mainframes have a stack, as I proved them, so their
only "example" vanished.

They go on because they just do not care about arguments.

C is what the regulars tell us it is. Facts notwithstanding.

Facts are off-topic in clc.
Ah, longjmp/setjmp are NOT topical here? That would be news to me.

Obviously regulars can do/say anything. In any case I do not
care about your opinion in this point Mr Falconer.

No one cares about Falconer's opinion in this or any other point - even
The Clique have got tired of his dribbling nonsense.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top