Index: llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp =================================================================== --- llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -39,18 +39,15 @@ 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 @@ 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; + } } } Index: llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp =================================================================== --- llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp +++ 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,17 +121,68 @@ "cie=1111ab9a000c pc=4444abcdabcd...5555bcdebcde"); } +static Error 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); +}; + +TEST(DWARFDebugFrame, InvalidCFIOpcodesTest) { + llvm::DenseSet 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 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);