https://github.com/MrEven132 updated https://github.com/llvm/llvm-project/pull/219149
>From 9b58abcdc394cad11e330a95988fa7ce3f082105 Mon Sep 17 00:00:00 2001 From: MrEven132 <[email protected]> Date: Thu, 27 Aug 2026 16:54:01 +0800 Subject: [PATCH 1/5] [llvm][lldb] Handle oversized LEB128 operands in DWARF expressions --- lldb/source/Expression/DWARFExpression.cpp | 5 ++ .../Expression/DWARFExpressionTest.cpp | 72 ++++++++++++++++ .../DWARF/LowLevel/DWARFExpression.h | 5 ++ .../DWARF/DWARFExpressionPrinter.cpp | 5 ++ .../DWARF/LowLevel/DWARFExpression.cpp | 83 +++++++++++++++++-- .../DWARFExpressionCompactPrinterTest.cpp | 51 ++++++++++++ 6 files changed, 213 insertions(+), 8 deletions(-) diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index e62b6945dc3ed..cf8e2967cb4f7 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -1447,6 +1447,11 @@ llvm::Expected<Value> DWARFExpression::Evaluate( DW_OP_value_to_name(opcode)); } + if (op->isOperandError()) + return llvm::createStringError( + "unable to decode operands for %s at offset 0x%" PRIx64, + DW_OP_value_to_name(opcode), op_offset); + if (std::optional<unsigned> arity = OperationArity(opcode)) { if (stack.size() < *arity) return llvm::createStringError( diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp index 76d2d4efcf557..02fd560372a3c 100644 --- a/lldb/unittests/Expression/DWARFExpressionTest.cpp +++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp @@ -733,6 +733,78 @@ TEST(DWARFExpression, GenericBinaryOpsAllowDifferentSignedness) { ExpectScalar(4)); } +TEST(DWARFExpression, OversizedLEB128Constants) { + auto evaluate = [](uint8_t opcode, uint8_t low_byte, + uint8_t expected_literal) { + std::vector<uint8_t> expr = { + opcode, + // These operands encode positive 2^64 plus the low seven bits. + low_byte, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02, + static_cast<uint8_t>(DW_OP_lit0 + expected_literal), DW_OP_eq, + DW_OP_stack_value}; + DataExtractor extractor(expr.data(), expr.size(), lldb::eByteOrderLittle, + /*addr_size=*/8); + return DWARFExpression::Evaluate( + /*exe_ctx=*/nullptr, /*reg_ctx=*/nullptr, /*module_sp=*/{}, extractor, + /*unit=*/nullptr, lldb::eRegisterKindLLDB, + /*initial_value_ptr=*/nullptr, /*object_address_ptr=*/nullptr); + }; + + // Generic constants are address-sized, so the high 2^64 bit is discarded. + // The comparisons also verify that the complete LEB128 operands are consumed. + EXPECT_THAT_EXPECTED(evaluate(DW_OP_constu, 0x80, 0), + ExpectScalar(64, 1, false)); + EXPECT_THAT_EXPECTED(evaluate(DW_OP_consts, 0x80, 0), + ExpectScalar(64, 1, false)); + EXPECT_THAT_EXPECTED(evaluate(DW_OP_constu, 0x85, 5), + ExpectScalar(64, 1, false)); + EXPECT_THAT_EXPECTED(evaluate(DW_OP_consts, 0x85, 5), + ExpectScalar(64, 1, false)); +} + +TEST(DWARFExpression, RejectsOversizedLEB128Operand) { + auto evaluate = [](llvm::ArrayRef<uint8_t> expr) { + DataExtractor extractor(expr.data(), expr.size(), lldb::eByteOrderLittle, + /*addr_size=*/8); + return DWARFExpression::Evaluate( + /*exe_ctx=*/nullptr, /*reg_ctx=*/nullptr, /*module_sp=*/{}, extractor, + /*unit=*/nullptr, lldb::eRegisterKindLLDB, + /*initial_value_ptr=*/nullptr, /*object_address_ptr=*/nullptr); + }; + + std::vector<uint8_t> oversized_uleb = {DW_OP_lit0, + DW_OP_plus_uconst, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x02, + DW_OP_stack_value}; + EXPECT_THAT_EXPECTED( + evaluate(oversized_uleb), + llvm::FailedWithMessage( + "unable to decode operands for DW_OP_plus_uconst at offset 0x1")); + + const uint8_t oversized_sleb[] = {DW_OP_breg0, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x02, DW_OP_stack_value}; + EXPECT_THAT_EXPECTED( + evaluate(oversized_sleb), + llvm::FailedWithMessage( + "unable to decode operands for DW_OP_breg0 at offset 0x0")); + + const uint8_t unterminated_constant[] = {DW_OP_constu, 0x80}; + EXPECT_THAT_EXPECTED( + evaluate(unterminated_constant), + llvm::FailedWithMessage( + "unable to decode operands for DW_OP_constu at offset 0x0")); +} + TEST(DWARFExpression, RelationalOpsProduceGenericResult) { struct TestCase { uint8_t opcode; diff --git a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h index 2bf448fb21708..724b3aee5584c 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h +++ b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h @@ -90,6 +90,7 @@ class DWARFExpression { uint8_t Opcode; ///< The Op Opcode, DW_OP_<something>. Description Desc; bool Error = false; + bool OperandError = false; uint64_t EndOffset; SmallVector<uint64_t> Operands; SmallVector<uint64_t> OperandEndOffsets; @@ -109,6 +110,10 @@ class DWARFExpression { } uint64_t getEndOffset() const { return EndOffset; } bool isError() const { return Error; } + /// Whether a known operation failed because an operand could not be + /// decoded. Unknown operations can be in the general error state without + /// having an operand error. + bool isOperandError() const { return Error && OperandError; } private: LLVM_ABI bool extract(DataExtractor Data, uint8_t AddressSize, diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp index 7a80ae027db1e..782b150e14c42 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp @@ -297,6 +297,11 @@ static bool printCompactDWARFExpr( while (I != E) { const DWARFExpression::Operation &Op = *I; + if (Op.isOperandError()) { + OS << "<decoding error>"; + return false; + } + uint8_t Opcode = Op.getCode(); switch (Opcode) { case dwarf::DW_OP_regx: { diff --git a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp index a0b5c42fa78cb..97df3bc6d8bb7 100644 --- a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp +++ b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp @@ -7,8 +7,10 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h" +#include "llvm/Support/Error.h" #include <cassert> #include <cstdint> +#include <optional> #include <vector> using namespace llvm; @@ -158,9 +160,54 @@ static Desc getSubOpDesc(unsigned Opcode, unsigned SubOpcode) { return getDescImpl(Descriptions, SubOpcode); } +static std::optional<uint64_t> extractLEB128(const DataExtractor &Data, + uint64_t &Offset, bool Signed) { + DataExtractor::Cursor Cursor(Offset); + uint64_t Value = Signed ? static_cast<uint64_t>(Data.getSLEB128(Cursor)) + : Data.getULEB128(Cursor); + if (!Cursor) { + consumeError(Cursor.takeError()); + return std::nullopt; + } + Offset = Cursor.tell(); + return Value; +} + +static std::optional<uint64_t> extractGenericConstant(const DataExtractor &Data, + uint64_t &Offset, + uint8_t AddressSize, + bool Signed) { + if (std::optional<uint64_t> Value = extractLEB128(Data, Offset, Signed)) + return Value; + + // Generic constants are truncated to the target's address-sized generic + // type. If the mathematical value does not fit the decoder's 64-bit return + // type, scan the complete operand while retaining only those low bits. + StringRef Bytes = Data.getData(); + const unsigned BitSize = AddressSize * 8; + unsigned Shift = 0; + uint64_t Value = 0; + while (Offset < Bytes.size()) { + uint8_t Byte = static_cast<uint8_t>(Bytes[Offset++]); + if (Shift < BitSize) { + unsigned RemainingBits = BitSize - Shift; + uint64_t Slice = Byte & 0x7f; + if (RemainingBits < 7) + Slice &= (uint64_t(1) << RemainingBits) - 1; + Value |= Slice << Shift; + } + if ((Byte & 0x80) == 0) + return Value; + if (Shift < BitSize) + Shift += 7; + } + return std::nullopt; +} + bool DWARFExpression::Operation::extract(DataExtractor Data, uint8_t AddressSize, uint64_t Offset, std::optional<DwarfFormat> Format) { + OperandError = false; EndOffset = Offset; Opcode = Data.getU8(&Offset); @@ -173,11 +220,21 @@ bool DWARFExpression::Operation::extract(DataExtractor Data, for (unsigned Operand = 0; Operand < Desc.Op.size(); ++Operand) { unsigned Size = Desc.Op[Operand]; unsigned Signed = Size & Operation::SignBit; + auto ExtractLEBOperand = [&](bool IsSigned) { + std::optional<uint64_t> Value = extractLEB128(Data, Offset, IsSigned); + if (!Value) { + OperandError = true; + return false; + } + Operands[Operand] = *Value; + return true; + }; switch (Size & ~Operation::SignBit) { case Operation::SizeSubOpLEB: assert(Operand == 0 && "SubOp operand must be the first operand"); - Operands[Operand] = Data.getULEB128(&Offset); + if (!ExtractLEBOperand(/*IsSigned=*/false)) + return false; Desc = getSubOpDesc(Opcode, Operands[Operand]); if (Desc.Version == Operation::DwarfNA) return false; @@ -214,17 +271,26 @@ bool DWARFExpression::Operation::extract(DataExtractor Data, Data.getUnsigned(&Offset, dwarf::getDwarfOffsetByteSize(*Format)); break; case Operation::SizeLEB: - if (Signed) - Operands[Operand] = Data.getSLEB128(&Offset); - else - Operands[Operand] = Data.getULEB128(&Offset); + if (Opcode == DW_OP_constu || Opcode == DW_OP_consts) { + if (std::optional<uint64_t> Value = extractGenericConstant( + Data, Offset, AddressSize, /*Signed=*/Signed)) + Operands[Operand] = *Value; + else { + OperandError = true; + return false; + } + } else if (!ExtractLEBOperand(Signed)) { + return false; + } break; case Operation::BaseTypeRef: - Operands[Operand] = Data.getULEB128(&Offset); + if (!ExtractLEBOperand(/*IsSigned=*/false)) + return false; break; case Operation::NvidiaMuxArg: assert(Operand == 1); - Operands[Operand] = Data.getULEB128(&Offset); + if (!ExtractLEBOperand(/*IsSigned=*/false)) + return false; // The selector names an NVIDIA specific operation, and the number and // type of the operands that follow it are implied by that operation. // No NVIDIA operation is known here, so where this operation ends is @@ -239,7 +305,8 @@ bool DWARFExpression::Operation::extract(DataExtractor Data, case 1: case 2: case 4: - Operands[Operand] = Data.getULEB128(&Offset); + if (!ExtractLEBOperand(/*IsSigned=*/false)) + return false; break; case 3: // global as uint32 Operands[Operand] = Data.getU32(&Offset); diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp index ff2f45334dbfc..48978cfb5d5c4 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp @@ -290,6 +290,57 @@ TEST(NVIDIAMux, Full_DW_OP_LLVM_NVIDIA_mux_MissingSelector) { EXPECT_EQ(OS.str(), "<decoding error> e9 0d"); } +TEST(LEB128Operands, OversizedGenericConstants) { + constexpr uint8_t Opcodes[] = {DW_OP_constu, DW_OP_consts}; + for (uint8_t Opcode : Opcodes) { + const uint8_t Enc[] = {Opcode, + // Positive 2^64 + 5 as either ULEB128 or SLEB128. + 0x85, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x02, DW_OP_stack_value}; + DataExtractor DE(Enc, true); + DWARFExpression Expr(DE, 8); + + auto It = Expr.begin(); + ASSERT_FALSE(It->isError()); + EXPECT_FALSE(It->isOperandError()); + EXPECT_EQ(It->getRawOperand(0), 5u); + EXPECT_EQ(It->getEndOffset(), 11u); + ++It; + ASSERT_FALSE(It->isError()); + EXPECT_EQ(It->getCode(), DW_OP_stack_value); + } +} + +TEST(LEB128Operands, OversizedNonGenericOperand) { + const uint8_t Enc[] = {DW_OP_plus_uconst, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x80, + 0x02, + DW_OP_stack_value}; + DataExtractor DE(Enc, true); + DWARFExpression Expr(DE, 8); + + auto It = Expr.begin(); + EXPECT_TRUE(It->isError()); + EXPECT_TRUE(It->isOperandError()); + ++It; + EXPECT_EQ(It, Expr.end()); +} + +TEST_F(DWARFExpressionCompactPrinterTest, + OversizedNonGenericOperandFailsCompactPrinting) { + TestExprPrinterFailure( + {DW_OP_breg0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02}, + "<decoding error>"); +} + // NVPTX packs virtual register names into DWARF register numbers, so compact // printing without a callback must recover the name and return true. TEST(NVPTXPackedRegister, Compact_DW_OP_regx_NoMRI) { >From d95351de0c0543302ba03ea5098aa31d0e00e0a4 Mon Sep 17 00:00:00 2001 From: MrEven132 <[email protected]> Date: Tue, 1 Sep 2026 15:40:35 +0800 Subject: [PATCH 2/5] [llvm][lldb] Handle oversized LEB128 operands in DWARF expressions Use checked signed and unsigned LEB128 helpers and centralize operand decoding failures. Reject errored DWARF expression operations when building the variable address map, preventing malformed locations from being indexed using stale operands. Add regression tests for errors in both the initial address operation and the optional DW_OP_plus_uconst operation. --- llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp | 4 +- .../DWARF/LowLevel/DWARFExpression.cpp | 77 ++++++++++++------- .../DebugInfo/DWARF/DWARFDebugInfoTest.cpp | 61 +++++++++++++++ 3 files changed, 111 insertions(+), 31 deletions(-) diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp index 99c13c3ba5799..54b23ae87c4a7 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -807,7 +807,7 @@ void DWARFUnit::updateVariableDieMap(DWARFDie Die) { DataExtractor Data(Location.Expr, isLittleEndian()); DWARFExpression Expr(Data, AddressSize); auto It = Expr.begin(); - if (It == Expr.end()) + if (It == Expr.end() || It->isError()) continue; // Match exactly the main sequence used to describe global variables: @@ -830,7 +830,7 @@ void DWARFUnit::updateVariableDieMap(DWARFDie Die) { // Read the optional 2nd operand, a DW_OP_plus_uconst. if (++It != Expr.end()) { - if (It->getCode() != dwarf::DW_OP_plus_uconst) + if (It->isError() || It->getCode() != dwarf::DW_OP_plus_uconst) continue; LocationAddr += It->getRawOperand(0); diff --git a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp index 97df3bc6d8bb7..05bfe7e3036bd 100644 --- a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp +++ b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp @@ -160,11 +160,8 @@ static Desc getSubOpDesc(unsigned Opcode, unsigned SubOpcode) { return getDescImpl(Descriptions, SubOpcode); } -static std::optional<uint64_t> extractLEB128(const DataExtractor &Data, - uint64_t &Offset, bool Signed) { - DataExtractor::Cursor Cursor(Offset); - uint64_t Value = Signed ? static_cast<uint64_t>(Data.getSLEB128(Cursor)) - : Data.getULEB128(Cursor); +static std::optional<uint64_t> finishLEB128(DataExtractor::Cursor &Cursor, + uint64_t &Offset, uint64_t Value) { if (!Cursor) { consumeError(Cursor.takeError()); return std::nullopt; @@ -173,13 +170,23 @@ static std::optional<uint64_t> extractLEB128(const DataExtractor &Data, return Value; } -static std::optional<uint64_t> extractGenericConstant(const DataExtractor &Data, - uint64_t &Offset, - uint8_t AddressSize, - bool Signed) { - if (std::optional<uint64_t> Value = extractLEB128(Data, Offset, Signed)) - return Value; +static std::optional<uint64_t> extractULEB128(const DataExtractor &Data, + uint64_t &Offset) { + DataExtractor::Cursor Cursor(Offset); + uint64_t Value = Data.getULEB128(Cursor); + return finishLEB128(Cursor, Offset, Value); +} +static std::optional<uint64_t> extractSLEB128(const DataExtractor &Data, + uint64_t &Offset) { + DataExtractor::Cursor Cursor(Offset); + uint64_t Value = static_cast<uint64_t>(Data.getSLEB128(Cursor)); + return finishLEB128(Cursor, Offset, Value); +} + +static std::optional<uint64_t> +extractTruncatedGenericConstant(const DataExtractor &Data, uint64_t &Offset, + uint8_t AddressSize) { // Generic constants are truncated to the target's address-sized generic // type. If the mathematical value does not fit the decoder's 64-bit return // type, scan the complete operand while retaining only those low bits. @@ -215,25 +222,32 @@ bool DWARFExpression::Operation::extract(DataExtractor Data, if (Desc.Version == Operation::DwarfNA) return false; + auto FailOperandDecode = [&] { + OperandError = true; + return false; + }; Operands.resize(Desc.Op.size()); OperandEndOffsets.resize(Desc.Op.size()); for (unsigned Operand = 0; Operand < Desc.Op.size(); ++Operand) { unsigned Size = Desc.Op[Operand]; unsigned Signed = Size & Operation::SignBit; - auto ExtractLEBOperand = [&](bool IsSigned) { - std::optional<uint64_t> Value = extractLEB128(Data, Offset, IsSigned); - if (!Value) { - OperandError = true; - return false; - } + auto ExtractLEBOperand = [&](std::optional<uint64_t> Value) { + if (!Value) + return FailOperandDecode(); Operands[Operand] = *Value; return true; }; + auto ExtractUnsignedLEBOperand = [&] { + return ExtractLEBOperand(extractULEB128(Data, Offset)); + }; + auto ExtractSignedLEBOperand = [&] { + return ExtractLEBOperand(extractSLEB128(Data, Offset)); + }; switch (Size & ~Operation::SignBit) { case Operation::SizeSubOpLEB: assert(Operand == 0 && "SubOp operand must be the first operand"); - if (!ExtractLEBOperand(/*IsSigned=*/false)) + if (!ExtractUnsignedLEBOperand()) return false; Desc = getSubOpDesc(Opcode, Operands[Operand]); if (Desc.Version == Operation::DwarfNA) @@ -272,24 +286,29 @@ bool DWARFExpression::Operation::extract(DataExtractor Data, break; case Operation::SizeLEB: if (Opcode == DW_OP_constu || Opcode == DW_OP_consts) { - if (std::optional<uint64_t> Value = extractGenericConstant( - Data, Offset, AddressSize, /*Signed=*/Signed)) - Operands[Operand] = *Value; - else { - OperandError = true; + std::optional<uint64_t> Value = Opcode == DW_OP_consts + ? extractSLEB128(Data, Offset) + : extractULEB128(Data, Offset); + if (!Value) + Value = extractTruncatedGenericConstant(Data, Offset, AddressSize); + if (!Value) + return FailOperandDecode(); + Operands[Operand] = *Value; + } else if (Signed) { + if (!ExtractSignedLEBOperand()) + return false; + } else { + if (!ExtractUnsignedLEBOperand()) return false; - } - } else if (!ExtractLEBOperand(Signed)) { - return false; } break; case Operation::BaseTypeRef: - if (!ExtractLEBOperand(/*IsSigned=*/false)) + if (!ExtractUnsignedLEBOperand()) return false; break; case Operation::NvidiaMuxArg: assert(Operand == 1); - if (!ExtractLEBOperand(/*IsSigned=*/false)) + if (!ExtractUnsignedLEBOperand()) return false; // The selector names an NVIDIA specific operation, and the number and // type of the operands that follow it are implied by that operation. @@ -305,7 +324,7 @@ bool DWARFExpression::Operation::extract(DataExtractor Data, case 1: case 2: case 4: - if (!ExtractLEBOperand(/*IsSigned=*/false)) + if (!ExtractUnsignedLEBOperand()) return false; break; case 3: // global as uint32 diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp index 373a58d259af5..f5c9134d50037 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp @@ -2230,6 +2230,67 @@ TEST(DWARFDebugInfo, TestDWARFDieRangeInfoIntersects) { AssertRangesIntersect(Ranges, {{0x20, 0x21}, {0x2f, 0x31}}); } +namespace { + +void ExpectVariableLocationRejected( + ArrayRef<uint8_t> Expression, uint64_t QueryAddress, + std::optional<uint64_t> AddressTableEntry = std::nullopt) { + Triple Triple = getDefaultTargetTripleForAddrSize(/*AddrSize=*/8); + if (!isConfigurationSupported(Triple)) + GTEST_SKIP(); + + auto ExpectedDG = dwarfgen::Generator::create(Triple, /*Version=*/5); + ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded()); + dwarfgen::Generator *DG = ExpectedDG.get().get(); + dwarfgen::CompileUnit &CU = DG->addCompileUnit(); + dwarfgen::DIE CUDie = CU.getUnitDIE(); + + if (AddressTableEntry) { + CUDie.addAddrBaseAttribute(); + CUDie.addAttribute(DW_AT_low_pc, DW_FORM_addrx, *AddressTableEntry); + } + + dwarfgen::DIE Variable = CUDie.addChild(DW_TAG_variable); + Variable.addAttribute(DW_AT_location, DW_FORM_exprloc, Expression.data(), + Expression.size()); + + MemoryBufferRef FileBuffer(DG->generate(), "dwarf"); + auto Obj = object::ObjectFile::createObjectFile(FileBuffer); + ASSERT_TRUE((bool)Obj); + std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj); + DWARFCompileUnit *U = cast<DWARFCompileUnit>(DwarfContext->getUnitAtIndex(0)); + + EXPECT_FALSE(U->getVariableForAddress(QueryAddress).isValid()); +} + +} // namespace + +#ifdef NO_SUPPORT_DEBUG_ADDR +TEST(DWARFDebugInfo, DISABLED_VariableLocationsRejectFirstOperandErrors) { +#else +TEST(DWARFDebugInfo, VariableLocationsRejectFirstOperandErrors) { +#endif + constexpr uint64_t AddrxAddress = 0x1010101010101010; + const uint8_t InvalidAddrxExpr[] = { + DW_OP_addrx, + // The operand encodes 2^64 and cannot name a .debug_addr entry. + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02}; + ExpectVariableLocationRejected(InvalidAddrxExpr, AddrxAddress, AddrxAddress); +} + +TEST(DWARFDebugInfo, VariableLocationsRejectSecondOperandErrors) { + constexpr uint64_t BaseAddress = 0x3030303030303030; + const uint8_t InvalidPlusExpr[] = { + DW_OP_addr, + // Repeated bytes make the address independent of target endianness. + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, DW_OP_plus_uconst, + // The operand encodes 2^64 and cannot be used as an address addend. + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02}; + // The iterator reuses operation storage, so the failed second operation + // retains the first operation's raw operand unless the error is rejected. + ExpectVariableLocationRejected(InvalidPlusExpr, BaseAddress * 2); +} + TEST(DWARFDebugInfo, TestDWARF64UnitLength) { static const char DebugInfoSecRaw[] = "\xff\xff\xff\xff" // DWARF64 mark >From 198c63910c2d4a20e8722151484b2548b1cb23ef Mon Sep 17 00:00:00 2001 From: MrEven132 <[email protected]> Date: Thu, 10 Sep 2026 15:59:10 +0800 Subject: [PATCH 3/5] [llvm][lldb] Simplify DWARF LEB128 operand handling Remove redundant helper wrappers and consolidate operand assignment and failure handling while keeping explicit signed and unsigned checked reads. Reuse the existing LLDB expression test helper for malformed operands. No change to production behavior. --- .../Expression/DWARFExpressionTest.cpp | 15 +--- .../DWARF/LowLevel/DWARFExpression.cpp | 71 +++++++------------ 2 files changed, 30 insertions(+), 56 deletions(-) diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp index 02fd560372a3c..221b5dd420ac5 100644 --- a/lldb/unittests/Expression/DWARFExpressionTest.cpp +++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp @@ -763,15 +763,6 @@ TEST(DWARFExpression, OversizedLEB128Constants) { } TEST(DWARFExpression, RejectsOversizedLEB128Operand) { - auto evaluate = [](llvm::ArrayRef<uint8_t> expr) { - DataExtractor extractor(expr.data(), expr.size(), lldb::eByteOrderLittle, - /*addr_size=*/8); - return DWARFExpression::Evaluate( - /*exe_ctx=*/nullptr, /*reg_ctx=*/nullptr, /*module_sp=*/{}, extractor, - /*unit=*/nullptr, lldb::eRegisterKindLLDB, - /*initial_value_ptr=*/nullptr, /*object_address_ptr=*/nullptr); - }; - std::vector<uint8_t> oversized_uleb = {DW_OP_lit0, DW_OP_plus_uconst, 0x80, @@ -786,7 +777,7 @@ TEST(DWARFExpression, RejectsOversizedLEB128Operand) { 0x02, DW_OP_stack_value}; EXPECT_THAT_EXPECTED( - evaluate(oversized_uleb), + Evaluate(oversized_uleb), llvm::FailedWithMessage( "unable to decode operands for DW_OP_plus_uconst at offset 0x1")); @@ -794,13 +785,13 @@ TEST(DWARFExpression, RejectsOversizedLEB128Operand) { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02, DW_OP_stack_value}; EXPECT_THAT_EXPECTED( - evaluate(oversized_sleb), + Evaluate(oversized_sleb), llvm::FailedWithMessage( "unable to decode operands for DW_OP_breg0 at offset 0x0")); const uint8_t unterminated_constant[] = {DW_OP_constu, 0x80}; EXPECT_THAT_EXPECTED( - evaluate(unterminated_constant), + Evaluate(unterminated_constant), llvm::FailedWithMessage( "unable to decode operands for DW_OP_constu at offset 0x0")); } diff --git a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp index 05bfe7e3036bd..6db1697c7745a 100644 --- a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp +++ b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp @@ -160,8 +160,10 @@ static Desc getSubOpDesc(unsigned Opcode, unsigned SubOpcode) { return getDescImpl(Descriptions, SubOpcode); } -static std::optional<uint64_t> finishLEB128(DataExtractor::Cursor &Cursor, - uint64_t &Offset, uint64_t Value) { +static std::optional<uint64_t> extractULEB128(const DataExtractor &Data, + uint64_t &Offset) { + DataExtractor::Cursor Cursor(Offset); + uint64_t Value = Data.getULEB128(Cursor); if (!Cursor) { consumeError(Cursor.takeError()); return std::nullopt; @@ -170,18 +172,16 @@ static std::optional<uint64_t> finishLEB128(DataExtractor::Cursor &Cursor, return Value; } -static std::optional<uint64_t> extractULEB128(const DataExtractor &Data, - uint64_t &Offset) { - DataExtractor::Cursor Cursor(Offset); - uint64_t Value = Data.getULEB128(Cursor); - return finishLEB128(Cursor, Offset, Value); -} - static std::optional<uint64_t> extractSLEB128(const DataExtractor &Data, uint64_t &Offset) { DataExtractor::Cursor Cursor(Offset); uint64_t Value = static_cast<uint64_t>(Data.getSLEB128(Cursor)); - return finishLEB128(Cursor, Offset, Value); + if (!Cursor) { + consumeError(Cursor.takeError()); + return std::nullopt; + } + Offset = Cursor.tell(); + return Value; } static std::optional<uint64_t> @@ -222,32 +222,24 @@ bool DWARFExpression::Operation::extract(DataExtractor Data, if (Desc.Version == Operation::DwarfNA) return false; - auto FailOperandDecode = [&] { - OperandError = true; - return false; - }; Operands.resize(Desc.Op.size()); OperandEndOffsets.resize(Desc.Op.size()); for (unsigned Operand = 0; Operand < Desc.Op.size(); ++Operand) { unsigned Size = Desc.Op[Operand]; unsigned Signed = Size & Operation::SignBit; - auto ExtractLEBOperand = [&](std::optional<uint64_t> Value) { - if (!Value) - return FailOperandDecode(); + auto extractLEBOperand = [&](std::optional<uint64_t> Value) { + if (!Value) { + OperandError = true; + return false; + } Operands[Operand] = *Value; return true; }; - auto ExtractUnsignedLEBOperand = [&] { - return ExtractLEBOperand(extractULEB128(Data, Offset)); - }; - auto ExtractSignedLEBOperand = [&] { - return ExtractLEBOperand(extractSLEB128(Data, Offset)); - }; switch (Size & ~Operation::SignBit) { case Operation::SizeSubOpLEB: assert(Operand == 0 && "SubOp operand must be the first operand"); - if (!ExtractUnsignedLEBOperand()) + if (!extractLEBOperand(extractULEB128(Data, Offset))) return false; Desc = getSubOpDesc(Opcode, Operands[Operand]); if (Desc.Version == Operation::DwarfNA) @@ -284,31 +276,22 @@ bool DWARFExpression::Operation::extract(DataExtractor Data, Operands[Operand] = Data.getUnsigned(&Offset, dwarf::getDwarfOffsetByteSize(*Format)); break; - case Operation::SizeLEB: - if (Opcode == DW_OP_constu || Opcode == DW_OP_consts) { - std::optional<uint64_t> Value = Opcode == DW_OP_consts - ? extractSLEB128(Data, Offset) - : extractULEB128(Data, Offset); - if (!Value) - Value = extractTruncatedGenericConstant(Data, Offset, AddressSize); - if (!Value) - return FailOperandDecode(); - Operands[Operand] = *Value; - } else if (Signed) { - if (!ExtractSignedLEBOperand()) - return false; - } else { - if (!ExtractUnsignedLEBOperand()) - return false; - } + case Operation::SizeLEB: { + std::optional<uint64_t> Value = + Signed ? extractSLEB128(Data, Offset) : extractULEB128(Data, Offset); + if (!Value && (Opcode == DW_OP_constu || Opcode == DW_OP_consts)) + Value = extractTruncatedGenericConstant(Data, Offset, AddressSize); + if (!extractLEBOperand(Value)) + return false; break; + } case Operation::BaseTypeRef: - if (!ExtractUnsignedLEBOperand()) + if (!extractLEBOperand(extractULEB128(Data, Offset))) return false; break; case Operation::NvidiaMuxArg: assert(Operand == 1); - if (!ExtractUnsignedLEBOperand()) + if (!extractLEBOperand(extractULEB128(Data, Offset))) return false; // The selector names an NVIDIA specific operation, and the number and // type of the operands that follow it are implied by that operation. @@ -324,7 +307,7 @@ bool DWARFExpression::Operation::extract(DataExtractor Data, case 1: case 2: case 4: - if (!ExtractUnsignedLEBOperand()) + if (!extractLEBOperand(extractULEB128(Data, Offset))) return false; break; case 3: // global as uint32 >From 16936face9c07f9b5964f94af3461dfa5c704854 Mon Sep 17 00:00:00 2001 From: MrEven132 <[email protected]> Date: Fri, 11 Sep 2026 11:00:05 +0800 Subject: [PATCH 4/5] [lldb] Test truncation of oversized signed DWARF constants Add a regression test for a 32-bit generic type where an oversized DW_OP_consts operand truncates to 0xffffffff and evaluates as -1. --- lldb/unittests/Expression/DWARFExpressionTest.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp index 221b5dd420ac5..855a036058fc9 100644 --- a/lldb/unittests/Expression/DWARFExpressionTest.cpp +++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp @@ -420,6 +420,20 @@ TEST(DWARFExpression, DW_OP_const) { EXPECT_THAT_EXPECTED( Evaluate({DW_OP_consts, 0x81, 0x82, 0x84, 0x88, 0x90, 0xa0, 0x40}), ExpectScalar(32, 0x01010101, true)); + + // A value wider than the 32-bit generic type is truncated before its + // signedness is applied. + const uint8_t oversized_sconst[] = { + DW_OP_consts, 0xff, 0xff, 0xff, 0xff, 0x8f, + 0x80, 0x80, 0x80, 0x80, 0x02, DW_OP_stack_value}; + DataExtractor extractor(oversized_sconst, sizeof(oversized_sconst), + lldb::eByteOrderLittle, /*addr_size=*/4); + EXPECT_THAT_EXPECTED( + DWARFExpression::Evaluate( + /*exe_ctx=*/nullptr, /*reg_ctx=*/nullptr, /*module_sp=*/{}, extractor, + /*unit=*/nullptr, lldb::eRegisterKindLLDB, + /*initial_value_ptr=*/nullptr, /*object_address_ptr=*/nullptr), + ExpectScalar(32, UINT32_MAX, true)); } TEST(DWARFExpression, DW_OP_skip) { >From 3a48e7496f14c4da5fc5b5f13ab0b522ff741322 Mon Sep 17 00:00:00 2001 From: MrEven132 <[email protected]> Date: Fri, 11 Sep 2026 17:57:34 +0800 Subject: [PATCH 5/5] [lldb] Check signedness of oversized DWARF constants Explicitly verify that a truncated DW_OP_consts value is treated as a signed 32-bit value, rather than only checking its numeric representation. --- lldb/unittests/Expression/DWARFExpressionTest.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp index 855a036058fc9..dbbb8970d167d 100644 --- a/lldb/unittests/Expression/DWARFExpressionTest.cpp +++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp @@ -428,12 +428,12 @@ TEST(DWARFExpression, DW_OP_const) { 0x80, 0x80, 0x80, 0x80, 0x02, DW_OP_stack_value}; DataExtractor extractor(oversized_sconst, sizeof(oversized_sconst), lldb::eByteOrderLittle, /*addr_size=*/4); - EXPECT_THAT_EXPECTED( - DWARFExpression::Evaluate( - /*exe_ctx=*/nullptr, /*reg_ctx=*/nullptr, /*module_sp=*/{}, extractor, - /*unit=*/nullptr, lldb::eRegisterKindLLDB, - /*initial_value_ptr=*/nullptr, /*object_address_ptr=*/nullptr), - ExpectScalar(32, UINT32_MAX, true)); + auto result = DWARFExpression::Evaluate( + /*exe_ctx=*/nullptr, /*reg_ctx=*/nullptr, /*module_sp=*/{}, extractor, + /*unit=*/nullptr, lldb::eRegisterKindLLDB, + /*initial_value_ptr=*/nullptr, /*object_address_ptr=*/nullptr); + EXPECT_THAT_EXPECTED(result, ExpectScalar(32, -1, true)); + ASSERT_TRUE(result->GetScalar().IsSigned()); } TEST(DWARFExpression, DW_OP_skip) { _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
