mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 12:41:49 +01: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:
parent
32f8985d4a
commit
e2b7bdef1d
@ -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
|
||||
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_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_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 {
|
||||
|
@ -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<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
|
||||
|
Loading…
x
Reference in New Issue
Block a user