Skip to content

[lldb] Fix stale Symbol pointer crash in statusline after 'target symbols add'#188377

Merged
GeorgeHuyubo merged 2 commits into
llvm:mainfrom
GeorgeHuyubo:main
Apr 15, 2026
Merged

[lldb] Fix stale Symbol pointer crash in statusline after 'target symbols add'#188377
GeorgeHuyubo merged 2 commits into
llvm:mainfrom
GeorgeHuyubo:main

Conversation

@GeorgeHuyubo

Copy link
Copy Markdown
Contributor

Context:
lldb might crash when running to a debuggee crashing state and do a target symbols add command.
Backtrace:

 #0 0x000055ca6790dc65 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /home/hyubo/osmeta/external/llvm-project/llvm/lib/Support/Unix/Signals.inc:848:11
 #1 0x000055ca6790e434 PrintStackTraceSignalHandler(void*) /home/hyubo/osmeta/external/llvm-project/llvm/lib/Support/Unix/Signals.inc:931:1
 #2 0x000055ca6790b839 llvm::sys::RunSignalHandlers() /home/hyubo/osmeta/external/llvm-project/llvm/lib/Support/Signals.cpp:104:5
 #3 0x000055ca6790ff6b SignalHandler(int, siginfo_t*, void*) /home/hyubo/osmeta/external/llvm-project/llvm/lib/Support/Unix/Signals.inc:430:38
 #4 0x00007fe9e5e44560 __restore_rt /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/signal/../sysdeps/unix/sysv/linux/libc_sigaction.c:13:0
 #5 0x00007fe9e5f25649 syscall /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/misc/../sysdeps/unix/sysv/linux/x86_64/syscall.S:38:0
 #6 0x00007fe9ec649170 SignalHandler(int, siginfo_t*, void*) /home/hyubo/osmeta/external/llvm-project/llvm/lib/Support/Unix/Signals.inc:429:7
 #7 0x00007fe9e5e44560 __restore_rt /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/signal/../sysdeps/unix/sysv/linux/libc_sigaction.c:13:0
 #8 0x00007fe9ebb77bf0 lldb_private::operator<(lldb_private::StackID const&, lldb_private::StackID const&) /home/hyubo/osmeta/external/llvm-project/lldb/source/Target/StackID.cpp:99:16
 #9 0x00007fe9ebb6863d CompareStackID(std::shared_ptr<lldb_private::StackFrame> const&, lldb_private::StackID const&) /home/hyubo/osmeta/external/llvm-project/lldb/source/Target/StackFrameList.cpp:683:3
