[llvm] bee8cdc - [DebugInfo/DWARF] - Test invalid CFI opcodes properly and refine related `CFIProgram::parse` code.
Georgii Rymar via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 8 02:18:04 PDT 2020
Author: Georgii Rymar
Date: 2020-07-08T12:10:23+03:00
New Revision: bee8cdcabd2b3931be3f240e70b0b04e766ea4fe
URL: https://github.com/llvm/llvm-project/commit/bee8cdcabd2b3931be3f240e70b0b04e766ea4fe
DIFF: https://github.com/llvm/llvm-project/commit/bee8cdcabd2b3931be3f240e70b0b04e766ea4fe.diff
LOG: [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
Added:
Modified:
llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp
Removed:
################################################################################
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
index 6ffbf1212d47..0a1b75592290 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
@@ -39,18 +39,15 @@ Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *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;
+ if (!C)
+ 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
// bits of the opcode itself.
uint64_t Op1 = Opcode & DWARF_CFI_PRIMARY_OPERAND_MASK;
switch (Primary) {
- default:
- return createStringError(errc::illegal_byte_sequence,
- "Invalid primary CFI opcode 0x%" PRIx8,
- Primary);
case DW_CFA_advance_loc:
case DW_CFA_restore:
addInstruction(Primary, Op1);
@@ -58,104 +55,106 @@ Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset,
case DW_CFA_offset:
addInstruction(Primary, Op1, Data.getULEB128(C));
break;
- }
- } else {
- // Extended opcode - its value is Opcode itself.
- switch (Opcode) {
default:
- return createStringError(errc::illegal_byte_sequence,
- "Invalid extended CFI opcode 0x%" PRIx8,
- Opcode);
- case DW_CFA_nop:
- case DW_CFA_remember_state:
- case DW_CFA_restore_state:
- case DW_CFA_GNU_window_save:
- // No operands
- addInstruction(Opcode);
- break;
- case DW_CFA_set_loc:
- // Operands: Address
- addInstruction(Opcode, Data.getRelocatedAddress(C));
- break;
- case DW_CFA_advance_loc1:
- // Operands: 1-byte delta
- addInstruction(Opcode, Data.getRelocatedValue(C, 1));
- break;
- case DW_CFA_advance_loc2:
- // Operands: 2-byte delta
- addInstruction(Opcode, Data.getRelocatedValue(C, 2));
- break;
- case DW_CFA_advance_loc4:
- // Operands: 4-byte delta
- addInstruction(Opcode, Data.getRelocatedValue(C, 4));
- break;
- case DW_CFA_restore_extended:
- case DW_CFA_undefined:
- case DW_CFA_same_value:
- case DW_CFA_def_cfa_register:
- case DW_CFA_def_cfa_offset:
- case DW_CFA_GNU_args_size:
- // Operands: ULEB128
- addInstruction(Opcode, Data.getULEB128(C));
- break;
- case DW_CFA_def_cfa_offset_sf:
- // Operands: SLEB128
- addInstruction(Opcode, Data.getSLEB128(C));
- break;
- case DW_CFA_offset_extended:
- case DW_CFA_register:
- case DW_CFA_def_cfa:
- case DW_CFA_val_offset: {
- // Operands: ULEB128, ULEB128
- // Note: We can not embed getULEB128 directly into function
- // argument list. getULEB128 changes Offset and order of evaluation
- // for arguments is unspecified.
- 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_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;
- }
+ llvm_unreachable("invalid primary CFI opcode");
}
+ continue;
+ }
+
+ // Extended opcode - its value is Opcode itself.
+ switch (Opcode) {
+ default:
+ return createStringError(errc::illegal_byte_sequence,
+ "invalid extended CFI opcode 0x%" PRIx8, Opcode);
+ case DW_CFA_nop:
+ case DW_CFA_remember_state:
+ case DW_CFA_restore_state:
+ case DW_CFA_GNU_window_save:
+ // No operands
+ addInstruction(Opcode);
+ break;
+ case DW_CFA_set_loc:
+ // Operands: Address
+ addInstruction(Opcode, Data.getRelocatedAddress(C));
+ break;
+ case DW_CFA_advance_loc1:
+ // Operands: 1-byte delta
+ addInstruction(Opcode, Data.getRelocatedValue(C, 1));
+ break;
+ case DW_CFA_advance_loc2:
+ // Operands: 2-byte delta
+ addInstruction(Opcode, Data.getRelocatedValue(C, 2));
+ break;
+ case DW_CFA_advance_loc4:
+ // Operands: 4-byte delta
+ addInstruction(Opcode, Data.getRelocatedValue(C, 4));
+ break;
+ case DW_CFA_restore_extended:
+ case DW_CFA_undefined:
+ case DW_CFA_same_value:
+ case DW_CFA_def_cfa_register:
+ case DW_CFA_def_cfa_offset:
+ case DW_CFA_GNU_args_size:
+ // Operands: ULEB128
+ addInstruction(Opcode, Data.getULEB128(C));
+ break;
+ case DW_CFA_def_cfa_offset_sf:
+ // Operands: SLEB128
+ addInstruction(Opcode, Data.getSLEB128(C));
+ break;
+ case DW_CFA_offset_extended:
+ case DW_CFA_register:
+ case DW_CFA_def_cfa:
+ case DW_CFA_val_offset: {
+ // Operands: ULEB128, ULEB128
+ // Note: We can not embed getULEB128 directly into function
+ // argument list. getULEB128 changes Offset and order of evaluation
+ // for arguments is unspecified.
+ 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_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;
+ }
}
}
diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp
index fd9a2be8a3e9..65e2be723090 100644
--- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp
+++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/Dwarf.h"
@@ -120,65 +121,116 @@ TEST(DWARFDebugFrame, DumpEH64FDE) {
"cie=1111ab9a000c pc=4444abcdabcd...5555bcdebcde");
}
+static Error 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);
+}
+
+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,
+ /*Offset=*/0x0,
+ /*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) {
- 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());
+ 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),
+ 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}),
+ 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}),
+ 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}),
+ 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}),
+ 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}),
+ 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),
+ parseCFI(TestCIE, Inst),
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
"malformed uleb128, extends past end"));
};
@@ -191,19 +243,19 @@ TEST(DWARFDebugFrame, ParseTruncatedCFITest) {
// Unable to read a truncated DW_CFA_def_cfa_offset_sf instruction.
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: "
"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),
+ parseCFI(TestCIE, Inst),
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
"malformed uleb128, extends past end"));
EXPECT_THAT_ERROR(
- ParseCFI(TestCIE, {Inst, /*Op1=*/0}),
+ parseCFI(TestCIE, {Inst, /*Op1=*/0}),
FailedWithMessage("unable to decode LEB128 at offset 0x00000002: "
"malformed uleb128, extends past end"));
};
@@ -215,12 +267,12 @@ TEST(DWARFDebugFrame, ParseTruncatedCFITest) {
// A test for an instruction with two operands: ULEB128, SLEB128.
auto CheckOp_ULEB128_SLEB128 = [&](uint8_t Inst) {
EXPECT_THAT_ERROR(
- ParseCFI(TestCIE, Inst),
+ parseCFI(TestCIE, Inst),
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
"malformed uleb128, extends past end"));
EXPECT_THAT_ERROR(
- ParseCFI(TestCIE, {Inst, /*Op1=*/0}),
+ parseCFI(TestCIE, {Inst, /*Op1=*/0}),
FailedWithMessage("unable to decode LEB128 at offset 0x00000002: "
"malformed sleb128, extends past end"));
};
@@ -231,16 +283,16 @@ TEST(DWARFDebugFrame, ParseTruncatedCFITest) {
// Unable to read a truncated DW_CFA_def_cfa_expression instruction.
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: "
"malformed uleb128, extends past end"));
EXPECT_THAT_ERROR(
- ParseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_expression,
+ 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,
+ EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_def_cfa_expression,
/*ExprLen=*/0}),
Succeeded());
@@ -248,19 +300,19 @@ TEST(DWARFDebugFrame, ParseTruncatedCFITest) {
// (ULEB128) and expression bytes.
auto CheckOp_ULEB128_Expr = [&](uint8_t Inst) {
EXPECT_THAT_ERROR(
- ParseCFI(TestCIE, {Inst}),
+ parseCFI(TestCIE, {Inst}),
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
"malformed uleb128, extends past end"));
EXPECT_THAT_ERROR(
- ParseCFI(TestCIE, {Inst, /*Op1=*/0}),
+ 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,
+ EXPECT_THAT_ERROR(parseCFI(TestCIE, {Inst,
/*Op1=*/0, /*ExprLen=*/0}),
Succeeded());
EXPECT_THAT_ERROR(
- ParseCFI(TestCIE, {Inst,
+ parseCFI(TestCIE, {Inst,
/*Op1=*/0, /*ExprLen=*/1}),
FailedWithMessage(
"unexpected end of data at offset 0x3 while reading [0x3, 0x4)"));
More information about the llvm-commits
mailing list