Reputation: 8112
I read code of C library and can not understand what is going on:
struct Foo *foo = NULL;
lib_var((void *)&foo);
if (setjmp(get_jmp_buf()) == 0) {
foo = ...;
// other calculation that may cause longjmp
} else {
//something bad happens
drop_if_not_null(foo);
}
where lib_var
is function that has such prototype void lib_var(void *);
,
and such implementation in separate C file:
void lib_var(void *variable)
{
// nothing
}
So what for lib_var
function, something like volatile
to force compiler reread variable from memory in setjmp() != 0
case?
Is this undefined behavior? Because of I am sure LTO remove such completely useless code,
and LTO should not change behavior of confirming to standard code.
Upvotes: 0
Views: 71
Reputation: 58393
Yes, it's undefined behavior from the standpoint of standard C - or at least, the value of foo
is indeterminate and can't be safely used for anything.
C17 (n2176) 7.13.2.1 (3):
All accessible objects have values, and all other components of the abstract machine have state, as of the time the longjmp function was called, except that the values of objects of automatic storage duration that are local to the function containing the invocation of the corresponding setjmp macro that do not have volatile-qualified type and have been changed between the setjmp invocation and longjmp call are indeterminate.
That's precisely the situation here: foo
has automatic storage duration and is local to the function where setjmp
is invoked, it's not volatile
-qualified, and it is changed between setjmp
and longjmp
.
The authors of the code were probably thinking that passing &foo
to lib_var
would ensure that it is allocated in memory, not in a register, and that since the compiler couldn't know whether lib_var
would modify foo
, it couldn't constant-propagate the value NULL
into the else
block. But as you say, LTO or various other optimizations could break this. It's possible the authors intended only to support a particular compiler that they knew didn't do any such thing, or that provided some stronger guarantees beyond what the standard says, but it's also possible they were just confused.
The correct fix would be to declare foo
as volatile
, i.e. struct Foo * volatile foo = NULL;
.
Upvotes: 2