#10 0x00007fe9ebb6d049 bool __gnu_cxx::__ops::_Iter_comp_val<bool (*)(std::shared_ptr<lldb_private::StackFrame> const&, lldb_private::StackID const&)>::operator()<__gnu_cxx::__normal_iterator<std::shared_ptr<lldb_private::StackFrame>*, std::vector<std::shared_ptr<lldb_private::StackFrame>, std::allocator<std::shared_ptr<lldb_private::StackFrame>>>>, lldb_private::StackID const>(__gnu_cxx::__normal_iterator<std::shared_ptr<lldb_private::StackFrame>*, std::vector<std::shared_ptr<lldb_private::StackFrame>, std::allocator<std::shared_ptr<lldb_private::StackFrame>>>>, lldb_private::StackID const&) /mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/bits/predefined_ops.h:196:4
#11 0x00007fe9ebb6cefe __gnu_cxx::__normal_iterator<std::shared_ptr<lldb_private::StackFrame>*, std::vector<std::shared_ptr<lldb_private::StackFrame>, std::allocator<std::shared_ptr<lldb_private::StackFrame>>>> std::__lower_bound<__gnu_cxx::__normal_iterator<std::shared_ptr<lldb_private::StackFrame>*, std::vector<std::shared_ptr<lldb_private::StackFrame>, std::allocator<std::shared_ptr<lldb_private::StackFrame>>>>, lldb_private::StackID, __gnu_cxx::__ops::_Iter_comp_val<bool (*)(std::shared_ptr<lldb_private::StackFrame> const&, lldb_private::StackID const&)>>(__gnu_cxx::__normal_iterator<std::shared_ptr<lldb_private::StackFrame>*, std::vector<std::shared_ptr<lldb_private::StackFrame>, std::allocator<std::shared_ptr<lldb_private::StackFrame>>>>, __gnu_cxx::__normal_iterator<std::shared_ptr<lldb_private::StackFrame>*, std::vector<std::shared_ptr<lldb_private::StackFrame>, std::allocator<std::shared_ptr<lldb_private::StackFrame>>>>, lldb_private::StackID const&, __gnu_cxx::__ops::_Iter_comp_val<bool (*)(std::shared_ptr<lldb_private::StackFrame> const&, lldb_private::StackID const&)>) /mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/bits/stl_algobase.h:1464:8
#12 0x00007fe9ebb6cdfc __gnu_cxx::__normal_iterator<std::shared_ptr<lldb_private::StackFrame>*, std::vector<std::shared_ptr<lldb_private::StackFrame>, std::allocator<std::shared_ptr<lldb_private::StackFrame>>>> std::lower_bound<__gnu_cxx::__normal_iterator<std::shared_ptr<lldb_private::StackFrame>*, std::vector<std::shared_ptr<lldb_private::StackFrame>, std::allocator<std::shared_ptr<lldb_private::StackFrame>>>>, lldb_private::StackID, bool (*)(std::shared_ptr<lldb_private::StackFrame> const&, lldb_private::StackID const&)>(__gnu_cxx::__normal_iterator<std::shared_ptr<lldb_private::StackFrame>*, std::vector<std::shared_ptr<lldb_private::StackFrame>, std::allocator<std::shared_ptr<lldb_private::StackFrame>>>>, __gnu_cxx::__normal_iterator<std::shared_ptr<lldb_private::StackFrame>*, std::vector<std::shared_ptr<lldb_private::StackFrame>, std::allocator<std::shared_ptr<lldb_private::StackFrame>>>>, lldb_private::StackID const&, bool (*)(std::shared_ptr<lldb_private::StackFrame> const&, lldb_private::StackID const&)) /mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/bits/stl_algo.h:2062:14
#13 0x00007fe9ebb685fa auto llvm::lower_bound<std::vector<std::shared_ptr<lldb_private::StackFrame>, std::allocator<std::shared_ptr<lldb_private::StackFrame>>>&, lldb_private::StackID const&, bool (*)(std::shared_ptr<lldb_private::StackFrame> const&, lldb_private::StackID const&)>(std::vector<std::shared_ptr<lldb_private::StackFrame>, std::allocator<std::shared_ptr<lldb_private::StackFrame>>>&, lldb_private::StackID const&, bool (*)(std::shared_ptr<lldb_private::StackFrame> const&, lldb_private::StackID const&)) /home/hyubo/osmeta/external/llvm-project/llvm/include/llvm/ADT/STLExtras.h:2001:10
#14 0x00007fe9ebb68441 lldb_private::StackFrameList::GetFrameWithStackID(lldb_private::StackID const&) /home/hyubo/osmeta/external/llvm-project/lldb/source/Target/StackFrameList.cpp:697:11
#15 0x00007fe9ebbee395 lldb_private::Thread::GetFrameWithStackID(lldb_private::StackID const&) /home/hyubo/osmeta/external/llvm-project/lldb/include/lldb/Target/Thread.h:459:7
#16 0x00007fe9ebac7cf7 lldb_private::ExecutionContextRef::GetFrameSP() const /home/hyubo/osmeta/external/llvm-project/lldb/source/Target/ExecutionContext.cpp:643:25
#17 0x00007fe9ebac80e1 lldb_private::GetStoppedExecutionContext(lldb_private::ExecutionContextRef const*) /home/hyubo/osmeta/external/llvm-project/lldb/source/Target/ExecutionContext.cpp:164:34
#18 0x00007fe9eb8903fa lldb_private::Statusline::Redraw(std::optional<lldb_private::ExecutionContextRef>) /home/hyubo/osmeta/external/llvm-project/lldb/source/Core/Statusline.cpp:139:7
#19 0x00007fe9eb7ac8be lldb_private::Debugger::RedrawStatusline(std::optional<lldb_private::ExecutionContextRef>) /home/hyubo/osmeta/external/llvm-project/lldb/source/Core/Debugger.cpp:1233:3
#20 0x00007fe9eb804d1e lldb_private::IOHandlerEditline::RedrawCallback() /home/hyubo/osmeta/external/llvm-project/lldb/source/Core/IOHandler.cpp:446:3
#21 0x00007fe9eb80aa81 lldb_private::IOHandlerEditline::IOHandlerEditline(lldb_private::Debugger&, lldb_private::IOHandler::Type, std::shared_ptr<lldb_private::File> const&, std::shared_ptr<lldb_private::LockableStreamFile> const&, std::shared_ptr<lldb_private::LockableStreamFile> const&, unsigned int, char const*, llvm::StringRef, llvm::StringRef, bool, bool, unsigned int, lldb_private::IOHandlerDelegate&)::$_2::operator()() const /home/hyubo/osmeta/external/llvm-project/lldb/source/Core/IOHandler.cpp:262:73
#22 0x00007fe9eb80aa5d void llvm::detail::UniqueFunctionBase<void>::CallImpl<lldb_private::IOHandlerEditline::IOHandlerEditline(lldb_private::Debugger&, lldb_private::IOHandler::Type, std::shared_ptr<lldb_private::File> const&, std::shared_ptr<lldb_private::LockableStreamFile> const&, std::shared_ptr<lldb_private::LockableStreamFile> const&, unsigned int, char const*, llvm::StringRef, llvm::StringRef, bool, bool, unsigned int, lldb_private::IOHandlerDelegate&)::$_2>(void*) /home/hyubo/osmeta/external/llvm-project/llvm/include/llvm/ADT/FunctionExtras.h:213:5
#23 0x00007fe9eb93bfbf llvm::unique_function<void ()>::operator()() /home/hyubo/osmeta/external/llvm-project/llvm/include/llvm/ADT/FunctionExtras.h:365:5
#24 0x00007fe9eb93bb80 lldb_private::Editline::GetCharacter(wchar_t*) /home/hyubo/osmeta/external/llvm-project/lldb/source/Host/common/Editline.cpp:0:5
#25 0x00007fe9eb941a18 lldb_private::Editline::ConfigureEditor(bool)::$_0::operator()(editline*, wchar_t*) const /home/hyubo/osmeta/external/llvm-project/lldb/source/Host/common/Editline.cpp:1287:5
#26 0x00007fe9eb9419e2 lldb_private::Editline::ConfigureEditor(bool)::$_0::__invoke(editline*, wchar_t*) /home/hyubo/osmeta/external/llvm-project/lldb/source/Host/common/Editline.cpp:1286:27
#27 0x00007fe9f3384e26 el_getc /home/engshare/third-party2/libedit/3.1/src/libedit/src/read.c:439:14
#28 0x00007fe9f3384e26 el_getc /home/engshare/third-party2/libedit/3.1/src/libedit/src/read.c:400:1
#29 0x00007fe9f3384f90 read_getcmd /home/engshare/third-party2/libedit/3.1/src/libedit/src/read.c:247:14
#30 0x00007fe9f3384f90 el_gets /home/engshare/third-party2/libedit/3.1/src/libedit/src/read.c:586:14
#31 0x00007fe9eb9409f3 lldb_private::Editline::GetLine(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, bool&) /home/hyubo/osmeta/external/llvm-project/lldb/source/Host/common/Editline.cpp:1636:16
#32 0x00007fe9eb8044d7 lldb_private::IOHandlerEditline::GetLine(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, bool&) /home/hyubo/osmeta/external/llvm-project/lldb/source/Core/IOHandler.cpp:339:5
#33 0x00007fe9eb805609 lldb_private::IOHandlerEditline::Run() /home/hyubo/osmeta/external/llvm-project/lldb/source/Core/IOHandler.cpp:600:11
#34 0x00007fe9eb7b214c lldb_private::Debugger::RunIOHandlers() /home/hyubo/osmeta/external/llvm-project/lldb/source/Core/Debugger.cpp:1280:16
#35 0x00007fe9eb98f00f lldb_private::CommandInterpreter::RunCommandInterpreter(lldb_private::CommandInterpreterRunOptions&) /home/hyubo/osmeta/external/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp:3620:16
#36 0x00007fe9eb4f0e09 lldb::SBDebugger::RunCommandInterpreter(bool, bool) /home/hyubo/osmeta/external/llvm-project/lldb/source/API/SBDebugger.cpp:1234:42
#37 0x000055ca6788d6b0 Driver::MainLoop() /home/hyubo/osmeta/external/llvm-project/lldb/tools/driver/Driver.cpp:677:3
#38 0x000055ca6788e226 main /home/hyubo/osmeta/external/llvm-project/lldb/tools/driver/Driver.cpp:887:17
#39 0x00007fe9e5e2c657 __libc_start_call_main /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#40 0x00007fe9e5e2c718 call_init /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../csu/libc-start.c:128:20
#41 0x00007fe9e5e2c718 __libc_start_main@GLIBC_2.2.5 /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../csu/libc-start.c:379:5
#42 0x000055ca67889a11 _start /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../sysdeps/x86_64/start.S:118:0
Segmentation fault (core dumped)

When target symbols add is run, Symtab::AddSymbol() can reallocate the underlying std::vector<Symbol> and resize it, invalidating all existing Symbol* pointers. While Process::Flush() clears stale stack frames, the statusline caches its own ExecutionContextRef containing a StackID with a SymbolContextScope* (which can be a Symbol*). This cached reference is not cleared by Process::Flush(), so the next statusline redraw accesses a dangling pointer and crashes.

Fix this by adding Statusline::Flush() which clears the cached frame, Debugger::Flush() which forwards to it under the statusline mutex, and calling Debugger::Flush() from Process::Flush() so that all flush paths (symbol add, exec, module load) also invalidate the statusline's stale state.

After this fix, lldb is not crashing anymore, new symbols from a symbol file are correctly loaded

@GeorgeHuyubo
GeorgeHuyubo requested a review from clayborg March 25, 2026 00:01
@GeorgeHuyubo
GeorgeHuyubo marked this pull request as ready for review March 25, 2026 00:01
@llvmbot llvmbot added the lldb label Mar 25, 2026
@llvmbot

llvmbot commented Mar 25, 2026

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-lldb

Author: GeorgeHuyubo

Changes

Context:
lldb might crash when running to a debuggee crashing state and do a target symbols add command.
Backtrace:

 #<!-- -->0 0x000055ca6790dc65 llvm::sys::PrintStackTrace(llvm::raw_ostream&amp;, int) /home/hyubo/osmeta/external/llvm-project/llvm/lib/Support/Unix/Signals.inc:848:11
 #<!-- -->1 0x000055ca6790e434 PrintStackTraceSignalHandler(void*) /home/hyubo/osmeta/external/llvm-project/llvm/lib/Support/Unix/Signals.inc:931:1
 #<!-- -->2 0x000055ca6790b839 llvm::sys::RunSignalHandlers() /home/hyubo/osmeta/external/llvm-project/llvm/lib/Support/Signals.cpp:104:5
 #<!-- -->3 0x000055ca6790ff6b SignalHandler(int, siginfo_t*, void*) /home/hyubo/osmeta/external/llvm-project/llvm/lib/Support/Unix/Signals.inc:430:38
 #<!-- -->4 0x00007fe9e5e44560 __restore_rt /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/signal/../sysdeps/unix/sysv/linux/libc_sigaction.c:13:0
 #<!-- -->5 0x00007fe9e5f25649 syscall /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/misc/../sysdeps/unix/sysv/linux/x86_64/syscall.S:38:0
 #<!-- -->6 0x00007fe9ec649170 SignalHandler(int, siginfo_t*, void*) /home/hyubo/osmeta/external/llvm-project/llvm/lib/Support/Unix/Signals.inc:429:7
 #<!-- -->7 0x00007fe9e5e44560 __restore_rt /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/signal/../sysdeps/unix/sysv/linux/libc_sigaction.c:13:0
 #<!-- -->8 0x00007fe9ebb77bf0 lldb_private::operator&lt;(lldb_private::StackID const&amp;, lldb_private::StackID const&amp;) /home/hyubo/osmeta/external/llvm-project/lldb/source/Target/StackID.cpp:99:16
 #<!-- -->9 0x00007fe9ebb6863d CompareStackID(std::shared_ptr&lt;lldb_private::StackFrame&gt; const&amp;, lldb_private::StackID const&amp;) /home/hyubo/osmeta/external/llvm-project/lldb/source/Target/StackFrameList.cpp:683:3
