(BUT REPORT?) Identifier gets freed twice when App exits

Hi all,

I’m having a problem with Identifier.

I get the following address sanitiser report when the application exits:

==21024==ERROR: AddressSanitizer: heap-use-after-free on address 0x604000070c10 at pc 0x000100890c19 bp 0x0003149e7ed0 sp 0x0003149e7ec8
READ of size 4 at 0x604000070c10 thread T0
    #0 0x100890c18 in int std::__1::__cxx_atomic_load<int>(std::__1::__cxx_atomic_base_impl<int> const*, std::__1::memory_order) atomic:1010
    #1 0x100890ab3 in std::__1::__atomic_base<int, false>::load(std::__1::memory_order) const atomic:1643
    #2 0x1008998a3 in juce::Atomic<int>::get() const juce_Atomic.h:64
    #3 0x1040a80d8 in juce::StringHolder::isEmptyString(juce::StringHolder*) juce_String.cpp:219
    #4 0x1040a7d94 in juce::StringHolder::release(juce::StringHolder*) juce_String.cpp:162
    #5 0x103f1d4b6 in juce::StringHolder::release(juce::CharPointer_UTF8) juce_String.cpp:169
    #6 0x103f1d2f1 in juce::String::~String() juce_String.cpp:247
    #7 0x103e6c798 in juce::String::~String() juce_String.cpp:246
    #8 0x10099bac4 in juce::ArrayBase<juce::String, juce::DummyCriticalSection>::clear() juce_ArrayBase.h:242
    #9 0x10099b958 in juce::ArrayBase<juce::String, juce::DummyCriticalSection>::~ArrayBase() juce_ArrayBase.h:55
    #10 0x10099b908 in juce::ArrayBase<juce::String, juce::DummyCriticalSection>::~ArrayBase() juce_ArrayBase.h:54
    #11 0x10099b8b8 in juce::Array<juce::String, juce::DummyCriticalSection, 0>::~Array() juce_Array.h:132
    #12 0x10099a778 in juce::Array<juce::String, juce::DummyCriticalSection, 0>::~Array() juce_Array.h:132
    #13 0x1040bad55 in juce::StringPool::~StringPool() juce_StringPool.h:39
    #14 0x103f54ad8 in juce::StringPool::~StringPool() juce_StringPool.h:39
    #15 0x7ff81f259dd3 in __cxa_finalize_ranges+0x198 (libsystem_c.dylib:x86_64+0x2edd3)
    #16 0x7ff81f259bed in exit+0x22 (libsystem_c.dylib:x86_64+0x2ebed)
    #17 0x7ff81f36d374 in dyld4::LibSystemHelpers::exit(int) const+0xa (libdyld.dylib:x86_64+0x6374)
    #18 0x2100ae547 in start+0x1f7 (dyld:x86_64+0x5547)

0x604000070c10 is located 0 bytes inside of 35-byte region [0x604000070c10,0x604000070c33)
freed by thread T0 here:
    #0 0x126bba78d in wrap__ZdaPv+0x7d (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x5478d)
    #1 0x1040a7e3d in juce::StringHolder::release(juce::StringHolder*) juce_String.cpp:164
    #2 0x103f1d4b6 in juce::StringHolder::release(juce::CharPointer_UTF8) juce_String.cpp:169
    #3 0x103f1d2f1 in juce::String::~String() juce_String.cpp:247
    #4 0x103e6c798 in juce::String::~String() juce_String.cpp:246
    #5 0x103f15de8 in juce::Identifier::~Identifier() juce_Identifier.cpp:27
    #6 0x103e64b18 in juce::Identifier::~Identifier() juce_Identifier.cpp:27
    #7 0x7ff81f259dd3 in __cxa_finalize_ranges+0x198 (libsystem_c.dylib:x86_64+0x2edd3)
    #8 0x7ff81f259bed in exit+0x22 (libsystem_c.dylib:x86_64+0x2ebed)
    #9 0x7ff81f36d374 in dyld4::LibSystemHelpers::exit(int) const+0xa (libdyld.dylib:x86_64+0x6374)
    #10 0x2100ae547 in start+0x1f7 (dyld:x86_64+0x5547)

