From bf9b995b29bc646053080a5011388890a377b45b Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Thu, 7 Dec 2023 14:39:45 -0800 Subject: [PATCH] [libDebugInfo] Prevent infinite recursion in DWARFDie::getTypeSize() (#74681) when run on invalid input. (cherry picked from commit c6805ea44af3bfd57e6b46f2d65ec6b0d0d6c64a) --- llvm/lib/DebugInfo/DWARF/DWARFDie.cpp | 34 ++++++++++++------ .../DebugInfo/DWARF/DWARFDebugInfoTest.cpp | 36 +++++++++++++++++++ 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp index 7af7ed8be7b4a..406298049bc13 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" @@ -487,18 +488,23 @@ void DWARFDie::getCallerFrame(uint32_t &CallFile, uint32_t &CallLine, CallDiscriminator = toUnsigned(find(DW_AT_GNU_discriminator), 0); } -std::optional DWARFDie::getTypeSize(uint64_t PointerSize) { - if (auto SizeAttr = find(DW_AT_byte_size)) +static std::optional +getTypeSizeImpl(DWARFDie Die, uint64_t PointerSize, + SmallPtrSetImpl &Visited) { + // Cycle detected? + if (!Visited.insert(Die.getDebugInfoEntry()).second) + return {}; + if (auto SizeAttr = Die.find(DW_AT_byte_size)) if (std::optional Size = SizeAttr->getAsUnsignedConstant()) return Size; - switch (getTag()) { + switch (Die.getTag()) { case DW_TAG_pointer_type: case DW_TAG_reference_type: case DW_TAG_rvalue_reference_type: return PointerSize; case DW_TAG_ptr_to_member_type: { - if (DWARFDie BaseType = getAttributeValueAsReferencedDie(DW_AT_type)) + if (DWARFDie BaseType = Die.getAttributeValueAsReferencedDie(DW_AT_type)) if (BaseType.getTag() == DW_TAG_subroutine_type) return 2 * PointerSize; return PointerSize; @@ -508,19 +514,20 @@ std::optional DWARFDie::getTypeSize(uint64_t PointerSize) { case DW_TAG_volatile_type: case DW_TAG_restrict_type: case DW_TAG_typedef: { - if (DWARFDie BaseType = getAttributeValueAsReferencedDie(DW_AT_type)) - return BaseType.getTypeSize(PointerSize); + if (DWARFDie BaseType = Die.getAttributeValueAsReferencedDie(DW_AT_type)) + return getTypeSizeImpl(BaseType, PointerSize, Visited); break; } case DW_TAG_array_type: { - DWARFDie BaseType = getAttributeValueAsReferencedDie(DW_AT_type); + DWARFDie BaseType = Die.getAttributeValueAsReferencedDie(DW_AT_type); if (!BaseType) return std::nullopt; - std::optional BaseSize = BaseType.getTypeSize(PointerSize); + std::optional BaseSize = + getTypeSizeImpl(BaseType, PointerSize, Visited); if (!BaseSize) return std::nullopt; uint64_t Size = *BaseSize; - for (DWARFDie Child : *this) { + for (DWARFDie Child : Die) { if (Child.getTag() != DW_TAG_subrange_type) continue; @@ -540,13 +547,18 @@ std::optional DWARFDie::getTypeSize(uint64_t PointerSize) { return Size; } default: - if (DWARFDie BaseType = getAttributeValueAsReferencedDie(DW_AT_type)) - return BaseType.getTypeSize(PointerSize); + if (DWARFDie BaseType = Die.getAttributeValueAsReferencedDie(DW_AT_type)) + return getTypeSizeImpl(BaseType, PointerSize, Visited); break; } return std::nullopt; } +std::optional DWARFDie::getTypeSize(uint64_t PointerSize) { + SmallPtrSet Visited; + return getTypeSizeImpl(*this, PointerSize, Visited); +} + /// Helper to dump a DIE with all of its parents, but no siblings. static unsigned dumpParentChain(DWARFDie Die, raw_ostream &OS, unsigned Indent, DIDumpOptions DumpOpts, unsigned Depth = 0) { diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp index 8f99fcc073b6d..934369bed639a 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp @@ -1660,6 +1660,42 @@ TEST(DWARFDebugInfo, TestFindRecurse) { EXPECT_EQ(AbsDieName, StringOpt.value_or(nullptr)); } +TEST(DWARFDebugInfo, TestSelfRecursiveType) { + typedef uint32_t AddrType; + Triple Triple = getDefaultTargetTripleForAddrSize(sizeof(AddrType)); + if (!isConfigurationSupported(Triple)) + GTEST_SKIP(); + + auto ExpectedDG = dwarfgen::Generator::create(Triple, 4); + ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded()); + dwarfgen::Generator *DG = ExpectedDG.get().get(); + dwarfgen::CompileUnit &CU = DG->addCompileUnit(); + dwarfgen::DIE CUDie = CU.getUnitDIE(); + + // Create an invalid self-recursive typedef. + dwarfgen::DIE TypedefDie = CUDie.addChild(DW_TAG_typedef); + TypedefDie.addAttribute(DW_AT_name, DW_FORM_strp, "illegal"); + TypedefDie.addAttribute(DW_AT_type, DW_FORM_ref_addr, TypedefDie); + + MemoryBufferRef FileBuffer(DG->generate(), "dwarf"); + auto Obj = object::ObjectFile::createObjectFile(FileBuffer); + EXPECT_TRUE((bool)Obj); + std::unique_ptr DwarfContext = DWARFContext::create(**Obj); + + // Verify the number of compile units is correct. + uint32_t NumCUs = DwarfContext->getNumCompileUnits(); + EXPECT_EQ(NumCUs, 1u); + DWARFCompileUnit *U = cast(DwarfContext->getUnitAtIndex(0)); + { + DWARFDie CUDie = U->getUnitDIE(false); + EXPECT_TRUE(CUDie.isValid()); + DWARFDie TypedefDie = CUDie.getFirstChild(); + + // Test that getTypeSize doesn't get into an infinite loop. + EXPECT_EQ(TypedefDie.getTypeSize(sizeof(AddrType)), std::nullopt); + } +} + TEST(DWARFDebugInfo, TestDwarfToFunctions) { // Test all of the dwarf::toXXX functions that take a // std::optional and extract the values from it.