#<!-- -->10 0x00007fe9ebb6d049 bool __gnu_cxx::__ops::_Iter_comp_val&lt;bool (*)(std::shared_ptr&lt;lldb_private::StackFrame&gt; const&amp;, lldb_private::StackID const&amp;)&gt;::operator()&lt;__gnu_cxx::__normal_iterator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;*, std::vector&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;, std::allocator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;&gt;&gt;&gt;, lldb_private::StackID const&gt;(__gnu_cxx::__normal_iterator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;*, std::vector&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;, std::allocator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;&gt;&gt;&gt;, lldb_private::StackID const&amp;) /mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/bits/predefined_ops.h:196:4
#<!-- -->11 0x00007fe9ebb6cefe __gnu_cxx::__normal_iterator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;*, std::vector&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;, std::allocator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;&gt;&gt;&gt; std::__lower_bound&lt;__gnu_cxx::__normal_iterator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;*, std::vector&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;, std::allocator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;&gt;&gt;&gt;, lldb_private::StackID, __gnu_cxx::__ops::_Iter_comp_val&lt;bool (*)(std::shared_ptr&lt;lldb_private::StackFrame&gt; const&amp;, lldb_private::StackID const&amp;)&gt;&gt;(__gnu_cxx::__normal_iterator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;*, std::vector&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;, std::allocator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;&gt;&gt;&gt;, __gnu_cxx::__normal_iterator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;*, std::vector&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;, std::allocator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;&gt;&gt;&gt;, lldb_private::StackID const&amp;, __gnu_cxx::__ops::_Iter_comp_val&lt;bool (*)(std::shared_ptr&lt;lldb_private::StackFrame&gt; const&amp;, lldb_private::StackID const&amp;)&gt;) /mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/bits/stl_algobase.h:1464:8
#<!-- -->12 0x00007fe9ebb6cdfc __gnu_cxx::__normal_iterator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;*, std::vector&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;, std::allocator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;&gt;&gt;&gt; std::lower_bound&lt;__gnu_cxx::__normal_iterator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;*, std::vector&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;, std::allocator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;&gt;&gt;&gt;, lldb_private::StackID, bool (*)(std::shared_ptr&lt;lldb_private::StackFrame&gt; const&amp;, lldb_private::StackID const&amp;)&gt;(__gnu_cxx::__normal_iterator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;*, std::vector&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;, std::allocator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;&gt;&gt;&gt;, __gnu_cxx::__normal_iterator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;*, std::vector&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;, std::allocator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;&gt;&gt;&gt;, lldb_private::StackID const&amp;, bool (*)(std::shared_ptr&lt;lldb_private::StackFrame&gt; const&amp;, lldb_private::StackID const&amp;)) /mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/bits/stl_algo.h:2062:14
#<!-- -->13 0x00007fe9ebb685fa auto llvm::lower_bound&lt;std::vector&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;, std::allocator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;&gt;&gt;&amp;, lldb_private::StackID const&amp;, bool (*)(std::shared_ptr&lt;lldb_private::StackFrame&gt; const&amp;, lldb_private::StackID const&amp;)&gt;(std::vector&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;, std::allocator&lt;std::shared_ptr&lt;lldb_private::StackFrame&gt;&gt;&gt;&amp;, lldb_private::StackID const&amp;, bool (*)(std::shared_ptr&lt;lldb_private::StackFrame&gt; const&amp;, lldb_private::StackID const&amp;)) /home/hyubo/osmeta/external/llvm-project/llvm/include/llvm/ADT/STLExtras.h:2001:10
#<!-- -->14 0x00007fe9ebb68441 lldb_private::StackFrameList::GetFrameWithStackID(lldb_private::StackID const&amp;) /home/hyubo/osmeta/external/llvm-project/lldb/source/Target/StackFrameList.cpp:697:11
#<!-- -->15 0x00007fe9ebbee395 lldb_private::Thread::GetFrameWithStackID(lldb_private::StackID const&amp;) /home/hyubo/osmeta/external/llvm-project/lldb/include/lldb/Target/Thread.h:459:7
#<!-- -->16 0x00007fe9ebac7cf7 lldb_private::ExecutionContextRef::GetFrameSP() const /home/hyubo/osmeta/external/llvm-project/lldb/source/Target/ExecutionContext.cpp:643:25
#<!-- -->17 0x00007fe9ebac80e1 lldb_private::GetStoppedExecutionContext(lldb_private::ExecutionContextRef const*) /home/hyubo/osmeta/external/llvm-project/lldb/source/Target/ExecutionContext.cpp:164:34
#<!-- -->18 0x00007fe9eb8903fa lldb_private::Statusline::Redraw(std::optional&lt;lldb_private::ExecutionContextRef&gt;) /home/hyubo/osmeta/external/llvm-project/lldb/source/Core/Statusline.cpp:139:7
#<!-- -->19 0x00007fe9eb7ac8be lldb_private::Debugger::RedrawStatusline(std::optional&lt;lldb_private::ExecutionContextRef&gt;) /home/hyubo/osmeta/external/llvm-project/lldb/source/Core/Debugger.cpp:1233:3
#<!-- -->20 0x00007fe9eb804d1e lldb_private::IOHandlerEditline::RedrawCallback() /home/hyubo/osmeta/external/llvm-project/lldb/source/Core/IOHandler.cpp:446:3
#<!-- -->21 0x00007fe9eb80aa81 lldb_private::IOHandlerEditline::IOHandlerEditline(lldb_private::Debugger&amp;, lldb_private::IOHandler::Type, std::shared_ptr&lt;lldb_private::File&gt; const&amp;, std::shared_ptr&lt;lldb_private::LockableStreamFile&gt; const&amp;, std::shared_ptr&lt;lldb_private::LockableStreamFile&gt; const&amp;, unsigned int, char const*, llvm::StringRef, llvm::StringRef, bool, bool, unsigned int, lldb_private::IOHandlerDelegate&amp;)::$_2::operator()() const /home/hyubo/osmeta/external/llvm-project/lldb/source/Core/IOHandler.cpp:262:73
#<!-- -->22 0x00007fe9eb80aa5d void llvm::detail::UniqueFunctionBase&lt;void&gt;::CallImpl&lt;lldb_private::IOHandlerEditline::IOHandlerEditline(lldb_private::Debugger&amp;, lldb_private::IOHandler::Type, std::shared_ptr&lt;lldb_private::File&gt; const&amp;, std::shared_ptr&lt;lldb_private::LockableStreamFile&gt; const&amp;, std::shared_ptr&lt;lldb_private::LockableStreamFile&gt; const&amp;, unsigned int, char const*, llvm::StringRef, llvm::StringRef, bool, bool, unsigned int, lldb_private::IOHandlerDelegate&amp;)::$_2&gt;(void*) /home/hyubo/osmeta/external/llvm-project/llvm/include/llvm/ADT/FunctionExtras.h:213:5
#<!-- -->23 0x00007fe9eb93bfbf llvm::unique_function&lt;void ()&gt;::operator()() /home/hyubo/osmeta/external/llvm-project/llvm/include/llvm/ADT/FunctionExtras.h:365:5
#<!-- -->24 0x00007fe9eb93bb80 lldb_private::Editline::GetCharacter(wchar_t*) /home/hyubo/osmeta/external/llvm-project/lldb/source/Host/common/Editline.cpp:0:5
#<!-- -->25 0x00007fe9eb941a18 lldb_private::Editline::ConfigureEditor(bool)::$_0::operator()(editline*, wchar_t*) const /home/hyubo/osmeta/external/llvm-project/lldb/source/Host/common/Editline.cpp:1287:5
#<!-- -->26 0x00007fe9eb9419e2 lldb_private::Editline::ConfigureEditor(bool)::$_0::__invoke(editline*, wchar_t*) /home/hyubo/osmeta/external/llvm-project/lldb/source/Host/common/Editline.cpp:1286:27
#<!-- -->27 0x00007fe9f3384e26 el_getc /home/engshare/third-party2/libedit/3.1/src/libedit/src/read.c:439:14
#<!-- -->28 0x00007fe9f3384e26 el_getc /home/engshare/third-party2/libedit/3.1/src/libedit/src/read.c:400:1
#<!-- -->29 0x00007fe9f3384f90 read_getcmd /home/engshare/third-party2/libedit/3.1/src/libedit/src/read.c:247:14
#<!-- -->30 0x00007fe9f3384f90 el_gets /home/engshare/third-party2/libedit/3.1/src/libedit/src/read.c:586:14
#<!-- -->31 0x00007fe9eb9409f3 lldb_private::Editline::GetLine(std::__cxx11::basic_string&lt;char, std::char_traits&lt;char&gt;, std::allocator&lt;char&gt;&gt;&amp;, bool&amp;) /home/hyubo/osmeta/external/llvm-project/lldb/source/Host/common/Editline.cpp:1636:16
#<!-- -->32 0x00007fe9eb8044d7 lldb_private::IOHandlerEditline::GetLine(std::__cxx11::basic_string&lt;char, std::char_traits&lt;char&gt;, std::allocator&lt;char&gt;&gt;&amp;, bool&amp;) /home/hyubo/osmeta/external/llvm-project/lldb/source/Core/IOHandler.cpp:339:5
#<!-- -->33 0x00007fe9eb805609 lldb_private::IOHandlerEditline::Run() /home/hyubo/osmeta/external/llvm-project/lldb/source/Core/IOHandler.cpp:600:11
#<!-- -->34 0x00007fe9eb7b214c lldb_private::Debugger::RunIOHandlers() /home/hyubo/osmeta/external/llvm-project/lldb/source/Core/Debugger.cpp:1280:16
#<!-- -->35 0x00007fe9eb98f00f lldb_private::CommandInterpreter::RunCommandInterpreter(lldb_private::CommandInterpreterRunOptions&amp;) /home/hyubo/osmeta/external/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp:3620:16
#<!-- -->36 0x00007fe9eb4f0e09 lldb::SBDebugger::RunCommandInterpreter(bool, bool) /home/hyubo/osmeta/external/llvm-project/lldb/source/API/SBDebugger.cpp:1234:42
#<!-- -->37 0x000055ca6788d6b0 Driver::MainLoop() /home/hyubo/osmeta/external/llvm-project/lldb/tools/driver/Driver.cpp:677:3
#<!-- -->38 0x000055ca6788e226 main /home/hyubo/osmeta/external/llvm-project/lldb/tools/driver/Driver.cpp:887:17
#<!-- -->39 0x00007fe9e5e2c657 __libc_start_call_main /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#<!-- -->40 0x00007fe9e5e2c718 call_init /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../csu/libc-start.c:128:20
#<!-- -->41 0x00007fe9e5e2c718 __libc_start_main@<!-- -->GLIBC_2.2.5 /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../csu/libc-start.c:379:5
#<!-- -->42 0x000055ca67889a11 _start /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../sysdeps/x86_64/start.S:118:0
Segmentation fault (core dumped)

When target symbols add is run, Symtab::AddSymbol() can reallocate the underlying std::vector&lt;Symbol&gt; and resize it, invalidating all existing Symbol* pointers. While Process::Flush() clears stale stack frames, the statusline caches its own ExecutionContextRef containing a StackID with a SymbolContextScope* (which can be a Symbol*). This cached reference is not cleared by Process::Flush(), so the next statusline redraw accesses a dangling pointer and crashes.

Fix this by adding Statusline::Flush() which clears the cached frame, Debugger::Flush() which forwards to it under the statusline mutex, and calling Debugger::Flush() from Process::Flush() so that all flush paths (symbol add, exec, module load) also invalidate the statusline's stale state.

After this fix, lldb is not crashing anymore, new symbols from a symbol file are correctly loaded


Full diff: https://github.com/llvm/llvm-project/pull/188377.diff

5 Files Affected:

  • (modified) lldb/include/lldb/Core/Debugger.h (+3)
  • (modified) lldb/include/lldb/Core/Statusline.h (+3)
  • (modified) lldb/source/Core/Debugger.cpp (+9)
  • (modified) lldb/source/Core/Statusline.cpp (+2)
  • (modified) lldb/source/Target/Process.cpp (+1)
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index fa4483c93e639..1b0dc7d26ba5b 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -457,6 +457,9 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
   /// Redraw the statusline if enabled.
   void RedrawStatusline(std::optional<ExecutionContextRef> exe_ctx_ref);
 
