llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: None (GeorgeHuyubo) <details> <summary>Changes</summary> When displaying a backtrace with std::tuple function arguments, GetChildAtIndex() can return a null ValueObjectSP. The code was dereferencing this pointer without checking for null, causing a SIGSEGV crash when LLDB tried to pretty-print function arguments containing tuples. Example lldb crash bt: ``` (lldb) bt LLDB diagnostics will be written to /tmp/diagnostics-02b2ba Please include the directory content when filing a bug report PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace. Stack dump: 0. Program arguments: ./build/Debug/fbcode-x86_64/toolchain/bin/lldb -o "auto-load-debuginfo /mnt/persistent-public/alexandreperez/tdiye4de13zax4j9/tdiye4de13zax4j9" #<!-- -->0 0x000055c3e8be3d25 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /home/hyubo/llvm-sand/external/llvm-project/llvm/lib/Support/Unix/Signals.inc:848:11 #<!-- -->1 0x000055c3e8be44f4 PrintStackTraceSignalHandler(void*) /home/hyubo/llvm-sand/external/llvm-project/llvm/lib/Support/Unix/Signals.inc:931:1 #<!-- -->2 0x000055c3e8be18f9 llvm::sys::RunSignalHandlers() /home/hyubo/llvm-sand/external/llvm-project/llvm/lib/Support/Signals.cpp:104:5 #<!-- -->3 0x000055c3e8be602b SignalHandler(int, siginfo_t*, void*) /home/hyubo/llvm-sand/external/llvm-project/llvm/lib/Support/Unix/Signals.inc:430:38 #<!-- -->4 0x00007f162f044560 __restore_rt /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/signal/../sysdeps/unix/sysv/linux/libc_sigaction.c:13:0 #<!-- -->5 0x00007f162f125649 syscall /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/misc/../sysdeps/unix/sysv/linux/x86_64/syscall.S:38:0 #<!-- -->6 0x00007f163584a6c0 SignalHandler(int, siginfo_t*, void*) /home/hyubo/llvm-sand/external/llvm-project/llvm/lib/Support/Unix/Signals.inc:429:7 #<!-- -->7 0x00007f162f044560 __restore_rt /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/signal/../sysdeps/unix/sysv/linux/libc_sigaction.c:13:0 #<!-- -->8 0x00007f16347f182a lldb_private::ValueObject::GetName() const /home/hyubo/llvm-sand/external/llvm-project/lldb/include/lldb/ValueObject/ValueObject.h:487:40 #<!-- -->9 0x00007f16351a9f95 (anonymous namespace)::LibStdcppTupleSyntheticFrontEnd::Update() /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp:69:44 #<!-- -->10 0x00007f16351a9bfe (anonymous namespace)::LibStdcppTupleSyntheticFrontEnd::LibStdcppTupleSyntheticFrontEnd(std::shared_ptr<lldb_private::ValueObject>) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp:52:1 #<!-- -->11 0x00007f16351a9b62 lldb_private::formatters::LibStdcppTupleSyntheticFrontEndCreator(lldb_private::CXXSyntheticChildren*, std::shared_ptr<lldb_private::ValueObject>) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp:112:27 #<!-- -->12 0x00007f1635148d9e GenericTupleSyntheticFrontEndCreator(lldb_private::CXXSyntheticChildren*, std::shared_ptr<lldb_private::ValueObject>) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp:1556:3 #<!-- -->13 0x00007f163514618b lldb_private::SyntheticChildrenFrontEnd* std::__invoke_impl<lldb_private::SyntheticChildrenFrontEnd*, lldb_private::SyntheticChildrenFrontEnd* (*&)(lldb_private::CXXSyntheticChildren*, std::shared_ptr<lldb_private::ValueObject>), lldb_private::CXXSyntheticChildren*, std::shared_ptr<lldb_private::ValueObject>>(std::__invoke_other, lldb_private::SyntheticChildrenFrontEnd* (*&)(lldb_private::CXXSyntheticChildren*, std::shared_ptr<lldb_private::ValueObject>), lldb_private::CXXSyntheticChildren*&&, std::shared_ptr<lldb_private::ValueObject>&&) /mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/bits/invoke.h:61:14 #<!-- -->14 0x00007f1635146107 std::enable_if<is_invocable_r_v<lldb_private::SyntheticChildrenFrontEnd*, lldb_private::SyntheticChildrenFrontEnd* (*&)(lldb_private::CXXSyntheticChildren*, std::shared_ptr<lldb_private::ValueObject>), lldb_private::CXXSyntheticChildren*, std::shared_ptr<lldb_private::ValueObject>>, lldb_private::SyntheticChildrenFrontEnd*>::type std::__invoke_r<lldb_private::SyntheticChildrenFrontEnd*, lldb_private::SyntheticChildrenFrontEnd* (*&)(lldb_private::CXXSyntheticChildren*, std::shared_ptr<lldb_private::ValueObject>), lldb_private::CXXSyntheticChildren*, std::shared_ptr<lldb_private::ValueObject>>(lldb_private::SyntheticChildrenFrontEnd* (*&)(lldb_private::CXXSyntheticChildren*, std::shared_ptr<lldb_private::ValueObject>), lldb_private::CXXSyntheticChildren*&&, std::shared_ptr<lldb_private::ValueObject>&&) /mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/bits/invoke.h:114:2 #<!-- -->15 0x00007f163514603d std::_Function_handler<lldb_private::SyntheticChildrenFrontEnd* (lldb_private::CXXSyntheticChildren*, std::shared_ptr<lldb_private::ValueObject>), lldb_private::SyntheticChildrenFrontEnd* (*)(lldb_private::CXXSyntheticChildren*, std::shared_ptr<lldb_private::ValueObject>)>::_M_invoke(std::_Any_data const&, lldb_private::CXXSyntheticChildren*&&, std::shared_ptr<lldb_private::ValueObject>&&) /mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/bits/std_function.h:291:2 #<!-- -->16 0x00007f1634af8245 std::function<lldb_private::SyntheticChildrenFrontEnd* (lldb_private::CXXSyntheticChildren*, std::shared_ptr<lldb_private::ValueObject>)>::operator()(lldb_private::CXXSyntheticChildren*, std::shared_ptr<lldb_private::ValueObject>) const /mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a/include/c++/11.x/bits/std_function.h:560:2 #<!-- -->17 0x00007f1634af7b84 lldb_private::CXXSyntheticChildren::GetFrontEnd(lldb_private::ValueObject&) /home/hyubo/llvm-sand/external/llvm-project/lldb/include/lldb/DataFormatters/TypeSynthetic.h:392:9 #<!-- -->18 0x00007f1634efbda3 lldb_private::ValueObjectSynthetic::CreateSynthFilter() /home/hyubo/llvm-sand/external/llvm-project/lldb/source/ValueObject/ValueObjectSynthetic.cpp:155:36 #<!-- -->19 0x00007f1634efb3c1 lldb_private::ValueObjectSynthetic::ValueObjectSynthetic(lldb_private::ValueObject&, std::shared_ptr<lldb_private::SyntheticChildren>) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/ValueObject/ValueObjectSynthetic.cpp:66:1 #<!-- -->20 0x00007f1634ee1d62 lldb_private::ValueObject::CalculateSyntheticValue() /home/hyubo/llvm-sand/external/llvm-project/lldb/source/ValueObject/ValueObject.cpp:2038:27 #<!-- -->21 0x00007f1634edb61d lldb_private::ValueObject::GetSyntheticValue() /home/hyubo/llvm-sand/external/llvm-project/lldb/source/ValueObject/ValueObject.cpp:2069:3 #<!-- -->22 0x00007f1634ee4823 lldb_private::ValueObject::GetQualifiedRepresentationIfAvailable(lldb::DynamicValueType, bool) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/ValueObject/ValueObject.cpp:2784:36 #<!-- -->23 0x00007f16349fd384 lldb_private::FormatEntity::PrettyPrintFunctionArguments(lldb_private::Stream&, lldb_private::VariableList const&, lldb_private::ExecutionContextScope*) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Core/FormatEntity.cpp:2629:22 #<!-- -->24 0x00007f163513a555 lldb_private::CPlusPlusLanguage::HandleFrameFormatVariable(lldb_private::SymbolContext const&, lldb_private::ExecutionContext const*, lldb_private::FormatEntity::Entry::Type, lldb_private::Stream&) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp:2271:5 #<!-- -->25 0x00007f16349f8005 lldb_private::FormatEntity::Format(lldb_private::FormatEntity::Entry const&, lldb_private::Stream&, lldb_private::SymbolContext const*, lldb_private::ExecutionContext const*, lldb_private::Address const*, lldb_private::ValueObject*, bool, bool) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Core/FormatEntity.cpp:1877:5 #<!-- -->26 0x00007f16349f5cd7 lldb_private::FormatEntity::Format(lldb_private::FormatEntity::Entry const&, lldb_private::Stream&, lldb_private::SymbolContext const*, lldb_private::ExecutionContext const*, lldb_private::Address const*, lldb_private::ValueObject*, bool, bool) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Core/FormatEntity.cpp:1344:11 #<!-- -->27 0x00007f16349fba58 FormatFunctionNameForLanguage(lldb_private::Stream&, lldb_private::ExecutionContext const*, lldb_private::SymbolContext const*) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Core/FormatEntity.cpp:1293:14 #<!-- -->28 0x00007f16349f8048 lldb_private::FormatEntity::Format(lldb_private::FormatEntity::Entry const&, lldb_private::Stream&, lldb_private::SymbolContext const*, lldb_private::ExecutionContext const*, lldb_private::Address const*, lldb_private::ValueObject*, bool, bool) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Core/FormatEntity.cpp:1885:9 #<!-- -->29 0x00007f16349f8c6f lldb_private::FormatEntity::Format(lldb_private::FormatEntity::Entry const&, lldb_private::Stream&, lldb_private::SymbolContext const*, lldb_private::ExecutionContext const*, lldb_private::Address const*, lldb_private::ValueObject*, bool, bool)::$_0::operator()(std::vector<lldb_private::FormatEntity::Entry, std::allocator<lldb_private::FormatEntity::Entry>> const&) const /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Core/FormatEntity.cpp:1360:13 #<!-- -->30 0x00007f16349f5e50 lldb_private::FormatEntity::Format(lldb_private::FormatEntity::Entry const&, lldb_private::Stream&, lldb_private::SymbolContext const*, lldb_private::ExecutionContext const*, lldb_private::Address const*, lldb_private::ValueObject*, bool, bool) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Core/FormatEntity.cpp:1368:11 #<!-- -->31 0x00007f16349f8c6f lldb_private::FormatEntity::Format(lldb_private::FormatEntity::Entry const&, lldb_private::Stream&, lldb_private::SymbolContext const*, lldb_private::ExecutionContext const*, lldb_private::Address const*, lldb_private::ValueObject*, bool, bool)::$_0::operator()(std::vector<lldb_private::FormatEntity::Entry, std::allocator<lldb_private::FormatEntity::Entry>> const&) const /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Core/FormatEntity.cpp:1360:13 #<!-- -->32 0x00007f16349f5e50 lldb_private::FormatEntity::Format(lldb_private::FormatEntity::Entry const&, lldb_private::Stream&, lldb_private::SymbolContext const*, lldb_private::ExecutionContext const*, lldb_private::Address const*, lldb_private::ValueObject*, bool, bool) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Core/FormatEntity.cpp:1368:11 #<!-- -->33 0x00007f16349f5cd7 lldb_private::FormatEntity::Format(lldb_private::FormatEntity::Entry const&, lldb_private::Stream&, lldb_private::SymbolContext const*, lldb_private::ExecutionContext const*, lldb_private::Address const*, lldb_private::ValueObject*, bool, bool) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Core/FormatEntity.cpp:1344:11 #<!-- -->34 0x00007f1634d62853 lldb_private::StackFrame::DumpUsingFormat(lldb_private::Stream&, lldb_private::FormatEntity::Entry const*, llvm::StringRef) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Target/StackFrame.cpp:1929:14 #<!-- -->35 0x00007f1634d62a92 lldb_private::StackFrame::DumpUsingSettingsFormat(lldb_private::Stream*, bool, char const*) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Target/StackFrame.cpp:1956:7 #<!-- -->36 0x00007f1634d632d5 lldb_private::StackFrame::GetStatus(lldb_private::Stream&, bool, bool, bool, char const*) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Target/StackFrame.cpp:2043:7 #<!-- -->37 0x00007f1634d6b2a7 lldb_private::StackFrameList::GetStatus(lldb_private::Stream&, unsigned int, unsigned int, bool, unsigned int, bool, bool, char const*) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Target/StackFrameList.cpp:937:9 #<!-- -->38 0x00007f1634ded778 lldb_private::Thread::GetStatus(lldb_private::Stream&, unsigned int, unsigned int, unsigned int, bool, bool, bool) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Target/Thread.cpp:1827:22 #<!-- -->39 0x00007f16372041dd CommandObjectThreadBacktrace::HandleOneThread(unsigned long, lldb_private::CommandReturnObject&) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Commands/CommandObjectThread.cpp:239:10 #<!-- -->40 0x00007f163721e9e1 lldb_private::CommandObjectIterateOverThreads::DoExecute(lldb_private::Args&, lldb_private::CommandReturnObject&) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Commands/CommandObjectThreadUtil.cpp:46:5 #<!-- -->41 0x00007f1634bb984c lldb_private::CommandObjectParsed::Execute(char const*, lldb_private::CommandReturnObject&) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Interpreter/CommandObject.cpp:832:5 #<!-- -->42 0x00007f1634b89e70 lldb_private::CommandInterpreter::HandleCommand(char const*, lldb_private::LazyBool, lldb_private::CommandReturnObject&, bool) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp:2211:3 #<!-- -->43 0x00007f16371bfefd lldb_private::CommandObjectRegexCommand::DoExecute(llvm::StringRef, lldb_private::CommandReturnObject&) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Commands/CommandObjectRegexCommand.cpp:76:21 #<!-- -->44 0x00007f1634bb9b6f lldb_private::CommandObjectRaw::Execute(char const*, lldb_private::CommandReturnObject&) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Interpreter/CommandObject.cpp:0:7 #<!-- -->45 0x00007f1634b89e70 lldb_private::CommandInterpreter::HandleCommand(char const*, lldb_private::LazyBool, lldb_private::CommandReturnObject&, bool) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp:2211:3 #<!-- -->46 0x00007f1634b8f0d1 lldb_private::CommandInterpreter::IOHandlerInputComplete(lldb_private::IOHandler&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp:3331:15 #<!-- -->47 0x00007f1634a072e4 lldb_private::IOHandlerEditline::Run() /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Core/IOHandler.cpp:605:7 #<!-- -->48 0x00007f16349b3dec lldb_private::Debugger::RunIOHandlers() /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Core/Debugger.cpp:1280:16 #<!-- -->49 0x00007f1634b90b4f lldb_private::CommandInterpreter::RunCommandInterpreter(lldb_private::CommandInterpreterRunOptions&) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp:3620:16 #<!-- -->50 0x00007f16346f2e09 lldb::SBDebugger::RunCommandInterpreter(bool, bool) /home/hyubo/llvm-sand/external/llvm-project/lldb/source/API/SBDebugger.cpp:1234:42 #<!-- -->51 0x000055c3e8b63770 Driver::MainLoop() /home/hyubo/llvm-sand/external/llvm-project/lldb/tools/driver/Driver.cpp:677:3 #<!-- -->52 0x000055c3e8b642e6 main /home/hyubo/llvm-sand/external/llvm-project/lldb/tools/driver/Driver.cpp:887:17 #<!-- -->53 0x00007f162f02c657 __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 #<!-- -->54 0x00007f162f02c718 call_init /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../csu/libc-start.c:128:20 #<!-- -->55 0x00007f162f02c718 __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 #<!-- -->56 0x000055c3e8b5fad1 _start /home/engshare/third-party2/glibc/2.34/src/glibc-2.34/csu/../sysdeps/x86_64/start.S:118:0 Segmentation fault (core dumped) ``` This PR fix the crash and add unit test which would crash before this PR and only pass after this PR. --- Full diff: https://github.com/llvm/llvm-project/pull/175700.diff 3 Files Affected: - (modified) lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp (+2) - (modified) lldb/unittests/Language/CPlusPlus/CMakeLists.txt (+4) - (added) lldb/unittests/Language/CPlusPlus/LibStdcppTupleTest.cpp (+167) ``````````diff diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp index cf72265bfbad3..076bbbb87448d 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp @@ -66,6 +66,8 @@ lldb::ChildCacheState LibStdcppTupleSyntheticFrontEnd::Update() { size_t child_count = current_child->GetNumChildrenIgnoringErrors(); for (size_t i = 0; i < child_count; ++i) { ValueObjectSP child_sp = current_child->GetChildAtIndex(i); + if (!child_sp) + continue; llvm::StringRef name_str = child_sp->GetName().GetStringRef(); if (name_str.starts_with("std::_Tuple_impl<")) { next_child_sp = child_sp; diff --git a/lldb/unittests/Language/CPlusPlus/CMakeLists.txt b/lldb/unittests/Language/CPlusPlus/CMakeLists.txt index 1d96fcf3db1b8..e680b5db77904 100644 --- a/lldb/unittests/Language/CPlusPlus/CMakeLists.txt +++ b/lldb/unittests/Language/CPlusPlus/CMakeLists.txt @@ -1,7 +1,11 @@ add_lldb_unittest(LanguageCPlusPlusTests CPlusPlusLanguageTest.cpp + LibStdcppTupleTest.cpp LINK_LIBS + lldbCore + lldbHost lldbPluginCPlusPlusLanguage + lldbPluginTypeSystemClang LLVMTestingSupport ) diff --git a/lldb/unittests/Language/CPlusPlus/LibStdcppTupleTest.cpp b/lldb/unittests/Language/CPlusPlus/LibStdcppTupleTest.cpp new file mode 100644 index 0000000000000..9c72796e0052f --- /dev/null +++ b/lldb/unittests/Language/CPlusPlus/LibStdcppTupleTest.cpp @@ -0,0 +1,167 @@ +//===-- LibStdcppTupleTest.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Plugins/Language/CPlusPlus/LibStdcpp.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "TestingSupport/SubsystemRAII.h" +#include "TestingSupport/Symbol/ClangTestUtils.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/ValueObject/ValueObject.h" + +#include "gtest/gtest.h" + +using namespace lldb; +using namespace lldb_private; + +namespace { + +/// A minimal ValueObject mock that returns null from GetChildAtIndex for a +/// specified index. This simulates the crash scenario where GetChildAtIndex() +/// returns null due to incomplete debug info or type system errors. +class ValueObjectWithNullChild : public ValueObject { +public: + static lldb::ValueObjectSP Create(CompilerType type, ConstString name, + size_t null_child_idx) { + auto manager = ValueObjectManager::Create(); + auto *obj = + new ValueObjectWithNullChild(*manager, type, name, null_child_idx); + return obj->GetSP(); + } + + ~ValueObjectWithNullChild() override = default; + + llvm::Expected<uint64_t> GetByteSize() override { return 4; } + + lldb::ValueType GetValueType() const override { + return lldb::eValueTypeConstResult; + } + + llvm::Expected<uint32_t> CalculateNumChildren(uint32_t max) override { + auto num_or_err = m_type.GetNumChildren(true, nullptr); + if (!num_or_err) + return num_or_err.takeError(); + return *num_or_err; + } + + ConstString GetTypeName() override { return m_type.GetTypeName(); } + + ConstString GetDisplayTypeName() override { return GetTypeName(); } + + bool IsInScope() override { return true; } + +protected: + bool UpdateValue() override { + m_error.Clear(); + return true; + } + + CompilerType GetCompilerTypeImpl() override { return m_type; } + + /// This is the key method - return null for the specified child index + /// to simulate the crash scenario. + ValueObject *CreateChildAtIndex(size_t idx) override { + if (idx == m_null_child_idx) { + return nullptr; // Return null to trigger the crash scenario! + } + return ValueObject::CreateChildAtIndex(idx); + } + +private: + ValueObjectWithNullChild(ValueObjectManager &manager, CompilerType type, + ConstString name, size_t null_child_idx) + : ValueObject(nullptr, manager), m_type(type), + m_null_child_idx(null_child_idx) { + SetName(name); + } + + CompilerType m_type; + size_t m_null_child_idx; +}; + +class LibStdcppTupleTest : public ::testing::Test { +public: + SubsystemRAII<FileSystem, HostInfo> m_subsystems; + + void SetUp() override { + m_holder = std::make_unique<clang_utils::TypeSystemClangHolder>("test"); + m_type_system = m_holder->GetAST(); + } + + /// Create a struct type with a child named "std::_Tuple_impl<0, int>" + /// to trigger the tuple synthetic frontend's child iteration. + CompilerType CreateTypeWithTupleImplChild() { + // Create outer type + CompilerType outer_type = m_type_system->CreateRecordType( + m_type_system->getASTContext().getTranslationUnitDecl(), + OptionalClangModuleID(), lldb::AccessType::eAccessPublic, + "std::tuple<int>", 0, lldb::LanguageType::eLanguageTypeC_plus_plus); + + // Create inner _Tuple_impl type + CompilerType inner_type = m_type_system->CreateRecordType( + m_type_system->getASTContext().getTranslationUnitDecl(), + OptionalClangModuleID(), lldb::AccessType::eAccessPublic, + "std::_Tuple_impl<0, int>", 0, + lldb::LanguageType::eLanguageTypeC_plus_plus); + + TypeSystemClang::StartTagDeclarationDefinition(inner_type); + TypeSystemClang::CompleteTagDeclarationDefinition(inner_type); + + // Add the inner type as a field of the outer type + TypeSystemClang::StartTagDeclarationDefinition(outer_type); + m_type_system->AddFieldToRecordType(outer_type, "std::_Tuple_impl<0, int>", + inner_type, + lldb::AccessType::eAccessPublic, 0); + TypeSystemClang::CompleteTagDeclarationDefinition(outer_type); + + return outer_type; + } + + TypeSystemClang *m_type_system; + +private: + std::unique_ptr<clang_utils::TypeSystemClangHolder> m_holder; +}; + +} // anonymous namespace + +TEST_F(LibStdcppTupleTest, CreatorHandlesNullValueObject) { + auto *frontend = formatters::LibStdcppTupleSyntheticFrontEndCreator( + nullptr, lldb::ValueObjectSP()); + EXPECT_EQ(frontend, nullptr); +} + +/// This test verifies the null child handling fix. +/// It creates a ValueObject that returns null from GetChildAtIndex(0), +/// simulating the crash scenario from incomplete debug info. +/// WITHOUT the fix (null check), this test will crash with SIGSEGV. +/// WITH the fix, this test passes. +TEST_F(LibStdcppTupleTest, UpdateHandlesNullChild) { + CompilerType type = CreateTypeWithTupleImplChild(); + + // Create a ValueObject that returns null for child at index 0 + auto valobj_sp = ValueObjectWithNullChild::Create( + type, ConstString("test_tuple"), 0 /* null_child_idx */); + ASSERT_TRUE(valobj_sp); + + // Verify our mock returns null for child 0 + ASSERT_FALSE(valobj_sp->GetChildAtIndex(0)); + + // Create the frontend - this calls Update() which iterates through children. + // WITHOUT the null check fix, this crashes with SIGSEGV when trying to call + // GetName() on a null child_sp. + // WITH the fix, this succeeds because null children are skipped. + auto *frontend = + formatters::LibStdcppTupleSyntheticFrontEndCreator(nullptr, valobj_sp); + ASSERT_NE(frontend, nullptr); + + // If we get here, the null check worked + auto num_children = frontend->CalculateNumChildren(); + ASSERT_TRUE(static_cast<bool>(num_children)); + + delete frontend; +} `````````` </details> https://github.com/llvm/llvm-project/pull/175700 _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
