From 1e820e82b1438a52124512175a0e7c6f8d23e158 Mon Sep 17 00:00:00 2001 From: Georgii Rymar Date: Wed, 17 Jun 2020 17:42:55 +0300 Subject: [PATCH] [DebugInfo/DWARF] - Do not hang when CFI are truncated. Currently when the .eh_frame section is truncated so that CFI instructions can't be read, it is possible to enter an infinite loop. It happens because `CFIProgram::parse` does not handle errors properly. This patch fixes the issue. Differential revision: https://reviews.llvm.org/D82017 --- llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp | 108 +++++++-------- .../DebugInfo/DWARF/DWARFDebugFrameTest.cpp | 151 +++++++++++++++++++++ 2 files changed, 206 insertions(+), 53 deletions(-) diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp index 746ba37..6ffbf12 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -36,8 +36,9 @@ const uint8_t DWARF_CFI_PRIMARY_OPERAND_MASK = 0x3f; Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset, uint64_t EndOffset) { - while (*Offset < EndOffset) { - uint8_t Opcode = Data.getRelocatedValue(1, Offset); + DataExtractor::Cursor C(*Offset); + while (C && C.tell() < EndOffset) { + uint8_t Opcode = Data.getRelocatedValue(C, 1); // Some instructions have a primary opcode encoded in the top bits. uint8_t Primary = Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK; @@ -55,7 +56,7 @@ Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset, addInstruction(Primary, Op1); break; case DW_CFA_offset: - addInstruction(Primary, Op1, Data.getULEB128(Offset)); + addInstruction(Primary, Op1, Data.getULEB128(C)); break; } } else { @@ -74,19 +75,19 @@ Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset, break; case DW_CFA_set_loc: // Operands: Address - addInstruction(Opcode, Data.getRelocatedAddress(Offset)); + addInstruction(Opcode, Data.getRelocatedAddress(C)); break; case DW_CFA_advance_loc1: // Operands: 1-byte delta - addInstruction(Opcode, Data.getRelocatedValue(1, Offset)); + addInstruction(Opcode, Data.getRelocatedValue(C, 1)); break; case DW_CFA_advance_loc2: // Operands: 2-byte delta - addInstruction(Opcode, Data.getRelocatedValue(2, Offset)); + addInstruction(Opcode, Data.getRelocatedValue(C, 2)); break; case DW_CFA_advance_loc4: // Operands: 4-byte delta - addInstruction(Opcode, Data.getRelocatedValue(4, Offset)); + addInstruction(Opcode, Data.getRelocatedValue(C, 4)); break; case DW_CFA_restore_extended: case DW_CFA_undefined: @@ -95,11 +96,11 @@ Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset, case DW_CFA_def_cfa_offset: case DW_CFA_GNU_args_size: // Operands: ULEB128 - addInstruction(Opcode, Data.getULEB128(Offset)); + addInstruction(Opcode, Data.getULEB128(C)); break; case DW_CFA_def_cfa_offset_sf: // Operands: SLEB128 - addInstruction(Opcode, Data.getSLEB128(Offset)); + addInstruction(Opcode, Data.getSLEB128(C)); break; case DW_CFA_offset_extended: case DW_CFA_register: @@ -109,56 +110,57 @@ Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset, // Note: We can not embed getULEB128 directly into function // argument list. getULEB128 changes Offset and order of evaluation // for arguments is unspecified. - auto op1 = Data.getULEB128(Offset); - auto op2 = Data.getULEB128(Offset); + uint64_t op1 = Data.getULEB128(C); + uint64_t op2 = Data.getULEB128(C); + addInstruction(Opcode, op1, op2); + break; + } + case DW_CFA_offset_extended_sf: + case DW_CFA_def_cfa_sf: + case DW_CFA_val_offset_sf: { + // Operands: ULEB128, SLEB128 + // Note: see comment for the previous case + uint64_t op1 = Data.getULEB128(C); + uint64_t op2 = (uint64_t)Data.getSLEB128(C); addInstruction(Opcode, op1, op2); break; - } - case DW_CFA_offset_extended_sf: - case DW_CFA_def_cfa_sf: - case DW_CFA_val_offset_sf: { - // Operands: ULEB128, SLEB128 - // Note: see comment for the previous case - auto op1 = Data.getULEB128(Offset); - auto op2 = (uint64_t)Data.getSLEB128(Offset); - addInstruction(Opcode, op1, op2); - break; - } - case DW_CFA_def_cfa_expression: { - uint32_t ExprLength = Data.getULEB128(Offset); - addInstruction(Opcode, 0); - DataExtractor Extractor( - Data.getData().slice(*Offset, *Offset + ExprLength), - Data.isLittleEndian(), Data.getAddressSize()); - // Note. We do not pass the DWARF format to DWARFExpression, because - // DW_OP_call_ref, the only operation which depends on the format, is - // prohibited in call frame instructions, see sec. 6.4.2 in DWARFv5. - Instructions.back().Expression = - DWARFExpression(Extractor, Data.getAddressSize()); - *Offset += ExprLength; - break; - } - case DW_CFA_expression: - case DW_CFA_val_expression: { - auto RegNum = Data.getULEB128(Offset); - auto BlockLength = Data.getULEB128(Offset); - addInstruction(Opcode, RegNum, 0); - DataExtractor Extractor( - Data.getData().slice(*Offset, *Offset + BlockLength), - Data.isLittleEndian(), Data.getAddressSize()); - // Note. We do not pass the DWARF format to DWARFExpression, because - // DW_OP_call_ref, the only operation which depends on the format, is - // prohibited in call frame instructions, see sec. 6.4.2 in DWARFv5. - Instructions.back().Expression = - DWARFExpression(Extractor, Data.getAddressSize()); - *Offset += BlockLength; - break; - } + } + case DW_CFA_def_cfa_expression: { + uint64_t ExprLength = Data.getULEB128(C); + addInstruction(Opcode, 0); + StringRef Expression = Data.getBytes(C, ExprLength); + + DataExtractor Extractor(Expression, Data.isLittleEndian(), + Data.getAddressSize()); + // Note. We do not pass the DWARF format to DWARFExpression, because + // DW_OP_call_ref, the only operation which depends on the format, is + // prohibited in call frame instructions, see sec. 6.4.2 in DWARFv5. + Instructions.back().Expression = + DWARFExpression(Extractor, Data.getAddressSize()); + break; + } + case DW_CFA_expression: + case DW_CFA_val_expression: { + uint64_t RegNum = Data.getULEB128(C); + addInstruction(Opcode, RegNum, 0); + + uint64_t BlockLength = Data.getULEB128(C); + StringRef Expression = Data.getBytes(C, BlockLength); + DataExtractor Extractor(Expression, Data.isLittleEndian(), + Data.getAddressSize()); + // Note. We do not pass the DWARF format to DWARFExpression, because + // DW_OP_call_ref, the only operation which depends on the format, is + // prohibited in call frame instructions, see sec. 6.4.2 in DWARFv5. + Instructions.back().Expression = + DWARFExpression(Extractor, Data.getAddressSize()); + break; + } } } } - return Error::success(); + *Offset = C.tell(); + return C.takeError(); } namespace { diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp index a7465a8..fd9a2be 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp @@ -10,6 +10,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" +#include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" using namespace llvm; @@ -119,4 +120,154 @@ TEST(DWARFDebugFrame, DumpEH64FDE) { "cie=1111ab9a000c pc=4444abcdabcd...5555bcdebcde"); } +// Here we test how truncated Call Frame Instructions are parsed. +TEST(DWARFDebugFrame, ParseTruncatedCFITest) { + auto ParseCFI = [](dwarf::CIE &C, ArrayRef Instructions, + Optional Size = None) { + DWARFDataExtractor Data(Instructions, /*IsLittleEndian=*/true, + /*AddressSize=*/8); + uint64_t Offset = 0; + const uint64_t EndOffset = Size ? *Size : (uint64_t)Instructions.size(); + return C.cfis().parse(Data, &Offset, EndOffset); + }; + + dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false, + /*Offset=*/0x0, + /*Length=*/0xff); + + // Having an empty instructions list is fine. + EXPECT_THAT_ERROR(ParseCFI(TestCIE, {}), Succeeded()); + + // Unable to read an opcode, because the instructions list is empty, but we + // say to the parser that it is not. + EXPECT_THAT_ERROR( + ParseCFI(TestCIE, {}, /*Size=*/1), + FailedWithMessage( + "unexpected end of data at offset 0x0 while reading [0x0, 0x1)")); + + // Unable to read a truncated DW_CFA_offset instruction. + EXPECT_THAT_ERROR( + ParseCFI(TestCIE, {dwarf::DW_CFA_offset}), + FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " + "malformed uleb128, extends past end")); + + // Unable to read a truncated DW_CFA_set_loc instruction. + EXPECT_THAT_ERROR( + ParseCFI(TestCIE, {dwarf::DW_CFA_set_loc}), + FailedWithMessage( + "unexpected end of data at offset 0x1 while reading [0x1, 0x9)")); + + // Unable to read a truncated DW_CFA_advance_loc1 instruction. + EXPECT_THAT_ERROR( + ParseCFI(TestCIE, {dwarf::DW_CFA_advance_loc1}), + FailedWithMessage( + "unexpected end of data at offset 0x1 while reading [0x1, 0x2)")); + + // Unable to read a truncated DW_CFA_advance_loc2 instruction. + EXPECT_THAT_ERROR( + ParseCFI(TestCIE, {dwarf::DW_CFA_advance_loc2}), + FailedWithMessage( + "unexpected end of data at offset 0x1 while reading [0x1, 0x3)")); + + // Unable to read a truncated DW_CFA_advance_loc4 instruction. + EXPECT_THAT_ERROR( + ParseCFI(TestCIE, {dwarf::DW_CFA_advance_loc4}), + FailedWithMessage( + "unexpected end of data at offset 0x1 while reading [0x1, 0x5)")); + + // A test for an instruction with a single ULEB128 operand. + auto CheckOp_ULEB128 = [&](uint8_t Inst) { + EXPECT_THAT_ERROR( + ParseCFI(TestCIE, Inst), + FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " + "malformed uleb128, extends past end")); + }; + + for (uint8_t Inst : + {dwarf::DW_CFA_restore_extended, dwarf::DW_CFA_undefined, + dwarf::DW_CFA_same_value, dwarf::DW_CFA_def_cfa_register, + dwarf::DW_CFA_def_cfa_offset, dwarf::DW_CFA_GNU_args_size}) + CheckOp_ULEB128(Inst); + + // Unable to read a truncated DW_CFA_def_cfa_offset_sf instruction. + EXPECT_THAT_ERROR( + ParseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_offset_sf}), + FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " + "malformed sleb128, extends past end")); + + // A test for an instruction with two ULEB128 operands. + auto CheckOp_ULEB128_ULEB128 = [&](uint8_t Inst) { + EXPECT_THAT_ERROR( + ParseCFI(TestCIE, Inst), + FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " + "malformed uleb128, extends past end")); + + EXPECT_THAT_ERROR( + ParseCFI(TestCIE, {Inst, /*Op1=*/0}), + FailedWithMessage("unable to decode LEB128 at offset 0x00000002: " + "malformed uleb128, extends past end")); + }; + + for (uint8_t Inst : {dwarf::DW_CFA_offset_extended, dwarf::DW_CFA_register, + dwarf::DW_CFA_def_cfa, dwarf::DW_CFA_val_offset}) + CheckOp_ULEB128_ULEB128(Inst); + + // A test for an instruction with two operands: ULEB128, SLEB128. + auto CheckOp_ULEB128_SLEB128 = [&](uint8_t Inst) { + EXPECT_THAT_ERROR( + ParseCFI(TestCIE, Inst), + FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " + "malformed uleb128, extends past end")); + + EXPECT_THAT_ERROR( + ParseCFI(TestCIE, {Inst, /*Op1=*/0}), + FailedWithMessage("unable to decode LEB128 at offset 0x00000002: " + "malformed sleb128, extends past end")); + }; + + for (uint8_t Inst : {dwarf::DW_CFA_offset_extended_sf, + dwarf::DW_CFA_def_cfa_sf, dwarf::DW_CFA_val_offset_sf}) + CheckOp_ULEB128_SLEB128(Inst); + + // Unable to read a truncated DW_CFA_def_cfa_expression instruction. + EXPECT_THAT_ERROR( + ParseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_expression}), + FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " + "malformed uleb128, extends past end")); + EXPECT_THAT_ERROR( + ParseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_expression, + /*expression length=*/0x1}), + FailedWithMessage( + "unexpected end of data at offset 0x2 while reading [0x2, 0x3)")); + // The DW_CFA_def_cfa_expression can contain a zero length expression. + EXPECT_THAT_ERROR(ParseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_expression, + /*ExprLen=*/0}), + Succeeded()); + + // A test for an instruction with three operands: ULEB128, expression length + // (ULEB128) and expression bytes. + auto CheckOp_ULEB128_Expr = [&](uint8_t Inst) { + EXPECT_THAT_ERROR( + ParseCFI(TestCIE, {Inst}), + FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " + "malformed uleb128, extends past end")); + EXPECT_THAT_ERROR( + ParseCFI(TestCIE, {Inst, /*Op1=*/0}), + FailedWithMessage("unable to decode LEB128 at offset 0x00000002: " + "malformed uleb128, extends past end")); + // A zero length expression is fine + EXPECT_THAT_ERROR(ParseCFI(TestCIE, {Inst, + /*Op1=*/0, /*ExprLen=*/0}), + Succeeded()); + EXPECT_THAT_ERROR( + ParseCFI(TestCIE, {Inst, + /*Op1=*/0, /*ExprLen=*/1}), + FailedWithMessage( + "unexpected end of data at offset 0x3 while reading [0x3, 0x4)")); + }; + + for (uint8_t Inst : {dwarf::DW_CFA_expression, dwarf::DW_CFA_val_expression}) + CheckOp_ULEB128_Expr(Inst); +} + } // end anonymous namespace -- 2.7.4