Skip to content

Stack overflow due to thread_local variable re-initialization #57828

@tahonermann

Description

@tahonermann

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 ()

Metadata

Metadata

Assignees

No one assigned

    Labels

    clang:codegenIR generation bugs: mangling, exceptions, etc.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions