tzaeschke / phtree-cpp

PH-Tree C++ implementation
Apache License 2.0
29 stars 9 forks source link

relocate in multimap causes heap-use-after-free issue #153

Open rockingdice opened 1 year ago

rockingdice commented 1 year ago

I cannot make a repro. It's a very big project. Dunno if the logs are helpful:

=================================================================
==63389==ERROR: AddressSanitizer: heap-use-after-free on address 0x61200100a480 at pc 0x000117eededa bp 0x7ff7bb329ec0 sp 0x7ff7bb329eb8
READ of size 2 at 0x61200100a480 thread T0
    #0 0x117eeded9 in improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long>::IsNode() const entry.h:158
    #1 0x117eefc99 in improbable::phtree::v16::PhTreeV16<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, improbable::phtree::DM_TREE_CONVERTER<2ul, fpm::fixed<int, long long, 8u, true>, long long, improbable::phtree::DMScalarConverter> >::clean_up(std::__1::array<long long, 4ul> const&, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long>*, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long>*) phtree_v16.h:467
    #2 0x117eed5ba in unsigned long improbable::phtree::v16::PhTreeV16<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, improbable::phtree::DM_TREE_CONVERTER<2ul, fpm::fixed<int, long long, 8u, true>, long long, improbable::phtree::DMScalarConverter> >::_relocate_mm<unsigned long improbable::phtree::PhTreeMultiMap<2ul, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, improbable::phtree::DM_TREE_CONVERTER<2ul, fpm::fixed<int, long long, 8u, true>, long long, improbable::phtree::DMScalarConverter>, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, false, improbable::phtree::QueryIntersect>::relocate<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&>(improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&, bool)::'lambda'(phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >&, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >&)&, unsigned long improbable::phtree::PhTreeMultiMap<2ul, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, improbable::phtree::DM_TREE_CONVERTER<2ul, fpm::fixed<int, long long, 8u, true>, long long, improbable::phtree::DMScalarConverter>, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, false, improbable::phtree::QueryIntersect>::relocate<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&>(improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&, bool)::'lambda'(phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >&)&>(std::__1::array<long long, 4ul> const&, std::__1::array<long long, 4ul> const&, bool, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&, unsigned long improbable::phtree::PhTreeMultiMap<2ul, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, improbable::phtree::DM_TREE_CONVERTER<2ul, fpm::fixed<int, long long, 8u, true>, long long, improbable::phtree::DMScalarConverter>, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, false, improbable::phtree::QueryIntersect>::relocate<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&>(improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&, bool)::'lambda'(phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >&)&) phtree_v16.h:457
    #3 0x117ac1e16 in unsigned long improbable::phtree::PhTreeMultiMap<2ul, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, improbable::phtree::DM_TREE_CONVERTER<2ul, fpm::fixed<int, long long, 8u, true>, long long, improbable::phtree::DMScalarConverter>, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, false, improbable::phtree::QueryIntersect>::relocate<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&>(improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&, bool) phtree_multimap.h:455
    #4 0x117abf16a in updateUnitsPosition(entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> >&, GameWorld*) GameWorld.cpp:231
    #5 0x117af74c9 in GameWorld::updateWorld() GameWorld.cpp:1307
    #6 0x1067b4a0f in GameBattle::OnUpdate(float) GameBattle.cpp:81
    #7 0x10709cc74 in ScriptGameBattle::OnUpdate(float) ScriptGameBattle.cpp:31
    #8 0x1055d54ab in GameInterface::Update(float) GameInterface.cpp:290
    #9 0x105c05459 in SceneBase::DoUpdate(float) SceneBase.cpp:147
    #10 0x105d24095 in SceneManager::Update(float) SceneManager.cpp:167
    #11 0x1054fa880 in GameInit::DoUpdate(float) GameInit.cpp:76
    #12 0x1090af31e in GameInitGame::DoUpdate(float) GameInitGame.cpp:412
    #13 0x1053ee97d in GameContral::update(float) GameContral.cpp:236
    #14 0x1054e5eb4 in void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float)::operator()(float) const CCScheduler.h:283
    #15 0x1054e5dac in decltype(static_cast<GameContral>(fp)(static_cast<float>(fp0))) std::__1::__invoke<void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float)&, float>(GameContral&&, float&&) type_traits:3918
    #16 0x1054e5c1c in void std::__1::__invoke_void_return_wrapper<void, true>::__call<void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float)&, float>(void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float)&, float&&) invoke.h:61
    #17 0x1054e5b0c in std::__1::__function::__alloc_func<void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float), std::__1::allocator<void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float)>, void (float)>::operator()(float&&) function.h:178
    #18 0x1054dfa88 in std::__1::__function::__func<void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float), std::__1::allocator<void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float)>, void (float)>::operator()(float&&) function.h:352
    #19 0x105f439ab in std::__1::__function::__value_func<void (float)>::operator()(float&&) const function.h:505
    #20 0x105e58ee2 in std::__1::function<void (float)>::operator()(float) const function.h:1182
    #21 0x11693cd6f in cocos2d::Scheduler::update(float) CCScheduler.cpp:853
    #22 0x116718628 in cocos2d::Director::drawScene() CCDirector.cpp:266
    #23 0x11672bbb8 in cocos2d::Director::mainLoop() CCDirector.cpp:1478
    #24 0x1171b9ded in cocos2d::Application::run() CCApplication-mac.mm:97
    #25 0x1148327c6 in main main.cpp:36
    #26 0x148d7052d in start+0x1cd (dyld:x86_64+0x552d)