previously allocated by thread T0 here:
    #0 0x126bba37d in wrap__Znam+0x7d (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x5437d)
    #1 0x103f1e696 in juce::StringHolder::createUninitialisedBytes(unsigned long) juce_String.cpp:71
    #2 0x103f20e42 in juce::CharPointer_UTF8 juce::StringHolder::createFromCharPointer<juce::CharPointer_UTF8>(juce::CharPointer_UTF8) juce_String.cpp:84
    #3 0x103f20b75 in juce::String::String(juce::CharPointer_UTF8) juce_String.cpp:349
    #4 0x103e8be0f in juce::String::String(juce::CharPointer_UTF8) juce_String.cpp:349
    #5 0x103f52ed0 in juce::String juce::addPooledString<juce::CharPointer_UTF8>(juce::Array<juce::String, juce::DummyCriticalSection, 0>&, juce::CharPointer_UTF8 const&) juce_StringPool.cpp:96
    #6 0x103f169c3 in juce::StringPool::getPooledString(char const*) juce_StringPool.cpp:107
    #7 0x103f1662c in juce::Identifier::Identifier(char const*) juce_Identifier.cpp:53
    #8 0x103e76b80 in juce::Identifier::Identifier(char const*) juce_Identifier.cpp:54
    #9 0x1008a21a6 in __cxx_global_var_init.182 Sound.h:31
    #10 0x1008a2ce8 in _GLOBAL__sub_I_MultiSoundEditor.cpp MultiSoundEditor.cpp
    #11 0x2100bddda in invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const+0xb5 (dyld:x86_64+0x14dda)
    #12 0x2100e40ba in invocation function for block in dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const+0x80 (dyld:x86_64+0x3b0ba)
    #13 0x2100db859 in invocation function for block in dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const+0x22c (dyld:x86_64+0x32859)
    #14 0x2100aadb2 in dyld3::MachOFile::forEachLoadCommand(Diagnostics&, void (load_command const*, bool&) block_pointer) const+0x80 (dyld:x86_64+0x1db2)
    #15 0x2100db5ea in dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const+0xb2 (dyld:x86_64+0x325ea)
    #16 0x2100e3aeb in dyld3::MachOAnalyzer::forEachInitializerPointerSection(Diagnostics&, void (unsigned int, unsigned int, unsigned char const*, bool&) block_pointer) const+0x75 (dyld:x86_64+0x3aaeb)
    #17 0x2100e3d5d in dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const+0x181 (dyld:x86_64+0x3ad5d)
    #18 0x2100bdd0d in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const+0x8f (dyld:x86_64+0x14d0d)
    #19 0x2100bde99 in dyld4::Loader::runInitializersBottomUp(dyld4::RuntimeState&, dyld3::Array<dyld4::Loader const*>&) const+0xb1 (dyld:x86_64+0x14e99)
    #20 0x2100bdf3d in dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const+0x6b (dyld:x86_64+0x14f3d)
    #21 0x2100d154d in dyld4::APIs::runAllInitializersForMain()+0xdd (dyld:x86_64+0x2854d)
    #22 0x2100af37c in dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*)+0xd72 (dyld:x86_64+0x637c)
    #23 0x2100ae4d3 in start+0x183 (dyld:x86_64+0x54d3)

SUMMARY: AddressSanitizer: heap-use-after-free atomic:1010 in int std::__1::__cxx_atomic_load<int>(std::__1::__cxx_atomic_base_impl<int> const*, std::__1::memory_order)
Shadow bytes around the buggy address:
  0x1c080000e130: fa fa fd fd fd fd fd fa fa fa 00 00 00 00 00 03
  0x1c080000e140: fa fa 00 00 00 00 03 fa fa fa 00 00 00 00 07 fa
  0x1c080000e150: fa fa 00 00 00 00 07 fa fa fa 00 00 00 00 00 03
  0x1c080000e160: fa fa 00 00 00 00 07 fa fa fa 00 00 00 00 00 03
  0x1c080000e170: fa fa 00 00 00 00 07 fa fa fa 00 00 00 00 00 03
=>0x1c080000e180: fa fa[fd]fd fd fd fd fa fa fa 00 00 00 00 00 07
  0x1c080000e190: fa fa 00 00 00 00 00 03 fa fa 00 00 00 00 07 fa
  0x1c080000e1a0: fa fa fd fd fd fd fd fd fa fa 00 00 00 00 03 fa
  0x1c080000e1b0: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fd
  0x1c080000e1c0: fa fa fd fd fd fd fd fa fa fa 00 00 00 00 07 fa
  0x1c080000e1d0: fa fa 00 00 00 00 03 fa fa fa fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==21024==ABORTING
AddressSanitizer report breakpoint hit. Use 'thread info -s' to get extended information about the report.

As one can see, this is related to the allocation and destruction of global variables. I know, this is kind of my first mistake using global variables. It’s a left over from the original SamplerPluginDemo. The one line related to this issue is (in header file)

namespace IDs
{
#define DECLARE_ID(name)  const juce::Identifier name(#name)
  DECLARE_ID(adsrEnabled); // Here is Sound.h line 31
#undef DECLARE_ID
}

But this alone does not trigger the error. The error is somehow triggered by my meta programming:

// In header
struct adsrEnabled {};
template <typename property> const Identifier identifier;

// In (of course only one) cpp
template <> const Identifier identifier<adsrEnabled> = IDs::adsrEnabled;

These two bits are enough to reproduce the error in a new JUCE project. Looking at the stack trace the global string pool seems to be freed first and then some kind of left over Identifier. While reproducing the bug, it is also possible to go vice versa. After half a day of searching I still cannot come up with an idea how/why this happens. A work around is of course very straight forward (just don’t use a global template variable but a template function) but I’d like to understand the problem first before going down this road.

Thanks in advance!