1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 02:52:53 +02:00

[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
This commit is contained in:
Georgii Rymar 2020-06-17 17:42:55 +03:00
parent 32f8985d4a
commit e2b7bdef1d
2 changed files with 206 additions and 53 deletions

View File

@ -36,8 +36,9 @@ const uint8_t DWARF_CFI_PRIMARY_OPERAND_MASK = 0x3f;
Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset, Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset,
uint64_t EndOffset) { uint64_t EndOffset) {
while (*Offset < EndOffset) { DataExtractor::Cursor C(*Offset);
uint8_t Opcode = Data.getRelocatedValue(1, Offset); while (C && C.tell() < EndOffset) {
uint8_t Opcode = Data.getRelocatedValue(C, 1);
// Some instructions have a primary opcode encoded in the top bits. // Some instructions have a primary opcode encoded in the top bits.
uint8_t Primary = Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK; uint8_t Primary = Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK;
@ -55,7 +56,7 @@ Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset,
addInstruction(Primary, Op1); addInstruction(Primary, Op1);
break; break;
case DW_CFA_offset: case DW_CFA_offset:
addInstruction(Primary, Op1, Data.getULEB128(Offset)); addInstruction(Primary, Op1, Data.getULEB128(C));
break; break;
} }
} else { } else {
@ -74,19 +75,19 @@ Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset,
break; break;
case DW_CFA_set_loc: case DW_CFA_set_loc:
// Operands: Address // Operands: Address
addInstruction(Opcode, Data.getRelocatedAddress(Offset)); addInstruction(Opcode, Data.getRelocatedAddress(C));
break; break;
case DW_CFA_advance_loc1: case DW_CFA_advance_loc1:
// Operands: 1-byte delta // Operands: 1-byte delta
addInstruction(Opcode, Data.getRelocatedValue(1, Offset)); addInstruction(Opcode, Data.getRelocatedValue(C, 1));
break; break;
case DW_CFA_advance_loc2: case DW_CFA_advance_loc2:
// Operands: 2-byte delta // Operands: 2-byte delta
addInstruction(Opcode, Data.getRelocatedValue(2, Offset)); addInstruction(Opcode, Data.getRelocatedValue(C, 2));
break; break;
case DW_CFA_advance_loc4: case DW_CFA_advance_loc4:
// Operands: 4-byte delta // Operands: 4-byte delta
addInstruction(Opcode, Data.getRelocatedValue(4, Offset)); addInstruction(Opcode, Data.getRelocatedValue(C, 4));
break; break;
case DW_CFA_restore_extended: case DW_CFA_restore_extended:
case DW_CFA_undefined: 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_def_cfa_offset:
case DW_CFA_GNU_args_size: case DW_CFA_GNU_args_size:
// Operands: ULEB128 // Operands: ULEB128
addInstruction(Opcode, Data.getULEB128(Offset)); addInstruction(Opcode, Data.getULEB128(C));
break; break;
case DW_CFA_def_cfa_offset_sf: case DW_CFA_def_cfa_offset_sf:
// Operands: SLEB128 // Operands: SLEB128
addInstruction(Opcode, Data.getSLEB128(Offset)); addInstruction(Opcode, Data.getSLEB128(C));
break; break;
case DW_CFA_offset_extended: case DW_CFA_offset_extended:
case DW_CFA_register: case DW_CFA_register:
@ -109,8 +110,8 @@ Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset,
// Note: We can not embed getULEB128 directly into function // Note: We can not embed getULEB128 directly into function
// argument list. getULEB128 changes Offset and order of evaluation // argument list. getULEB128 changes Offset and order of evaluation
// for arguments is unspecified. // for arguments is unspecified.
auto op1 = Data.getULEB128(Offset); uint64_t op1 = Data.getULEB128(C);
auto op2 = Data.getULEB128(Offset); uint64_t op2 = Data.getULEB128(C);
addInstruction(Opcode, op1, op2); addInstruction(Opcode, op1, op2);
break; break;
} }
@ -119,46 +120,47 @@ Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset,
case DW_CFA_val_offset_sf: { case DW_CFA_val_offset_sf: {
// Operands: ULEB128, SLEB128 // Operands: ULEB128, SLEB128
// Note: see comment for the previous case // Note: see comment for the previous case
auto op1 = Data.getULEB128(Offset); uint64_t op1 = Data.getULEB128(C);
auto op2 = (uint64_t)Data.getSLEB128(Offset); uint64_t op2 = (uint64_t)Data.getSLEB128(C);
addInstruction(Opcode, op1, op2); addInstruction(Opcode, op1, op2);
break; break;
} }
case DW_CFA_def_cfa_expression: { case DW_CFA_def_cfa_expression: {
uint32_t ExprLength = Data.getULEB128(Offset); uint64_t ExprLength = Data.getULEB128(C);
addInstruction(Opcode, 0); addInstruction(Opcode, 0);
DataExtractor Extractor( StringRef Expression = Data.getBytes(C, ExprLength);
Data.getData().slice(*Offset, *Offset + ExprLength),
Data.isLittleEndian(), Data.getAddressSize()); DataExtractor Extractor(Expression, Data.isLittleEndian(),
Data.getAddressSize());
// Note. We do not pass the DWARF format to DWARFExpression, because // Note. We do not pass the DWARF format to DWARFExpression, because
// DW_OP_call_ref, the only operation which depends on the format, is // 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. // prohibited in call frame instructions, see sec. 6.4.2 in DWARFv5.
Instructions.back().Expression = Instructions.back().Expression =
DWARFExpression(Extractor, Data.getAddressSize()); DWARFExpression(Extractor, Data.getAddressSize());
*Offset += ExprLength;
break; break;
} }
case DW_CFA_expression: case DW_CFA_expression:
case DW_CFA_val_expression: { case DW_CFA_val_expression: {
auto RegNum = Data.getULEB128(Offset); uint64_t RegNum = Data.getULEB128(C);
auto BlockLength = Data.getULEB128(Offset);
addInstruction(Opcode, RegNum, 0); addInstruction(Opcode, RegNum, 0);
DataExtractor Extractor(
Data.getData().slice(*Offset, *Offset + BlockLength), uint64_t BlockLength = Data.getULEB128(C);
Data.isLittleEndian(), Data.getAddressSize()); StringRef Expression = Data.getBytes(C, BlockLength);
DataExtractor Extractor(Expression, Data.isLittleEndian(),
Data.getAddressSize());
// Note. We do not pass the DWARF format to DWARFExpression, because // Note. We do not pass the DWARF format to DWARFExpression, because
// DW_OP_call_ref, the only operation which depends on the format, is // 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. // prohibited in call frame instructions, see sec. 6.4.2 in DWARFv5.
Instructions.back().Expression = Instructions.back().Expression =
DWARFExpression(Extractor, Data.getAddressSize()); DWARFExpression(Extractor, Data.getAddressSize());
*Offset += BlockLength;
break; break;
} }
} }
} }
} }
return Error::success(); *Offset = C.tell();
return C.takeError();
} }
namespace { namespace {

View File

@ -10,6 +10,7 @@
#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/Dwarf.h" #include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
using namespace llvm; using namespace llvm;
@ -119,4 +120,154 @@ TEST(DWARFDebugFrame, DumpEH64FDE) {
"cie=1111ab9a000c pc=4444abcdabcd...5555bcdebcde"); "cie=1111ab9a000c pc=4444abcdabcd...5555bcdebcde");
} }
// Here we test how truncated Call Frame Instructions are parsed.
TEST(DWARFDebugFrame, ParseTruncatedCFITest) {
auto ParseCFI = [](dwarf::CIE &C, ArrayRef<uint8_t> Instructions,
Optional<uint64_t> 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 } // end anonymous namespace