0x61200100a480 is located 64 bytes inside of 288-byte region [0x61200100a440,0x61200100a560)
freed by thread T0 here:
    #0 0x1472a562d in wrap__ZdlPv+0x7d (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x5c62d)
    #1 0x117b4d434 in void std::__1::__libcpp_operator_delete<void*>(void*) new:245
    #2 0x117b4d3e8 in void std::__1::__do_deallocate_handle_size<>(void*, unsigned long) new:269
    #3 0x117b4d374 in std::__1::__libcpp_deallocate(void*, unsigned long, unsigned long) new:285
    #4 0x117ea1aff in std::__1::allocator<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> > >::deallocate(std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> >*, unsigned long) allocator.h:117
    #5 0x117ea1184 in std::__1::allocator_traits<std::__1::allocator<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> > > >::deallocate(std::__1::allocator<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> > >&, std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> >*, unsigned long) allocator_traits.h:282
    #6 0x117ea10a9 in std::__1::__split_buffer<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> >, std::__1::allocator<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> > >&>::~__split_buffer() __split_buffer:347
    #7 0x117e9cea8 in std::__1::__split_buffer<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> >, std::__1::allocator<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> > >&>::~__split_buffer() __split_buffer:344
    #8 0x117efcae1 in std::__1::__wrap_iter<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> >*> std::__1::vector<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> >, std::__1::allocator<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> > > >::emplace<std::__1::piecewise_construct_t const&, std::__1::tuple<unsigned int&>, std::__1::tuple<std::__1::array<long long, 4ul> const&> >(std::__1::__wrap_iter<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> > const*>, std::__1::piecewise_construct_t const&, std::__1::tuple<unsigned int&>&&, std::__1::tuple<std::__1::array<long long, 4ul> const&>&&) vector:1886
    #9 0x117efb7e9 in auto improbable::phtree::detail::sparse_map<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> >::try_emplace_base<std::__1::array<long long, 4ul> const&>(std::__1::__wrap_iter<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> >*> const&, unsigned int, std::__1::array<long long, 4ul> const&) flat_sparse_map.h:140
    #10 0x117efa9c8 in auto improbable::phtree::detail::sparse_map<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> >::try_emplace<std::__1::array<long long, 4ul> const&>(unsigned int, std::__1::array<long long, 4ul> const&) flat_sparse_map.h:111
    #11 0x117eeeeca in improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long>& improbable::phtree::v16::Node<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long>::Emplace<>(bool&, std::__1::array<long long, 4ul> const&, unsigned int) node.h:127
    #12 0x117eed08a in unsigned long improbable::phtree::v16::PhTreeV16<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, improbable::phtree::DM_TREE_CONVERTER<2ul, fpm::fixed<int, long long, 8u, true>, long long, improbable::phtree::DMScalarConverter> >::_relocate_mm<unsigned long improbable::phtree::PhTreeMultiMap<2ul, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, improbable::phtree::DM_TREE_CONVERTER<2ul, fpm::fixed<int, long long, 8u, true>, long long, improbable::phtree::DMScalarConverter>, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, false, improbable::phtree::QueryIntersect>::relocate<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&>(improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&, bool)::'lambda'(phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >&, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >&)&, unsigned long improbable::phtree::PhTreeMultiMap<2ul, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, improbable::phtree::DM_TREE_CONVERTER<2ul, fpm::fixed<int, long long, 8u, true>, long long, improbable::phtree::DMScalarConverter>, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, false, improbable::phtree::QueryIntersect>::relocate<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&>(improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&, bool)::'lambda'(phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >&)&>(std::__1::array<long long, 4ul> const&, std::__1::array<long long, 4ul> const&, bool, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&, unsigned long improbable::phtree::PhTreeMultiMap<2ul, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, improbable::phtree::DM_TREE_CONVERTER<2ul, fpm::fixed<int, long long, 8u, true>, long long, improbable::phtree::DMScalarConverter>, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, false, improbable::phtree::QueryIntersect>::relocate<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&>(improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&, bool)::'lambda'(phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >&)&) phtree_v16.h:438
    #13 0x117ac1e16 in unsigned long improbable::phtree::PhTreeMultiMap<2ul, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, improbable::phtree::DM_TREE_CONVERTER<2ul, fpm::fixed<int, long long, 8u, true>, long long, improbable::phtree::DMScalarConverter>, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, false, improbable::phtree::QueryIntersect>::relocate<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&>(improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&, bool) phtree_multimap.h:455
    #14 0x117abf16a in updateUnitsPosition(entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> >&, GameWorld*) GameWorld.cpp:231
    #15 0x117af74c9 in GameWorld::updateWorld() GameWorld.cpp:1307
    #16 0x1067b4a0f in GameBattle::OnUpdate(float) GameBattle.cpp:81
    #17 0x10709cc74 in ScriptGameBattle::OnUpdate(float) ScriptGameBattle.cpp:31
    #18 0x1055d54ab in GameInterface::Update(float) GameInterface.cpp:290
    #19 0x105c05459 in SceneBase::DoUpdate(float) SceneBase.cpp:147
    #20 0x105d24095 in SceneManager::Update(float) SceneManager.cpp:167
    #21 0x1054fa880 in GameInit::DoUpdate(float) GameInit.cpp:76
    #22 0x1090af31e in GameInitGame::DoUpdate(float) GameInitGame.cpp:412
    #23 0x1053ee97d in GameContral::update(float) GameContral.cpp:236
    #24 0x1054e5eb4 in void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float)::operator()(float) const CCScheduler.h:283
    #25 0x1054e5dac in decltype(static_cast<GameContral>(fp)(static_cast<float>(fp0))) std::__1::__invoke<void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float)&, float>(GameContral&&, float&&) type_traits:3918
    #26 0x1054e5c1c in void std::__1::__invoke_void_return_wrapper<void, true>::__call<void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float)&, float>(void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float)&, float&&) invoke.h:61
    #27 0x1054e5b0c in std::__1::__function::__alloc_func<void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float), std::__1::allocator<void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float)>, void (float)>::operator()(float&&) function.h:178
    #28 0x1054dfa88 in std::__1::__function::__func<void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float), std::__1::allocator<void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float)>, void (float)>::operator()(float&&) function.h:352
    #29 0x105f439ab in std::__1::__function::__value_func<void (float)>::operator()(float&&) const function.h:505

previously allocated by thread T0 here:
    #0 0x1472a520d in wrap__Znwm+0x7d (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x5c20d)
    #1 0x117b5d414 in void* std::__1::__libcpp_operator_new<unsigned long>(unsigned long) new:235
    #2 0x117b5d24c in std::__1::__libcpp_allocate(unsigned long, unsigned long) new:261
    #3 0x117e9dd78 in std::__1::allocator<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> > >::allocate(unsigned long) allocator.h:108
    #4 0x117e9d89c in std::__1::allocator_traits<std::__1::allocator<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> > > >::allocate(std::__1::allocator<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> > >&, unsigned long) allocator_traits.h:262
    #5 0x117e9d522 in std::__1::__split_buffer<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> >, std::__1::allocator<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> > >&>::__split_buffer(unsigned long, unsigned long, std::__1::allocator<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> > >&) __split_buffer:315
    #6 0x117e9c9a0 in std::__1::__split_buffer<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> >, std::__1::allocator<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> > >&>::__split_buffer(unsigned long, unsigned long, std::__1::allocator<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> > >&) __split_buffer:314
    #7 0x117e9be81 in std::__1::vector<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> >, std::__1::allocator<std::__1::pair<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> > > >::reserve(unsigned long) vector:1609
    #8 0x117e9bc16 in improbable::phtree::detail::sparse_map<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> >::sparse_map() flat_sparse_map.h:46
    #9 0x117e9bb88 in improbable::phtree::detail::sparse_map<unsigned int, improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long> >::sparse_map() flat_sparse_map.h:45
    #10 0x117e9bb38 in improbable::phtree::v16::Node<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long>::Node() node.h:85
    #11 0x117e9ba28 in improbable::phtree::v16::Node<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long>::Node() node.h:85
    #12 0x117f0a902 in auto& improbable::phtree::v16::Node<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long>::InsertSplit<>(improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long>&, std::__1::array<long long, 4ul> const&, unsigned int) node.h:387
    #13 0x117efaf3f in auto& improbable::phtree::v16::Node<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long>::HandleCollision<>(improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long>&, bool&, std::__1::array<long long, 4ul> const&, unsigned int) node.h:373
    #14 0x117eef1de in improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long>& improbable::phtree::v16::Node<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long>::Emplace<>(bool&, std::__1::array<long long, 4ul> const&, unsigned int) node.h:134
    #15 0x117eed08a in unsigned long improbable::phtree::v16::PhTreeV16<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, improbable::phtree::DM_TREE_CONVERTER<2ul, fpm::fixed<int, long long, 8u, true>, long long, improbable::phtree::DMScalarConverter> >::_relocate_mm<unsigned long improbable::phtree::PhTreeMultiMap<2ul, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, improbable::phtree::DM_TREE_CONVERTER<2ul, fpm::fixed<int, long long, 8u, true>, long long, improbable::phtree::DMScalarConverter>, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, false, improbable::phtree::QueryIntersect>::relocate<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&>(improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&, bool)::'lambda'(phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >&, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >&)&, unsigned long improbable::phtree::PhTreeMultiMap<2ul, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, improbable::phtree::DM_TREE_CONVERTER<2ul, fpm::fixed<int, long long, 8u, true>, long long, improbable::phtree::DMScalarConverter>, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, false, improbable::phtree::QueryIntersect>::relocate<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&>(improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&, bool)::'lambda'(phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >&)&>(std::__1::array<long long, 4ul> const&, std::__1::array<long long, 4ul> const&, bool, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&, unsigned long improbable::phtree::PhTreeMultiMap<2ul, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, improbable::phtree::DM_TREE_CONVERTER<2ul, fpm::fixed<int, long long, 8u, true>, long long, improbable::phtree::DMScalarConverter>, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, false, improbable::phtree::QueryIntersect>::relocate<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&>(improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&, bool)::'lambda'(phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >&)&) phtree_v16.h:438
    #16 0x117ac1e16 in unsigned long improbable::phtree::PhTreeMultiMap<2ul, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, improbable::phtree::DM_TREE_CONVERTER<2ul, fpm::fixed<int, long long, 8u, true>, long long, improbable::phtree::DMScalarConverter>, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, false, improbable::phtree::QueryIntersect>::relocate<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&>(improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, improbable::phtree::PhBox<2ul, fpm::fixed<int, long long, 8u, true> > const&, entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >&, bool) phtree_multimap.h:455
    #17 0x117abf16a in updateUnitsPosition(entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> >&, GameWorld*) GameWorld.cpp:231
    #18 0x117af74c9 in GameWorld::updateWorld() GameWorld.cpp:1307
    #19 0x1067b4a0f in GameBattle::OnUpdate(float) GameBattle.cpp:81
    #20 0x10709cc74 in ScriptGameBattle::OnUpdate(float) ScriptGameBattle.cpp:31
    #21 0x1055d54ab in GameInterface::Update(float) GameInterface.cpp:290
    #22 0x105c05459 in SceneBase::DoUpdate(float) SceneBase.cpp:147
    #23 0x105d24095 in SceneManager::Update(float) SceneManager.cpp:167
    #24 0x1054fa880 in GameInit::DoUpdate(float) GameInit.cpp:76
    #25 0x1090af31e in GameInitGame::DoUpdate(float) GameInitGame.cpp:412
    #26 0x1053ee97d in GameContral::update(float) GameContral.cpp:236
    #27 0x1054e5eb4 in void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float)::operator()(float) const CCScheduler.h:283
    #28 0x1054e5dac in decltype(static_cast<GameContral>(fp)(static_cast<float>(fp0))) std::__1::__invoke<void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float)&, float>(GameContral&&, float&&) type_traits:3918
    #29 0x1054e5c1c in void std::__1::__invoke_void_return_wrapper<void, true>::__call<void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float)&, float>(void cocos2d::Scheduler::scheduleUpdate<GameContral>(GameContral*, int, bool)::'lambda'(float)&, float&&) invoke.h:61

SUMMARY: AddressSanitizer: heap-use-after-free entry.h:158 in improbable::phtree::v16::Entry<4ul, phtree::bptree::b_plus_tree_hash_set<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > >, std::__1::hash<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > >, std::__1::equal_to<entt::basic_handle<entt::basic_registry<entt::entity, std::__1::allocator<entt::entity> > > > >, long long>::IsNode() const
Shadow bytes around the buggy address:
  0x1c2400201440: fd fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa
  0x1c2400201450: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x1c2400201460: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c2400201470: fd fd fd fd fd fd fd fd fd fd fd fd fa fa fa fa
  0x1c2400201480: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
=>0x1c2400201490:[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c24002014a0: fd fd fd fd fd fd fd fd fd fd fd fd fa fa fa fa
  0x1c24002014b0: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x1c24002014c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1c24002014d0: 00 00 00 00 00 00 00 00 00 00 00 00 fa fa fa fa
  0x1c24002014e0: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
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
==63389==ABORTING
AddressSanitizer report breakpoint hit. Use 'thread info -s' to get extended information about the report.
(lldb) 
rockingdice commented 1 year ago

I'm using apple-clang on mac os. c++17

rockingdice commented 1 year ago

here's a screenshot of the crash moment:

image
rockingdice commented 1 year ago

possible related threads:

https://stackoverflow.com/questions/66994805/asan-heap-use-after-free-after-vector-emplacepush-back-in-a-recursive-functio

rockingdice commented 1 year ago

This is the moment that freed the entry:

image

which will trigger the heap-use-after-free problem.

rockingdice commented 1 year ago

It's very likely the same problem as I mentioned:

possible related threads:

https://stackoverflow.com/questions/66994805/asan-heap-use-after-free-after-vector-emplacepush-back-in-a-recursive-functio

I've tried to modify this line:

        data_.reserve(4);

to

        data_.reserve(400);

And it won't lead to the issue anymore, so reallocating the vector is probably the problem. The code from:

class sparse_map {
    using Entry = std::pair<KeyT, ValueT>;
    using iterator = typename std::vector<Entry>::iterator;

  public:
    explicit sparse_map() : data_{} {
        data_.reserve(4);        <<<<<<<<<<<< this line
    }
tzaeschke commented 1 year ago

Hi, thanks for reporting! Do you happen to have a small(ish) testcase that reproduces the problem?

rockingdice commented 1 year ago

As I mentioned, the project is too big, and I may not have enough time to make one... but what I did is trivial:

  1. I put four separate entities in the game, and they are moving towards the same goal so that they will get together eventually.

  2. Keep relocating their positions in the multimap ph-tree in the update function for each frame:

    image
  3. When they are near enough, the problem will arise. You can modify the data_.reserve(4); to data_.reserve(1); It will happen as soon as two entities are in the same entry, which will definitely trigger the problem in my case.

To test the asan problem, you need to turn on this option in the Xcode:

image
tzaeschke commented 1 year ago

I haven't managed to reproduce it. Could you do (some) of the following?

tzaeschke commented 1 year ago

Btw, running the unit tests with ASAN can be done with bazel test ... --config=asan.

tzaeschke commented 1 year ago

Could you try running the following test with ASAN and see weather it fails?

#include "phtree/phtree.h"
#include "phtree/phtree_multimap.h"
#include <include/gtest/gtest.h>
#include <random>
#include <vector>

using namespace improbable::phtree;

namespace phtree_test_issue_153 {

// Number of entries that have the same coordinate
static const size_t NUM_DUPL = 1;
static const double WORLD_MIN = -100;
static const double WORLD_MAX = 100;
static const double BOX_LEN = 1;

template <dimension_t DIM>
using TestKey = PhBoxD<DIM>;

class DoubleRng {
  public:
    DoubleRng(double minIncl, double maxExcl) : eng(), rnd{minIncl, maxExcl} {}

    double next() {
        return rnd(eng);
    }

  private:
    std::default_random_engine eng;
    std::uniform_real_distribution<double> rnd;
};

template <dimension_t DIM>
void generateCube(std::vector<TestKey<DIM>>& points, size_t N) {
    assert(N % NUM_DUPL == 0);
    DoubleRng rng(WORLD_MIN, WORLD_MAX);
    auto reference_set = std::unordered_map<TestKey<DIM>, size_t>();

    points.reserve(N);
    for (size_t i = 0; i < N / NUM_DUPL; i++) {
        // create duplicates, i.e. entries with the same coordinates. However, avoid unintentional
        // duplicates.
        TestKey<DIM> key{};
        for (dimension_t d = 0; d < DIM; ++d) {
            key.min()[d] = rng.next();
            key.max()[d] = key.min()[d] + BOX_LEN;
        }
        if (reference_set.count(key) != 0) {
            i--;
            continue;
        }
        reference_set.emplace(key, i);
        for (size_t dupl = 0; dupl < NUM_DUPL; dupl++) {
            auto point = TestKey<DIM>(key);
            points.push_back(point);
        }
    }
    ASSERT_EQ(reference_set.size(), N / NUM_DUPL);
    ASSERT_EQ(points.size(), N);
}

TEST(PhTreeTestIssue153, TestIssue153) {
    /*
     * This used to cause a heap-use-after-free problem in sparse_map
     */
    auto points = std::vector<PhBoxD<2>>();
    points.emplace_back(PhBoxD<2>{{1, 1}, {1, 1}});
    points.emplace_back(PhBoxD<2>{{3, 3}, {3, 3}});
    points.emplace_back(PhBoxD<2>{{1, 3}, {1, 3}});
    points.emplace_back(PhBoxD<2>{{3, 1}, {3, 1}});
    points.emplace_back(PhBoxD<2>{{0, 0}, {0, 0}});

    // The real test is ABOVE, see "#define CALLBACK"
    auto tree = PhTreeMultiMapBoxD<2, size_t>();
    for (size_t i = 0; i < points.size(); ++i) {
        tree.emplace(points[i], i);
    }

    PhBoxD<2> b{{2, 2}, {2, 2}};
    for (size_t r = 0; r < 100; ++r) {
        for (size_t i = 0; i < points.size(); ++i) {
            PhBoxD<2>& bOld = points[i];
            double m0 = (bOld.min()[0] + b.min()[0]) / 2;
            double m1 = (bOld.min()[1] + b.min()[1]) / 2;
            double m2 = (bOld.max()[0] + b.max()[0]) / 2;
            double m3 = (bOld.max()[1] + b.max()[1]) / 2;
            PhBoxD<2> bNew{{m0, m1}, {m2, m3}};
            ASSERT_EQ(1, tree.relocate(bOld, bNew, i));
            points[i] = bNew;
        }
    }
}

/*
 * Try move in ever smaller steps.
 */
TEST(PhTreeTestIssue153, TestIssue153_2) {
    int N = 10;
    const dimension_t DIM = 2;
    std::vector<TestKey<DIM>> points;
    generateCube(points, N);

    auto tree = PhTreeMultiMapBoxD<DIM, size_t>();
    for (size_t i = 0; i < points.size(); ++i) {
        tree.emplace(points[i], i);
    }

    TestKey<DIM> x{};
    for (dimension_t d = 0; d < DIM; ++d) {
        x.min()[d] = 2.;
        x.max()[d] = 2.;
    }

    for (size_t r = 0; r < 100; ++r) {
        for (size_t i = 0; i < points.size(); ++i) {
            TestKey<DIM>& bOld = points[i];
            TestKey<DIM> bNew{};
            for (dimension_t d = 0; d < DIM; ++d) {
                double m0 = (bOld.min()[d] + x.min()[d]) / 2;
                double m1 = (bOld.max()[d] + x.max()[d]) / 2;
                bNew.min()[d] = m0;
                bNew.max()[d] = m1;
            }
            ASSERT_EQ(1, tree.relocate(bOld, bNew, i));
            points[i] = bNew;
        }
    }
}

/*
 * Try moving in very small (but const-sized) steps.
 */
TEST(PhTreeTestIssue153, TestIssue153_Linear) {
    int N = 10;
    const int R_MAX = 1000000;
    const dimension_t DIM = 2;
    std::vector<TestKey<DIM>> points;
    generateCube(points, N);

    TestKey<DIM> x{};
    for (dimension_t d = 0; d < DIM; ++d) {
        x.min()[d] = 2.;
        x.max()[d] = 2. + BOX_LEN;
    }

    std::vector<PhPointD<DIM>> moves;

    auto tree = PhTreeMultiMapBoxD<DIM, size_t>();
    for (size_t i = 0; i < points.size(); ++i) {
        tree.emplace(points[i], i);
        // distances.emplace_back(distance(points[i], x));
        PhPointD<DIM>& m = moves.emplace_back();
        for (dimension_t d = 0; d < DIM; ++d) {
            m[d] = (x.min()[d] - points[i].min()[d]) / R_MAX;
        }
    }

    for (size_t r = 0; r < R_MAX; ++r) {
        for (size_t i = 0; i < points.size(); ++i) {
            TestKey<DIM>& bOld = points[i];
            if (i == 0)
                std::cout << "r=" << r << " i=" << i << " " << bOld << std::endl;
            TestKey<DIM> bNew{};
            PhPointD<DIM> mov = moves[i];
            for (dimension_t d = 0; d < DIM; ++d) {
                bNew.min()[d] = bOld.min()[d] + mov[d];
                bNew.max()[d] = bOld.max()[d] + mov[d];
            }
            ASSERT_EQ(1, tree.relocate(bOld, bNew, i));
            points[i] = bNew;
        }
    }
}

}  // namespace phtree_test_issue_153
rockingdice commented 1 year ago

Sorry for the late reply. I've been swamped these days... I may try them when I'm free, but no promise on the time, sadly :(

tzaeschke commented 1 year ago

@rockingdice No worries :-)