In the case that a local jmp_buf is actually represented by registers rather than stack memory, is it possible for setjmp or longjmp to cause the contents of the local jmp_buf to be indeterminate when setjmp returns from a longjmp?
The suggested duplicate Is it allowed to do longjmp() multiple times for one setjmp() call? asks in the context of a global variable. It was suggested since the answer explains that the variable is not modified in a way that would prevent it from being subsequently called, that sufficiently answers the question for a local variable too.
However, treatment of a local variable differs from a global variable. In particular, if the local jmp_buf variable is actually held in registers and not memory, restoration after longjmp may not render a reusable jmp_buf variable.
As an academic exercise, I was attempting to use setjmp as a substitute for goto. To keep the loop replacement local to the function, the jmp_buf used is also a local variable.
void foo (int n) {
jmp_buf jb;
volatile int i;
i = setjmp(jb);
if (i < n) {
do_stuff(i);
longjmp(jb, ++i);
}
}
I understand that non-volatile local variables that have been modified between the setjmp call and the longjmp call are unspecified after longjmp. However, I was curious about the local jmp_buf variable itself, particularly in the case where the jmp_buf variable is represented by registers rather than memory on the stack.
It is unclear if longjmp itself can be considered something that may modify the local jmp_buf variable, and whether this means its contents are unspecified when setjmp returns after the call to longjmp.
I thought I could easily dispatch the issue by declaring jb to be volatile, but this triggered a warning (which I treat as an error):
... error: passing argument 1 of ‘_setjmp’ discards ‘volatile’ qualifier from pointer target type [-Werror=discarded-qualifiers]
setjmp(jb);
^~
Also, the specification of setjmp does not speak to whether it is saving the register values as they would be after setting the jmp_buf or before setting the jmp_buf.
If I need to be concerned about it, I can create a volatile copy of the jmp_buf and copy its contents around. But, I'd like to avoid that if it isn't required.
TL;DR Since the standard isn't clear, it is better to treat the value of a local
jmp_bufas indeterminate after a locallongjmp.ISO/IEC 9899:2018 §17.13.1.1 ¶2 describes the behavior of
setjmp, and ¶3 describes what happens on return.We infer that a successful return from
setjmpresults in an initializedjmp_bufargument. However, there is no mention if the initialization takes into account of thejmp_bufitself having automatic storage duration (and so, itself could be represented by registers rather than by memory).ISO/IEC 9899:2018 §7.13.2.1 ¶3 describes the behavior of
longjmp, and is worded the same as the 2011 text cited by Marko:However, the meaning of the word between is somewhat elusive. The standard could have explicitly specified the context of between to mean after
setjmpcompleted. For example, the wording could have stated:The current wording suggests that one should include the invocation of
setjmpitself as something that may trigger the indeterminate condition.There is a possibility that the semantics of the return of
longjmpcovers for this problem, however. ISO/IEC 9899:2018 §17.13.2.1 ¶4 states:This sentence could be interpreted to mean that the invocation semantics of
setjmpis the same whether it returns from direct invocation or returns from alongjmpfunction. That is, the return ofsetjmpmeans thejmp_bufargument is initialized and can be used by anotherlongjmp. But again, this is not clear. In the most limiting interpretation, the as if clause only speaks to the value returned bysetjmp, and not the invocation itself.Since the semantics are ambiguous, it is proper to treat the
jmp_bufobject value as indeterminate upon return fromlongjmp.