+  /// Flush cached state (e.g. stale execution context in the statusline).
+  void Flush();
+
   /// This is the correct way to query the state of Interruption.
   /// If you are on the RunCommandInterpreter thread, it will check the
   /// command interpreter state, and if it is on another thread it will
diff --git a/lldb/include/lldb/Core/Statusline.h b/lldb/include/lldb/Core/Statusline.h
index a5ab1927b57f5..26bf9323d5650 100644
--- a/lldb/include/lldb/Core/Statusline.h
+++ b/lldb/include/lldb/Core/Statusline.h
@@ -32,6 +32,9 @@ class Statusline {
   /// Redraw the statusline.
   void Redraw(std::optional<ExecutionContextRef> exe_ctx_ref);
 
+  /// Clear the cached execution context to discard stale pointers.
+  void Flush();
+
   /// Inform the statusline that the terminal dimensions have changed.
   void TerminalSizeChanged();
 
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index e1b2ce1b063e0..0c449b01da040 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -1284,6 +1284,15 @@ void Debugger::RedrawStatusline(
   m_statusline->Redraw(exe_ctx_ref);
 }
 
+void Debugger::Flush() {
+  std::lock_guard<std::mutex> guard(m_statusline_mutex);
+
+  if (!m_statusline)
+    return;
+
+  m_statusline->Flush();
+}
+
 ExecutionContext Debugger::GetSelectedExecutionContext() {
   bool adopt_selected = true;
   ExecutionContextRef exe_ctx_ref(GetSelectedTarget().get(), adopt_selected);
diff --git a/lldb/source/Core/Statusline.cpp b/lldb/source/Core/Statusline.cpp
index bdc649580637c..357ecbc825200 100644
--- a/lldb/source/Core/Statusline.cpp
+++ b/lldb/source/Core/Statusline.cpp
@@ -125,6 +125,8 @@ void Statusline::UpdateScrollWindow(ScrollWindowMode mode) {
   m_debugger.RefreshIOHandler();
 }
 
+void Statusline::Flush() { m_exe_ctx_ref.ClearFrame(); }
+
 void Statusline::Redraw(std::optional<ExecutionContextRef> exe_ctx_ref) {
   // Update the cached execution context.
   if (exe_ctx_ref)
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 587d9f6408ae4..b9adc90cde436 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -6025,6 +6025,7 @@ void Process::Flush() {
   m_extended_thread_stop_id = 0;
   m_queue_list.Clear();
   m_queue_list_stop_id = 0;
+  GetTarget().GetDebugger().Flush();
 }
 
 lldb::addr_t Process::GetCodeAddressMask() {

@clayborg clayborg left a comment

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.

@JDevlieghere we ran into the status line crashing us when we added symbols to an ELF file that was stopped in a __lldb_unamed_symbolXXX symbol. It is hard to reproduce in a test case as it requires a specific order or things happening to the heap.

The issue is the StackID cached the Symbol * in StackID.m_symbol_scope. Adding symbols can cause symbols to get added to the symbol table when the new symbol file is loaded which contains local symbols. This causes the symbol table vector to be re-sized and invalidates the pointer. But the status line now has a stale SymbolContextScope *m_symbol_scope cached which can cause a crash depending on if the memory that this pointed to points to other random data.

We need a way to invalidate any out of date SymbolContextScope * entries that anyone is holding onto. In this case the StatusLine, but we might need to also clear other things within the debugger in the future. Like maybe the ExecutionContext in the command interpreter.

@bulbazord

Copy link
Copy Markdown
Member

This one is particularly scary because LLDB passes out Symbol * to many classes without any tracking or refcounting. It's not just SymbolContextScope. From a cursory search with grep, I see LanguageRuntime, ValueObjectVTable, and ClangExpressionVariable. The worst offender of all is SBSymbol which is backed by a Symbol * entirely. The Symbol * going away is a big no-no.

I think we need to rethink our Symbol management entirely if we're allowed to add to the symbol tables whenever we want.

Comment thread lldb/include/lldb/Core/Statusline.h Outdated
Comment thread lldb/include/lldb/Core/Debugger.h Outdated
@GeorgeHuyubo

Copy link
Copy Markdown
Contributor Author

@JDevlieghere Renamed function to be more specific, we can always rename it later once it clear more things

@llvm llvm deleted a comment from github-actions Bot Apr 1, 2026

@clayborg clayborg left a comment

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.

@JDevlieghere are you ok with the current state here? This is a stop gap fix to stop things from crashing.

The much harder but root cause fix is to have the symbol table be able to have multiple vectors of symbols and have APIs to add symbols by adding a complete vector. That way when an object file is parsing its symbol table (original executable), it will create its own vector and add the vector into the symbol table, and then when another object file (dSYM or newly added symbol file) can do the same thing and register another vector of symbols. This will ensure that no symbol pointers can go stale. This change will be much larger and more complex, but should be done at some point.

@GeorgeHuyubo
GeorgeHuyubo merged commit 02fdc8f into llvm:main Apr 15, 2026
13 of 14 checks passed
@JDevlieghere

Copy link
Copy Markdown
Member

@GeorgeHuyubo This PR was merged without approval. Please review our Code Review Policy:

[O]nce a patch is sent out for review, it needs an explicit approval before it is committed. Do not assume silent approval, or solicit objections to a patch with a deadline.

I still have two concerns with this patch even if we want to go for a pragmatic solution:

  1. This needs a test. I'm sure we have a test that does target symbols add but only one test runs with the statusline enabled.
  2. I'm not (yet) convinced that clearing the execution context is the correct thing to do, versus creating a new one with just the target. We go out of our way to always have a target because that's part of the default format. I'm worried this will leave the statusline empty (should be validated by the aforementioned test).

If this can be addressed quickly I'm fine with fixing forward, but otherwise please revert while we iterate on a solution.

@GeorgeHuyubo

Copy link
Copy Markdown
Contributor Author

Added test: #193854 @JDevlieghere

GeorgeHuyubo added a commit that referenced this pull request Apr 24, 2026
…ne (#193854)

Add a test that exercises the code path fixed in
[88f024223cc4](#188377)
("[lldb] Fix stale Symbol pointer crash in statusline after 'target
symbols add'").

The bug: when `target symbols add` is called, `Symtab::AddSymbol()` can
reallocate the underlying `std::vector<Symbol>`, invalidating all
existing `Symbol*` pointers. The statusline caches an
`ExecutionContextRef` containing a `StackID` with a
`SymbolContextScope*` (which can be a `Symbol*`). If a concurrent
statusline redraw occurs between the Symtab reallocation and
`Process::Flush()` (e.g. from a progress event on the event handler
thread), the cached `StackID` matches the old frame via pointer-equal
comparison, and `GetSymbolContext()` dereferences the dangling
`Symbol*`.

The test:
  1. Builds a stripped binary and its unstripped counterpart.
  2. Runs the stripped binary and stops at a breakpoint in `main`.
3. Enables the statusline (populating the cached `ExecutionContextRef`).
  4. Calls `target symbols add` to add debug symbols back.
5. Forces a statusline redraw via terminal resize, which calls
`Redraw(std::nullopt)` using the cached (potentially stale) execution
context.
  6. Verifies the statusline still renders correctly.

The dangling pointer is a use-after-free that silently succeeds in
non-sanitizer builds (the freed memory is still accessible). This test
is most effective under AddressSanitizer, which poisons freed memory and
immediately detects the stale access.

Co-authored-by: George Hu <georgehuyubo@gmail.com>
llvm-sync Bot pushed a commit to arm/arm-toolchain that referenced this pull request Apr 24, 2026
…in statusline (#193854)

Add a test that exercises the code path fixed in
[88f024223cc4](llvm/llvm-project#188377)
("[lldb] Fix stale Symbol pointer crash in statusline after 'target
symbols add'").

The bug: when `target symbols add` is called, `Symtab::AddSymbol()` can
reallocate the underlying `std::vector<Symbol>`, invalidating all
existing `Symbol*` pointers. The statusline caches an
`ExecutionContextRef` containing a `StackID` with a
`SymbolContextScope*` (which can be a `Symbol*`). If a concurrent
statusline redraw occurs between the Symtab reallocation and
`Process::Flush()` (e.g. from a progress event on the event handler
thread), the cached `StackID` matches the old frame via pointer-equal
comparison, and `GetSymbolContext()` dereferences the dangling
`Symbol*`.

The test:
  1. Builds a stripped binary and its unstripped counterpart.
  2. Runs the stripped binary and stops at a breakpoint in `main`.
3. Enables the statusline (populating the cached `ExecutionContextRef`).
  4. Calls `target symbols add` to add debug symbols back.
5. Forces a statusline redraw via terminal resize, which calls
`Redraw(std::nullopt)` using the cached (potentially stale) execution
context.
  6. Verifies the statusline still renders correctly.

The dangling pointer is a use-after-free that silently succeeds in
non-sanitizer builds (the freed memory is still accessible). This test
is most effective under AddressSanitizer, which poisons freed memory and
immediately detects the stale access.

Co-authored-by: George Hu <georgehuyubo@gmail.com>
llvm-upstreamsync Bot pushed a commit to qualcomm/cpullvm-toolchain that referenced this pull request Apr 24, 2026
…in statusline (#193854)

Add a test that exercises the code path fixed in
[88f024223cc4](llvm/llvm-project#188377)
("[lldb] Fix stale Symbol pointer crash in statusline after 'target
symbols add'").

The bug: when `target symbols add` is called, `Symtab::AddSymbol()` can
reallocate the underlying `std::vector<Symbol>`, invalidating all
existing `Symbol*` pointers. The statusline caches an
`ExecutionContextRef` containing a `StackID` with a
`SymbolContextScope*` (which can be a `Symbol*`). If a concurrent
statusline redraw occurs between the Symtab reallocation and
`Process::Flush()` (e.g. from a progress event on the event handler
thread), the cached `StackID` matches the old frame via pointer-equal
comparison, and `GetSymbolContext()` dereferences the dangling
`Symbol*`.

The test:
  1. Builds a stripped binary and its unstripped counterpart.
  2. Runs the stripped binary and stops at a breakpoint in `main`.
3. Enables the statusline (populating the cached `ExecutionContextRef`).
  4. Calls `target symbols add` to add debug symbols back.
5. Forces a statusline redraw via terminal resize, which calls
`Redraw(std::nullopt)` using the cached (potentially stale) execution
context.
  6. Verifies the statusline still renders correctly.

The dangling pointer is a use-after-free that silently succeeds in
non-sanitizer builds (the freed memory is still accessible). This test
is most effective under AddressSanitizer, which poisons freed memory and
immediately detects the stale access.

Co-authored-by: George Hu <georgehuyubo@gmail.com>
cpullvm-upstream-sync Bot pushed a commit to navaneethshan/cpullvm-toolchain-1 that referenced this pull request Apr 24, 2026
…in statusline (#193854)

Add a test that exercises the code path fixed in
[88f024223cc4](llvm/llvm-project#188377)
("[lldb] Fix stale Symbol pointer crash in statusline after 'target
symbols add'").

The bug: when `target symbols add` is called, `Symtab::AddSymbol()` can
reallocate the underlying `std::vector<Symbol>`, invalidating all
existing `Symbol*` pointers. The statusline caches an
`ExecutionContextRef` containing a `StackID` with a
`SymbolContextScope*` (which can be a `Symbol*`). If a concurrent
statusline redraw occurs between the Symtab reallocation and
`Process::Flush()` (e.g. from a progress event on the event handler
thread), the cached `StackID` matches the old frame via pointer-equal
comparison, and `GetSymbolContext()` dereferences the dangling
`Symbol*`.

The test:
  1. Builds a stripped binary and its unstripped counterpart.
  2. Runs the stripped binary and stops at a breakpoint in `main`.
3. Enables the statusline (populating the cached `ExecutionContextRef`).
  4. Calls `target symbols add` to add debug symbols back.
5. Forces a statusline redraw via terminal resize, which calls
`Redraw(std::nullopt)` using the cached (potentially stale) execution
context.
  6. Verifies the statusline still renders correctly.

The dangling pointer is a use-after-free that silently succeeds in
non-sanitizer builds (the freed memory is still accessible). This test
is most effective under AddressSanitizer, which poisons freed memory and
immediately detects the stale access.

Co-authored-by: George Hu <georgehuyubo@gmail.com>
yingopq pushed a commit to yingopq/llvm-project that referenced this pull request Apr 29, 2026
…ne (llvm#193854)

Add a test that exercises the code path fixed in
[88f024223cc4](llvm#188377)
("[lldb] Fix stale Symbol pointer crash in statusline after 'target
symbols add'").

The bug: when `target symbols add` is called, `Symtab::AddSymbol()` can
reallocate the underlying `std::vector<Symbol>`, invalidating all
existing `Symbol*` pointers. The statusline caches an
`ExecutionContextRef` containing a `StackID` with a
`SymbolContextScope*` (which can be a `Symbol*`). If a concurrent
statusline redraw occurs between the Symtab reallocation and
`Process::Flush()` (e.g. from a progress event on the event handler
thread), the cached `StackID` matches the old frame via pointer-equal
comparison, and `GetSymbolContext()` dereferences the dangling
`Symbol*`.

The test:
  1. Builds a stripped binary and its unstripped counterpart.
  2. Runs the stripped binary and stops at a breakpoint in `main`.
3. Enables the statusline (populating the cached `ExecutionContextRef`).
  4. Calls `target symbols add` to add debug symbols back.
5. Forces a statusline redraw via terminal resize, which calls
`Redraw(std::nullopt)` using the cached (potentially stale) execution
context.
  6. Verifies the statusline still renders correctly.

The dangling pointer is a use-after-free that silently succeeds in
non-sanitizer builds (the freed memory is still accessible). This test
is most effective under AddressSanitizer, which poisons freed memory and
immediately detects the stale access.

Co-authored-by: George Hu <georgehuyubo@gmail.com>
KHicketts pushed a commit to KHicketts/llvm-project that referenced this pull request Apr 30, 2026
…ne (llvm#193854)

Add a test that exercises the code path fixed in
[88f024223cc4](llvm#188377)
("[lldb] Fix stale Symbol pointer crash in statusline after 'target
symbols add'").

The bug: when `target symbols add` is called, `Symtab::AddSymbol()` can
reallocate the underlying `std::vector<Symbol>`, invalidating all
existing `Symbol*` pointers. The statusline caches an
`ExecutionContextRef` containing a `StackID` with a
`SymbolContextScope*` (which can be a `Symbol*`). If a concurrent
statusline redraw occurs between the Symtab reallocation and
`Process::Flush()` (e.g. from a progress event on the event handler
thread), the cached `StackID` matches the old frame via pointer-equal
comparison, and `GetSymbolContext()` dereferences the dangling
`Symbol*`.

The test:
  1. Builds a stripped binary and its unstripped counterpart.
  2. Runs the stripped binary and stops at a breakpoint in `main`.
3. Enables the statusline (populating the cached `ExecutionContextRef`).
  4. Calls `target symbols add` to add debug symbols back.
5. Forces a statusline redraw via terminal resize, which calls
`Redraw(std::nullopt)` using the cached (potentially stale) execution
context.
  6. Verifies the statusline still renders correctly.

The dangling pointer is a use-after-free that silently succeeds in
non-sanitizer builds (the freed memory is still accessible). This test
is most effective under AddressSanitizer, which poisons freed memory and
immediately detects the stale access.

Co-authored-by: George Hu <georgehuyubo@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants