Skip to content

Don't detach the auto-GC thread#15614

Open
edolstra wants to merge 1 commit into
masterfrom
join-auto-gc-thread
Open

Don't detach the auto-GC thread#15614
edolstra wants to merge 1 commit into
masterfrom
join-auto-gc-thread

Conversation

@edolstra
Copy link
Copy Markdown
Member

@edolstra edolstra commented Apr 1, 2026

Motivation

It could still be running after this is destroyed. So we need to join it in the LocalStore destructor.

Context


Add 👍 to pull requests you find important.

The Nix maintainer team uses a GitHub project board to schedule and track reviews.

It could still be running after `this` is destroyed. So we need to
join it in the LocalStore destructor.
@edolstra edolstra requested a review from Ericson2314 as a code owner April 1, 2026 20:03
@github-actions github-actions Bot added the store Issues and pull requests concerning the Nix store label Apr 1, 2026
@xokdvium
Copy link
Copy Markdown
Contributor

xokdvium commented Apr 1, 2026

cc @dramforever

Comment on lines +435 to +439
{
auto state(_state->lock());
if (state->gcThread.joinable())
state->gcThread.join();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can't throw, right?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also try to interrupt the GC thread here? Not sure how though.

@xokdvium
Copy link
Copy Markdown
Contributor

xokdvium commented Apr 2, 2026

@dramforever says it doesn't help with the use-after-free issue of static globals. Unclear what's going on there. We don't have any global static store instances AFAIK.

@dramforever
Copy link
Copy Markdown
Contributor

To be clear this is the crash I still got (the second trace being the crashing thread):

Details
Thread 2 (LWP 27962):
#0  0x00007f27934a7f18 in unlink_chunk.isra () from /nix/store/vr7ds8vwbl2fz7pr221d5y0f8n9a5wda-glibc-2.40-218/lib/libc.so.6
#1  0x00007f27934a8117 in malloc_consolidate () from /nix/store/vr7ds8vwbl2fz7pr221d5y0f8n9a5wda-glibc-2.40-218/lib/libc.so.6
#2  0x00007f27934a9460 in _int_free_maybe_consolidate.part.0 () from /nix/store/vr7ds8vwbl2fz7pr221d5y0f8n9a5wda-glibc-2.40-218/lib/libc.so.6
#3  0x00007f27934a9b12 in _int_free () from /nix/store/vr7ds8vwbl2fz7pr221d5y0f8n9a5wda-glibc-2.40-218/lib/libc.so.6
#4  0x00007f27934ac3ee in free () from /nix/store/vr7ds8vwbl2fz7pr221d5y0f8n9a5wda-glibc-2.40-218/lib/libc.so.6
#5  0x00005555d1d86d08 in std::__new_allocator<std::__detail::_State<char> >::deallocate (this=<optimized out>, __p=<optimized out>, __n=<optimized out>) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/new_allocator.h:172
#6  std::allocator<std::__detail::_State<char> >::deallocate (this=<optimized out>, __p=<optimized out>, __n=<optimized out>) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/allocator.h:208
#7  std::allocator_traits<std::allocator<std::__detail::_State<char> > >::deallocate (__a=..., __p=<optimized out>, __n=<optimized out>) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/alloc_traits.h:550
#8  std::_Vector_base<std::__detail::_State<char>, std::allocator<std::__detail::_State<char> > >::_M_deallocate (this=0x5555de945a58, __p=<optimized out>, __n=<optimized out>) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/stl_vector.h:392
#9  std::_Vector_base<std::__detail::_State<char>, std::allocator<std::__detail::_State<char> > >::_M_deallocate (this=0x5555de945a58, __p=<optimized out>, __n=<optimized out>) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/stl_vector.h:388
#10 std::_Vector_base<std::__detail::_State<char>, std::allocator<std::__detail::_State<char> > >::~_Vector_base (this=0x5555de945a58, __in_chrg=<optimized out>) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/stl_vector.h:371
#11 std::vector<std::__detail::_State<char>, std::allocator<std::__detail::_State<char> > >::~vector (this=0x5555de945a58, __in_chrg=<optimized out>) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/stl_vector.h:751
#12 std::__detail::_NFA<std::__cxx11::regex_traits<char> >::~_NFA (this=0x5555de945a20, __in_chrg=<optimized out>) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/regex_automaton.h:220
#13 std::destroy_at<std::__detail::_NFA<std::__cxx11::regex_traits<char> > > (__location=0x5555de945a20) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/stl_construct.h:88
#14 std::_Destroy<std::__detail::_NFA<std::__cxx11::regex_traits<char> > > (__pointer=0x5555de945a20) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/stl_construct.h:149
#15 std::allocator_traits<std::allocator<void> >::destroy<std::__detail::_NFA<std::__cxx11::regex_traits<char> > > (__p=0x5555de945a20) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/alloc_traits.h:720
#16 std::_Sp_counted_ptr_inplace<std::__detail::_NFA<std::__cxx11::regex_traits<char> >, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>::_M_dispose (this=0x5555de945a10) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/shared_ptr_base.h:616
#17 0x00007f27949783ea in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release (this=0x5555de945a10) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/shared_ptr_base.h:346
#18 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release (this=0x5555de945a10) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/shared_ptr_base.h:317
#19 0x00007f27949c101a in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count (this=0x7f27949fef58 <nix::parseFlakeIdRef(nix::fetchers::Settings const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool)::flakeRegex+24>, __in_chrg=<optimized out>) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/shared_ptr_base.h:1069
#20 std::__shared_ptr<std::__detail::_NFA<std::__cxx11::regex_traits<char> > const, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr (this=0x7f27949fef50 <nix::parseFlakeIdRef(nix::fetchers::Settings const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool)::flakeRegex+16>, __in_chrg=<optimized out>) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/shared_ptr_base.h:1525
#21 std::shared_ptr<std::__detail::_NFA<std::__cxx11::regex_traits<char> > const>::~shared_ptr (this=0x7f27949fef50 <nix::parseFlakeIdRef(nix::fetchers::Settings const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool)::flakeRegex+16>, __in_chrg=<optimized out>) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/shared_ptr.h:175
#22 std::__cxx11::basic_regex<char, std::__cxx11::regex_traits<char> >::~basic_regex (this=0x7f27949fef40 <nix::parseFlakeIdRef(nix::fetchers::Settings const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool)::flakeRegex>, __in_chrg=<optimized out>) at /nix/store/yps9cjmi8g58mh0f7pw902br7gxwg2dz-gcc-14.3.0/include/c++/14.3.0/bits/regex.h:557
#23 0x00007f2793444271 in __run_exit_handlers () from /nix/store/vr7ds8vwbl2fz7pr221d5y0f8n9a5wda-glibc-2.40-218/lib/libc.so.6
#24 0x00007f279344433e in exit () from /nix/store/vr7ds8vwbl2fz7pr221d5y0f8n9a5wda-glibc-2.40-218/lib/libc.so.6
#25 0x00007f279342a4df in __libc_start_call_main () from /nix/store/vr7ds8vwbl2fz7pr221d5y0f8n9a5wda-glibc-2.40-218/lib/libc.so.6
#26 0x00007f279342a59b in __libc_start_main_impl () from /nix/store/vr7ds8vwbl2fz7pr221d5y0f8n9a5wda-glibc-2.40-218/lib/libc.so.6
#27 0x00005555d1cfebe5 in _start ()

Thread 1 (LWP 27978):
#0  0x00007f2794658aab in boost::re_detail_500::perl_matcher<char const*, std::allocator<boost::sub_match<char const*> >, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::match_all_states (this=this@entry=0x7f2771b24bd0) at /nix/store/zw6w6chqvqsh2c6jfsb4pw9bpgdkmjnh-boost-1.87.0-dev/include/boost/regex/v5/perl_matcher_non_recursive.hpp:199
#1  0x00007f2794673eea in boost::re_detail_500::perl_matcher<char const*, std::allocator<boost::sub_match<char const*> >, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::match_prefix (this=0x7f2771b24bd0) at /nix/store/zw6w6chqvqsh2c6jfsb4pw9bpgdkmjnh-boost-1.87.0-dev/include/boost/regex/v5/perl_matcher_common.hpp:306
#2  boost::re_detail_500::perl_matcher<char const*, std::allocator<boost::sub_match<char const*> >, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::match_imp (this=this@entry=0x7f2771b24bd0) at /nix/store/zw6w6chqvqsh2c6jfsb4pw9bpgdkmjnh-boost-1.87.0-dev/include/boost/regex/v5/perl_matcher_common.hpp:196
#3  0x00007f279464c7f9 in boost::re_detail_500::perl_matcher<char const*, std::allocator<boost::sub_match<char const*> >, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::match (this=0x7f2771b24bd0) at /nix/store/zw6w6chqvqsh2c6jfsb4pw9bpgdkmjnh-boost-1.87.0-dev/include/boost/regex/v5/perl_matcher_common.hpp:172
#4  boost::regex_match<char const*, std::allocator<boost::sub_match<char const*> >, char, boost::regex_traits<char, boost::cpp_regex_traits<char> > > (first=0x7f23640025a3 "42", last=<optimized out>, m=..., e=..., flags=boost::regex_constants::match_any) at /nix/store/zw6w6chqvqsh2c6jfsb4pw9bpgdkmjnh-boost-1.87.0-dev/include/boost/regex/v5/regex_match.hpp:39
#5  boost::regex_match<char, boost::regex_traits<char, boost::cpp_regex_traits<char> > > (str=0x7f23640025a3 "42", e=..., flags=boost::regex_constants::match_default) at /nix/store/zw6w6chqvqsh2c6jfsb4pw9bpgdkmjnh-boost-1.87.0-dev/include/boost/regex/v5/regex_match.hpp:75
#6  nix::findRuntimeRootsUnchecked[abi:cxx11](nix::StoreDirConfig const&) (config=...) at ../src/libstore/local-gc.cc:74
#7  0x00007f27945ca722 in nix::LocalStore::findRuntimeRoots (this=0x5555de838870, roots=..., censor=true) at ../src/libstore/gc.cc:334
#8  0x00007f27945cc4f5 in nix::LocalStore::collectGarbage (this=0x5555de838870, options=..., results=...) at ../src/libstore/gc.cc:519
#9  0x00007f27945c6aff in operator() (__closure=0x5555deb8a028) at ../src/libstore/gc.cc:907
#10 0x00007f27938ed064 in execute_native_thread_routine () from /nix/store/0p8b2lqk47fvxm9hc6c8mnln5l8x51q1-gcc-14.3.0-lib/lib/libstdc++.so.6
#11 0x00007f279349a97a in start_thread () from /nix/store/vr7ds8vwbl2fz7pr221d5y0f8n9a5wda-glibc-2.40-218/lib/libc.so.6
#12 0x00007f2793522d2c in __clone3 () from /nix/store/vr7ds8vwbl2fz7pr221d5y0f8n9a5wda-glibc-2.40-218/lib/libc.so.6

And the crash is here in the boost::regex_match accessing a now-gone digitsRegex:

static const auto digitsRegex = boost::regex(R"(^\d+$)");
static const auto mapRegex = boost::regex(R"(^\s*\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(/\S+)\s*$)");
auto storePathRegex = boost::regex(quoteRegexChars(config.storeDir) + R"(/[0-9a-z]+[0-9a-zA-Z\+\-\._\?=]*)");
while (errno = 0, ent = readdir(procDir.get())) {
checkInterrupt();
if (boost::regex_match(ent->d_name, digitsRegex)) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

store Issues and pull requests concerning the Nix store

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants