-
Notifications
You must be signed in to change notification settings - Fork 15.1k
Description
The following program, when compiled with Clang, produces an executable that, when run, results in a stack overflow due to recursive re-initialization of a variable with thread local storage duration. This reproduces with Clang 16 trunk back to at least Clang 13. I've only tested on x86_64 Linux.
The problem appears to be that the initialization of the tlsdm
thread local variable causes an invocation of the ct<T>::mc
constructor; the constructor then calls ct<T>::smf()
which references tlsdm
; the reference triggers a check to see if the variable has been initialized; the variable is found to have not yet been initialized (presumably because construction is not yet complete); the constructor is invoked again, and the cycle repeats.
The problem only appears to occur when templates are involved.
I would not be surprised to learn that the program exhibits undefined-behavior according to the C++ standard, but I wasn't able to identify wording that lead me a conclusive answer.
$ cat t.cpp
template<typename T>
struct ct {
struct mc {
mc() { ct<T>::smf(); }
void mf() const {}
};
thread_local static mc tlsdm;
static void smf() { tlsdm.mf(); }
};
template<typename T>
thread_local typename ct<T>::mc ct<T>::tlsdm;
int main() {
ct<int>::smf();
}
$ clang --version
clang version 16.0.0 (https://github.com/tahonermann/llvm-project.git 6073e0a2f7018fb01283f48e946a45aff5512eae)
Target: x86_64-unknown-linux-gnu
Thread model: posix
...
$ clang t.cpp -g -o t
$ ./t
Segmentation fault (core dumped)
$ gdb ./t
...
(gdb) break 'ct<int>::smf'
...
(gdb) run
... (several break and continue cycles) ...
Breakpoint 1, 0x0000555555554674 in ct<int>::smf() ()
(gdb) bt
#0 0x0000555555554674 in ct<int>::smf() ()
#1 0x00005555555546a1 in ct<int>::mc::mc() ()
#2 0x0000555555554559 in TLS init function for ct<int>::tlsdm ()
#3 0x00005555555546b9 in TLS wrapper function for ct<int>::tlsdm ()
#4 0x0000555555554679 in ct<int>::smf() ()
#5 0x00005555555546a1 in ct<int>::mc::mc() ()
#6 0x0000555555554559 in TLS init function for ct<int>::tlsdm ()
#7 0x00005555555546b9 in TLS wrapper function for ct<int>::tlsdm ()
#8 0x0000555555554679 in ct<int>::smf() ()
#9 0x00005555555546a1 in ct<int>::mc::mc() ()
#10 0x0000555555554559 in TLS init function for ct<int>::tlsdm ()
#11 0x00005555555546b9 in TLS wrapper function for ct<int>::tlsdm ()
#12 0x0000555555554679 in ct<int>::smf() ()
#13 0x00005555555546a1 in ct<int>::mc::mc() ()
#14 0x0000555555554559 in TLS init function for ct<int>::tlsdm ()
#15 0x00005555555546b9 in TLS wrapper function for ct<int>::tlsdm ()
#16 0x0000555555554679 in ct<int>::smf() ()
#17 0x00005555555546a1 in ct<int>::mc::mc() ()
#18 0x0000555555554559 in TLS init function for ct<int>::tlsdm ()
#19 0x00005555555546b9 in TLS wrapper function for ct<int>::tlsdm ()
#20 0x0000555555554679 in ct<int>::smf() ()
#21 0x0000555555554669 in main ()