Bug 68200 - g++ 5.2 optimizes out pointer assignment in libstdc++ mt_allocator freelist destructor, causing crash at global-dtor time
Summary: g++ 5.2 optimizes out pointer assignment in libstdc++ mt_allocator freelist d...
Status: RESOLVED DUPLICATE of bug 52604
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 5.2.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-11-03 17:15 UTC by Joshua Oreman
Modified: 2015-11-10 13:24 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Joshua Oreman 2015-11-03 17:15:54 UTC
PR 52064 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52604) added a guard assignment to libstdc++-v3/src/c++98/mt_allocator.cc (_M_thread_freelist = 0), so that attempts to use the freelist after it had been destroyed would not access invalid memory. This resolved issues with global-scope STL containers that used mt_allocator being destroyed after the mt_allocator freelist was destroyed.

    ~__freelist()
    {
      if (_M_thread_freelist_array)
        {
          __gthread_key_delete(_M_key);
          ::operator delete(static_cast<void*>(_M_thread_freelist_array));
          _M_thread_freelist = 0;
        }
    }

The setting of the _M_thread_freelist member can validly be optimized away, because accessing the object after its destructor has run is undefined behavior, and the _M_thread_freelist assignment can't be detected other than by doing that. Since PR 52064 was resolved in 2012, it appears that gcc has become smarter about optimizing this case, and the guard assignment no longer is compiled. I get this assembly:

0x7ffff055d4f0 <(anonymous namespace)::__freelist::~__freelist()>:
+ 0:  cmpq   $0x0,0x8(%rdi)
+ 5:  je     0x7ffff055d510 <(anonymous namespace)::__freelist::~__freelist()+32>
+ 7:  push   %rbx
+ 8:  mov    %rdi,%rbx
+11:  mov    0x18(%rdi),%edi
+14:  callq  0x7ffff054d670 <pthread_key_delete@plt>
+19:  mov    0x8(%rbx),%rdi
+23:  pop    %rbx
+24:  jmpq   0x7ffff054b9f0 <_ZdlPv@plt>
+29:  nopl   (%rax)
+32:  repz retq 

It does seem like the existing code is taking advantage of undefined behavior, so maybe a larger change is in order, but changing the assignment to happen through a volatile pointer - const_cast<_Thread_record *volatile &>(_M_thread_freelist) = 0 - resolves the issue.
Comment 1 Joshua Oreman 2015-11-03 17:16:44 UTC
Correction: the original bug was PR 52604, not PR 52064.
Comment 2 Richard Biener 2015-11-04 09:18:09 UTC
Duplicate then (52604 is not marked fixed anyway).

*** This bug has been marked as a duplicate of bug 52604 ***
Comment 3 Jonathan Wakely 2015-11-10 13:24:27 UTC
I would like to deprecate mt_allocator, I don't recommend using it.