mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 03:02:36 +01:00
[DebugInfo/DWARF] - Test invalid CFI opcodes properly and refine related CFIProgram::parse
code.
There are following issues with `CFIProgram::parse` code: 1) Invalid CFI opcodes were never tested. And currently a test would fail when the `LLVM_ENABLE_ABI_BREAKING_CHECKS` is enabled. It happens because the `DataExtractor::Cursor C` remains unchecked when the "Invalid extended CFI opcode" error is reported: ``` .eh_frame section at offset 0x1128 address 0x0: Program aborted due to an unhandled Error: Error value was Success. (Note: Success values must still be checked prior to being destroyed). ``` 2) It is impossible to reach the "Invalid primary CFI opcode" error with the current code. There are 3 possible primary opcode values and all of them are handled. Hence this error should be replaced with llvm_unreachable. 3) Errors currently reported are upper-case. This patch refines the code in the `CFIProgram::parse` method to fix all issues mentioned and adds unit tests for all possible invalid extended CFI opcodes. Differential revision: https://reviews.llvm.org/D82868
This commit is contained in:
parent
54223457e2
commit
6e1353517c
@ -39,18 +39,15 @@ Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset,
|
|||||||
DataExtractor::Cursor C(*Offset);
|
DataExtractor::Cursor C(*Offset);
|
||||||
while (C && C.tell() < EndOffset) {
|
while (C && C.tell() < EndOffset) {
|
||||||
uint8_t Opcode = Data.getRelocatedValue(C, 1);
|
uint8_t Opcode = Data.getRelocatedValue(C, 1);
|
||||||
// Some instructions have a primary opcode encoded in the top bits.
|
if (!C)
|
||||||
uint8_t Primary = Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK;
|
break;
|
||||||
|
|
||||||
if (Primary) {
|
// Some instructions have a primary opcode encoded in the top bits.
|
||||||
|
if (uint8_t Primary = Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK) {
|
||||||
// If it's a primary opcode, the first operand is encoded in the bottom
|
// If it's a primary opcode, the first operand is encoded in the bottom
|
||||||
// bits of the opcode itself.
|
// bits of the opcode itself.
|
||||||
uint64_t Op1 = Opcode & DWARF_CFI_PRIMARY_OPERAND_MASK;
|
uint64_t Op1 = Opcode & DWARF_CFI_PRIMARY_OPERAND_MASK;
|
||||||
switch (Primary) {
|
switch (Primary) {
|
||||||
default:
|
|
||||||
return createStringError(errc::illegal_byte_sequence,
|
|
||||||
"Invalid primary CFI opcode 0x%" PRIx8,
|
|
||||||
Primary);
|
|
||||||
case DW_CFA_advance_loc:
|
case DW_CFA_advance_loc:
|
||||||
case DW_CFA_restore:
|
case DW_CFA_restore:
|
||||||
addInstruction(Primary, Op1);
|
addInstruction(Primary, Op1);
|
||||||
@ -58,14 +55,17 @@ Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset,
|
|||||||
case DW_CFA_offset:
|
case DW_CFA_offset:
|
||||||
addInstruction(Primary, Op1, Data.getULEB128(C));
|
addInstruction(Primary, Op1, Data.getULEB128(C));
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
llvm_unreachable("invalid primary CFI opcode");
|
||||||
}
|
}
|
||||||
} else {
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Extended opcode - its value is Opcode itself.
|
// Extended opcode - its value is Opcode itself.
|
||||||
switch (Opcode) {
|
switch (Opcode) {
|
||||||
default:
|
default:
|
||||||
return createStringError(errc::illegal_byte_sequence,
|
return createStringError(errc::illegal_byte_sequence,
|
||||||
"Invalid extended CFI opcode 0x%" PRIx8,
|
"invalid extended CFI opcode 0x%" PRIx8, Opcode);
|
||||||
Opcode);
|
|
||||||
case DW_CFA_nop:
|
case DW_CFA_nop:
|
||||||
case DW_CFA_remember_state:
|
case DW_CFA_remember_state:
|
||||||
case DW_CFA_restore_state:
|
case DW_CFA_restore_state:
|
||||||
@ -157,7 +157,6 @@ Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
*Offset = C.tell();
|
*Offset = C.tell();
|
||||||
return C.takeError();
|
return C.takeError();
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "llvm/ADT/DenseSet.h"
|
||||||
#include "llvm/ADT/SmallVector.h"
|
#include "llvm/ADT/SmallVector.h"
|
||||||
#include "llvm/ADT/StringRef.h"
|
#include "llvm/ADT/StringRef.h"
|
||||||
#include "llvm/BinaryFormat/Dwarf.h"
|
#include "llvm/BinaryFormat/Dwarf.h"
|
||||||
@ -120,65 +121,116 @@ TEST(DWARFDebugFrame, DumpEH64FDE) {
|
|||||||
"cie=1111ab9a000c pc=4444abcdabcd...5555bcdebcde");
|
"cie=1111ab9a000c pc=4444abcdabcd...5555bcdebcde");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here we test how truncated Call Frame Instructions are parsed.
|
static Error parseCFI(dwarf::CIE &C, ArrayRef<uint8_t> Instructions,
|
||||||
TEST(DWARFDebugFrame, ParseTruncatedCFITest) {
|
|
||||||
auto ParseCFI = [](dwarf::CIE &C, ArrayRef<uint8_t> Instructions,
|
|
||||||
Optional<uint64_t> Size = None) {
|
Optional<uint64_t> Size = None) {
|
||||||
DWARFDataExtractor Data(Instructions, /*IsLittleEndian=*/true,
|
DWARFDataExtractor Data(Instructions, /*IsLittleEndian=*/true,
|
||||||
/*AddressSize=*/8);
|
/*AddressSize=*/8);
|
||||||
uint64_t Offset = 0;
|
uint64_t Offset = 0;
|
||||||
const uint64_t EndOffset = Size ? *Size : (uint64_t)Instructions.size();
|
const uint64_t EndOffset = Size ? *Size : (uint64_t)Instructions.size();
|
||||||
return C.cfis().parse(Data, &Offset, EndOffset);
|
return C.cfis().parse(Data, &Offset, EndOffset);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
TEST(DWARFDebugFrame, InvalidCFIOpcodesTest) {
|
||||||
|
llvm::DenseSet<uint8_t> ValidExtendedOpcodes = {
|
||||||
|
dwarf::DW_CFA_nop,
|
||||||
|
dwarf::DW_CFA_advance_loc,
|
||||||
|
dwarf::DW_CFA_offset,
|
||||||
|
dwarf::DW_CFA_restore,
|
||||||
|
dwarf::DW_CFA_set_loc,
|
||||||
|
dwarf::DW_CFA_advance_loc1,
|
||||||
|
dwarf::DW_CFA_advance_loc2,
|
||||||
|
dwarf::DW_CFA_advance_loc4,
|
||||||
|
dwarf::DW_CFA_offset_extended,
|
||||||
|
dwarf::DW_CFA_restore_extended,
|
||||||
|
dwarf::DW_CFA_undefined,
|
||||||
|
dwarf::DW_CFA_same_value,
|
||||||
|
dwarf::DW_CFA_register,
|
||||||
|
dwarf::DW_CFA_remember_state,
|
||||||
|
dwarf::DW_CFA_restore_state,
|
||||||
|
dwarf::DW_CFA_def_cfa,
|
||||||
|
dwarf::DW_CFA_def_cfa_register,
|
||||||
|
dwarf::DW_CFA_def_cfa_offset,
|
||||||
|
dwarf::DW_CFA_def_cfa_expression,
|
||||||
|
dwarf::DW_CFA_expression,
|
||||||
|
dwarf::DW_CFA_offset_extended_sf,
|
||||||
|
dwarf::DW_CFA_def_cfa_sf,
|
||||||
|
dwarf::DW_CFA_def_cfa_offset_sf,
|
||||||
|
dwarf::DW_CFA_val_offset,
|
||||||
|
dwarf::DW_CFA_val_offset_sf,
|
||||||
|
dwarf::DW_CFA_val_expression,
|
||||||
|
dwarf::DW_CFA_MIPS_advance_loc8,
|
||||||
|
dwarf::DW_CFA_GNU_window_save,
|
||||||
|
dwarf::DW_CFA_AARCH64_negate_ra_state,
|
||||||
|
dwarf::DW_CFA_GNU_args_size};
|
||||||
|
|
||||||
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
|
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
|
||||||
/*Offset=*/0x0,
|
/*Offset=*/0x0,
|
||||||
/*Length=*/0xff);
|
/*Length=*/0xff);
|
||||||
|
|
||||||
|
// See DWARF standard v3, section 7.23: low 6 bits are used to encode an
|
||||||
|
// extended opcode.
|
||||||
|
for (uint8_t Code = 0; Code <= 63; ++Code) {
|
||||||
|
if (ValidExtendedOpcodes.count(Code))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
EXPECT_THAT_ERROR(parseCFI(TestCIE, Code),
|
||||||
|
FailedWithMessage(("invalid extended CFI opcode 0x" +
|
||||||
|
Twine::utohexstr(Code))
|
||||||
|
.str()
|
||||||
|
.c_str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here we test how truncated Call Frame Instructions are parsed.
|
||||||
|
TEST(DWARFDebugFrame, ParseTruncatedCFITest) {
|
||||||
|
dwarf::CIE TestCIE = createCIE(/*IsDWARF64=*/false,
|
||||||
|
/*Offset=*/0x0,
|
||||||
|
/*Length=*/0xff);
|
||||||
|
|
||||||
// Having an empty instructions list is fine.
|
// Having an empty instructions list is fine.
|
||||||
EXPECT_THAT_ERROR(ParseCFI(TestCIE, {}), Succeeded());
|
EXPECT_THAT_ERROR(parseCFI(TestCIE, {}), Succeeded());
|
||||||
|
|
||||||
// Unable to read an opcode, because the instructions list is empty, but we
|
// Unable to read an opcode, because the instructions list is empty, but we
|
||||||
// say to the parser that it is not.
|
// say to the parser that it is not.
|
||||||
EXPECT_THAT_ERROR(
|
EXPECT_THAT_ERROR(
|
||||||
ParseCFI(TestCIE, {}, /*Size=*/1),
|
parseCFI(TestCIE, {}, /*Size=*/1),
|
||||||
FailedWithMessage(
|
FailedWithMessage(
|
||||||
"unexpected end of data at offset 0x0 while reading [0x0, 0x1)"));
|
"unexpected end of data at offset 0x0 while reading [0x0, 0x1)"));
|
||||||
|
|
||||||
// Unable to read a truncated DW_CFA_offset instruction.
|
// Unable to read a truncated DW_CFA_offset instruction.
|
||||||
EXPECT_THAT_ERROR(
|
EXPECT_THAT_ERROR(
|
||||||
ParseCFI(TestCIE, {dwarf::DW_CFA_offset}),
|
parseCFI(TestCIE, {dwarf::DW_CFA_offset}),
|
||||||
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
|
||||||
"malformed uleb128, extends past end"));
|
"malformed uleb128, extends past end"));
|
||||||
|
|
||||||
// Unable to read a truncated DW_CFA_set_loc instruction.
|
// Unable to read a truncated DW_CFA_set_loc instruction.
|
||||||
EXPECT_THAT_ERROR(
|
EXPECT_THAT_ERROR(
|
||||||
ParseCFI(TestCIE, {dwarf::DW_CFA_set_loc}),
|
parseCFI(TestCIE, {dwarf::DW_CFA_set_loc}),
|
||||||
FailedWithMessage(
|
FailedWithMessage(
|
||||||
"unexpected end of data at offset 0x1 while reading [0x1, 0x9)"));
|
"unexpected end of data at offset 0x1 while reading [0x1, 0x9)"));
|
||||||
|
|
||||||
// Unable to read a truncated DW_CFA_advance_loc1 instruction.
|
// Unable to read a truncated DW_CFA_advance_loc1 instruction.
|
||||||
EXPECT_THAT_ERROR(
|
EXPECT_THAT_ERROR(
|
||||||
ParseCFI(TestCIE, {dwarf::DW_CFA_advance_loc1}),
|
parseCFI(TestCIE, {dwarf::DW_CFA_advance_loc1}),
|
||||||
FailedWithMessage(
|
FailedWithMessage(
|
||||||
"unexpected end of data at offset 0x1 while reading [0x1, 0x2)"));
|
"unexpected end of data at offset 0x1 while reading [0x1, 0x2)"));
|
||||||
|
|
||||||
// Unable to read a truncated DW_CFA_advance_loc2 instruction.
|
// Unable to read a truncated DW_CFA_advance_loc2 instruction.
|
||||||
EXPECT_THAT_ERROR(
|
EXPECT_THAT_ERROR(
|
||||||
ParseCFI(TestCIE, {dwarf::DW_CFA_advance_loc2}),
|
parseCFI(TestCIE, {dwarf::DW_CFA_advance_loc2}),
|
||||||
FailedWithMessage(
|
FailedWithMessage(
|
||||||
"unexpected end of data at offset 0x1 while reading [0x1, 0x3)"));
|
"unexpected end of data at offset 0x1 while reading [0x1, 0x3)"));
|
||||||
|
|
||||||
// Unable to read a truncated DW_CFA_advance_loc4 instruction.
|
// Unable to read a truncated DW_CFA_advance_loc4 instruction.
|
||||||
EXPECT_THAT_ERROR(
|
EXPECT_THAT_ERROR(
|
||||||
ParseCFI(TestCIE, {dwarf::DW_CFA_advance_loc4}),
|
parseCFI(TestCIE, {dwarf::DW_CFA_advance_loc4}),
|
||||||
FailedWithMessage(
|
FailedWithMessage(
|
||||||
"unexpected end of data at offset 0x1 while reading [0x1, 0x5)"));
|
"unexpected end of data at offset 0x1 while reading [0x1, 0x5)"));
|
||||||
|
|
||||||
// A test for an instruction with a single ULEB128 operand.
|
// A test for an instruction with a single ULEB128 operand.
|
||||||
auto CheckOp_ULEB128 = [&](uint8_t Inst) {
|
auto CheckOp_ULEB128 = [&](uint8_t Inst) {
|
||||||
EXPECT_THAT_ERROR(
|
EXPECT_THAT_ERROR(
|
||||||
ParseCFI(TestCIE, Inst),
|
parseCFI(TestCIE, Inst),
|
||||||
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
|
||||||
"malformed uleb128, extends past end"));
|
"malformed uleb128, extends past end"));
|
||||||
};
|
};
|
||||||
@ -191,19 +243,19 @@ TEST(DWARFDebugFrame, ParseTruncatedCFITest) {
|
|||||||
|
|
||||||
// Unable to read a truncated DW_CFA_def_cfa_offset_sf instruction.
|
// Unable to read a truncated DW_CFA_def_cfa_offset_sf instruction.
|
||||||
EXPECT_THAT_ERROR(
|
EXPECT_THAT_ERROR(
|
||||||
ParseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_offset_sf}),
|
parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_offset_sf}),
|
||||||
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
|
||||||
"malformed sleb128, extends past end"));
|
"malformed sleb128, extends past end"));
|
||||||
|
|
||||||
// A test for an instruction with two ULEB128 operands.
|
// A test for an instruction with two ULEB128 operands.
|
||||||
auto CheckOp_ULEB128_ULEB128 = [&](uint8_t Inst) {
|
auto CheckOp_ULEB128_ULEB128 = [&](uint8_t Inst) {
|
||||||
EXPECT_THAT_ERROR(
|
EXPECT_THAT_ERROR(
|
||||||
ParseCFI(TestCIE, Inst),
|
parseCFI(TestCIE, Inst),
|
||||||
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
|
||||||
"malformed uleb128, extends past end"));
|
"malformed uleb128, extends past end"));
|
||||||
|
|
||||||
EXPECT_THAT_ERROR(
|
EXPECT_THAT_ERROR(
|
||||||
ParseCFI(TestCIE, {Inst, /*Op1=*/0}),
|
parseCFI(TestCIE, {Inst, /*Op1=*/0}),
|
||||||
FailedWithMessage("unable to decode LEB128 at offset 0x00000002: "
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000002: "
|
||||||
"malformed uleb128, extends past end"));
|
"malformed uleb128, extends past end"));
|
||||||
};
|
};
|
||||||
@ -215,12 +267,12 @@ TEST(DWARFDebugFrame, ParseTruncatedCFITest) {
|
|||||||
// A test for an instruction with two operands: ULEB128, SLEB128.
|
// A test for an instruction with two operands: ULEB128, SLEB128.
|
||||||
auto CheckOp_ULEB128_SLEB128 = [&](uint8_t Inst) {
|
auto CheckOp_ULEB128_SLEB128 = [&](uint8_t Inst) {
|
||||||
EXPECT_THAT_ERROR(
|
EXPECT_THAT_ERROR(
|
||||||
ParseCFI(TestCIE, Inst),
|
parseCFI(TestCIE, Inst),
|
||||||
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
|
||||||
"malformed uleb128, extends past end"));
|
"malformed uleb128, extends past end"));
|
||||||
|
|
||||||
EXPECT_THAT_ERROR(
|
EXPECT_THAT_ERROR(
|
||||||
ParseCFI(TestCIE, {Inst, /*Op1=*/0}),
|
parseCFI(TestCIE, {Inst, /*Op1=*/0}),
|
||||||
FailedWithMessage("unable to decode LEB128 at offset 0x00000002: "
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000002: "
|
||||||
"malformed sleb128, extends past end"));
|
"malformed sleb128, extends past end"));
|
||||||
};
|
};
|
||||||
@ -231,16 +283,16 @@ TEST(DWARFDebugFrame, ParseTruncatedCFITest) {
|
|||||||
|
|
||||||
// Unable to read a truncated DW_CFA_def_cfa_expression instruction.
|
// Unable to read a truncated DW_CFA_def_cfa_expression instruction.
|
||||||
EXPECT_THAT_ERROR(
|
EXPECT_THAT_ERROR(
|
||||||
ParseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_expression}),
|
parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_expression}),
|
||||||
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
|
||||||
"malformed uleb128, extends past end"));
|
"malformed uleb128, extends past end"));
|
||||||
EXPECT_THAT_ERROR(
|
EXPECT_THAT_ERROR(
|
||||||
ParseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_expression,
|
parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_expression,
|
||||||
/*expression length=*/0x1}),
|
/*expression length=*/0x1}),
|
||||||
FailedWithMessage(
|
FailedWithMessage(
|
||||||
"unexpected end of data at offset 0x2 while reading [0x2, 0x3)"));
|
"unexpected end of data at offset 0x2 while reading [0x2, 0x3)"));
|
||||||
// The DW_CFA_def_cfa_expression can contain a zero length expression.
|
// The DW_CFA_def_cfa_expression can contain a zero length expression.
|
||||||
EXPECT_THAT_ERROR(ParseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_expression,
|
EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_expression,
|
||||||
/*ExprLen=*/0}),
|
/*ExprLen=*/0}),
|
||||||
Succeeded());
|
Succeeded());
|
||||||
|
|
||||||
@ -248,19 +300,19 @@ TEST(DWARFDebugFrame, ParseTruncatedCFITest) {
|
|||||||
// (ULEB128) and expression bytes.
|
// (ULEB128) and expression bytes.
|
||||||
auto CheckOp_ULEB128_Expr = [&](uint8_t Inst) {
|
auto CheckOp_ULEB128_Expr = [&](uint8_t Inst) {
|
||||||
EXPECT_THAT_ERROR(
|
EXPECT_THAT_ERROR(
|
||||||
ParseCFI(TestCIE, {Inst}),
|
parseCFI(TestCIE, {Inst}),
|
||||||
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
|
||||||
"malformed uleb128, extends past end"));
|
"malformed uleb128, extends past end"));
|
||||||
EXPECT_THAT_ERROR(
|
EXPECT_THAT_ERROR(
|
||||||
ParseCFI(TestCIE, {Inst, /*Op1=*/0}),
|
parseCFI(TestCIE, {Inst, /*Op1=*/0}),
|
||||||
FailedWithMessage("unable to decode LEB128 at offset 0x00000002: "
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000002: "
|
||||||
"malformed uleb128, extends past end"));
|
"malformed uleb128, extends past end"));
|
||||||
// A zero length expression is fine
|
// A zero length expression is fine
|
||||||
EXPECT_THAT_ERROR(ParseCFI(TestCIE, {Inst,
|
EXPECT_THAT_ERROR(parseCFI(TestCIE, {Inst,
|
||||||
/*Op1=*/0, /*ExprLen=*/0}),
|
/*Op1=*/0, /*ExprLen=*/0}),
|
||||||
Succeeded());
|
Succeeded());
|
||||||
EXPECT_THAT_ERROR(
|
EXPECT_THAT_ERROR(
|
||||||
ParseCFI(TestCIE, {Inst,
|
parseCFI(TestCIE, {Inst,
|
||||||
/*Op1=*/0, /*ExprLen=*/1}),
|
/*Op1=*/0, /*ExprLen=*/1}),
|
||||||
FailedWithMessage(
|
FailedWithMessage(
|
||||||
"unexpected end of data at offset 0x3 while reading [0x3, 0x4)"));
|
"unexpected end of data at offset 0x3 while reading [0x3, 0x4)"));
|
||||||
|
Loading…
Reference in New Issue
Block a user