From 1dc62d0358c89d3e5db970e62723fa8b0b0c56e3 Mon Sep 17 00:00:00 2001 From: James Henderson Date: Fri, 7 Feb 2020 09:18:43 +0000 Subject: [PATCH] [DebugInfo][test] Replace pre-canned binary test The DebugInfo/dwarfdump-invalid-line-table test used a pre-canned binary generated by a fuzzer to demonstrate a bug fix. Unfortunately, the binary is rigid and requires hand-editing if we change behaviour, such as rejecting certain properties within it (as I plan on doing in another change). Rather than hand-edit the binary, I have replaced it with two tests. The first tests the high-level code path from the debug line parser that produces the same error as this test previously did, and the second is a set of unit test cases that comprehensively cover the FormValue::skipValue method, which in turn covers the area that the original bug fix touched. Reviewed by: MaskRay, dblaikie Differential Revision: https://reviews.llvm.org/D74202 --- llvm/test/DebugInfo/Inputs/invalid.linetable | Bin 680 -> 0 bytes .../DebugInfo/dwarfdump-invalid-line-table.test | 5 - .../X86/Inputs/debug_line_malformed.s | 40 +++++ .../llvm-dwarfdump/X86/debug_line_invalid.test | 16 +- .../DebugInfo/DWARF/DWARFFormValueTest.cpp | 178 +++++++++++++++++++++ 5 files changed, 231 insertions(+), 8 deletions(-) delete mode 100644 llvm/test/DebugInfo/Inputs/invalid.linetable delete mode 100644 llvm/test/DebugInfo/dwarfdump-invalid-line-table.test diff --git a/llvm/test/DebugInfo/Inputs/invalid.linetable b/llvm/test/DebugInfo/Inputs/invalid.linetable deleted file mode 100644 index 5df41391afa6eaa95476541072097273c95cb45e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 680 zcmb<-^>Jea0|p5A|6l#T0RsaE1Hp;4gOt>y()9S8%ym#pEQ~)d=x$h7 zcy{iED;G9sFO8nzo4nAaW%sVT&a1X5yVTizUZCEvWb?BIKZM@ARK1k)e2~pRum$8J zQ`|mMMs^;^=^#E%A2A_(^bsWxQZkcEfL{6r3j|PrK>`6u3!ZQT8rB2U0&+mTs}EeK zBFG$Y_yJ=CtewTk)a3I5SR`;kBM9bXhG?L5kC}jKf#BcUU*{PZlo%Oyz4rQ}z`y~F zA`svP+cKY#g+TylvQTMpYCH!IBar!-c|kqU9h-n^fV^Xj4C(PDl?AEA44fb?1DcwR ntRSDwVQ_{hH~74uVVwc0jzj;a{se|RnnngrG~+>r6Hp2O6i&wQ diff --git a/llvm/test/DebugInfo/dwarfdump-invalid-line-table.test b/llvm/test/DebugInfo/dwarfdump-invalid-line-table.test deleted file mode 100644 index afcaec0..0000000 --- a/llvm/test/DebugInfo/dwarfdump-invalid-line-table.test +++ /dev/null @@ -1,5 +0,0 @@ -Verify that dwarfdump doesn't crash on invalid line table prologue. -OSS-Fuzz Issue 4644 (https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=4644) - -RUN: llvm-dwarfdump --verbose %p/Inputs/invalid.linetable 2>&1 | FileCheck %s --check-prefix=INVALID-LINE-TABLE -INVALID-LINE-TABLE: invalid directory or file table description diff --git a/llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s b/llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s index 913ec62..e01ed9a 100644 --- a/llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s +++ b/llvm/test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s @@ -383,6 +383,46 @@ .byte 0, 1, 1 # DW_LNE_end_sequence .Linvalid_md5_end1: +# V5 invalid directory content description has unsupported form. +.long .Linvalid_dir_form_end0-.Linvalid_dir_form_start0 # Length of Unit +.Linvalid_dir_form_start0: +.short 5 # DWARF version number +.byte 8 # Address Size +.byte 0 # Segment Selector Size +.long .Linvalid_dir_form_header_end0 - .Linvalid_dir_form_params0 +.Linvalid_dir_form_params0: +.byte 1 # Minimum Instruction Length +.byte 1 # Maximum Operations per Instruction +.byte 1 # Default is_stmt +.byte -5 # Line Base +.byte 14 # Line Range +.byte 13 # Opcode Base +.byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 # Standard Opcode Lengths +# Directory table format +.byte 2 # Two elements per directory entry +.byte 1 # DW_LNCT_path +.byte 0x08 # DW_FORM_string +.byte 2 # DW_LNCT_directory_index (ignored) +.byte 0x7f # Unknown form +# Directory table entries +.byte 2 # 2 directories +.asciz "/foo" # Directory name +.byte 0xff # Arbitrary data for unknown form +.asciz "/bar" # Directory name +.byte 0xff # Arbitrary data for unknown form +# File table format +.byte 1 # 1 element per file entry +.byte 1 # DW_LNCT_path +.byte 0x08 # DW_FORM_string +# File table entries +.byte 1 # 1 file +.asciz "xyz" # File names +.Linvalid_dir_form_header_end0: +.byte 0, 9, 2 # DW_LNE_set_address +.quad 0xaaaabbbbccccdddd +.byte 0, 1, 1 # DW_LNE_end_sequence +.Linvalid_dir_form_end0: + # Trailing good section. .long .Lunit_good_end - .Lunit_good_start # Length of Unit (DWARF-32 format) .Lunit_good_start: diff --git a/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test b/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test index 45bd52c..73efb39 100644 --- a/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test +++ b/llvm/test/tools/llvm-dwarfdump/X86/debug_line_invalid.test @@ -36,7 +36,7 @@ # RUN: FileCheck %s --input-file=%t-malformed-off-first.err --check-prefix=ALL ## Don't stop looking for the later unit if non-fatal issues are found. -# RUN: llvm-dwarfdump -debug-line=0x2ec %t-malformed.o 2> %t-malformed-off-last.err \ +# RUN: llvm-dwarfdump -debug-line=0x332 %t-malformed.o 2> %t-malformed-off-last.err \ # RUN: | FileCheck %s --check-prefix=LAST --implicit-check-not='debug_line[{{.*}}]' # RUN: FileCheck %s --input-file=%t-malformed-off-last.err --check-prefix=ALL @@ -143,14 +143,22 @@ ## Case 12: V5 invalid MD5 hash form when data beyond the prologue length has ## been read before the MD5 problem is identified. -# NONFATAL: debug_line[0x000002ae] +# NONFATAL: debug_line[0x000002ae] # NONFATAL-NEXT: Line table prologue # NONFATAL: include_directories[ 0] = "/tmp" # NONFATAL-NOT: file_names # NONFATAL: 0x0000000000000000 {{.*}} epilogue_begin # NONFATAL: 0x4321432143214321 {{.*}} is_stmt end_sequence -# LAST: debug_line[0x000002ec] +## Case 13: V5 invalid directory content description has unsupported form. +# NONFATAL: debug_line[0x000002ec] +# NONFATAL-NEXT: Line table prologue +# NONFATAL: include_directories[ 0] = "/foo" +# NONFATAL-NOT: include_directories +# NONFATAL-NOT: file_names +# NONFATAL: 0xaaaabbbbccccdddd {{.*}} is_stmt end_sequence + +# LAST: debug_line[0x00000332] # LAST: 0x00000000cafebabe {{.*}} end_sequence # RESERVED: warning: parsing line table prologue at offset 0x00000048 unsupported reserved unit length found of value 0xfffffffe @@ -173,4 +181,6 @@ # ALL-NEXT: warning: parsing line table prologue at 0x000002ae found an invalid directory or file table description at 0x000002e0 # ALL-NEXT: warning: failed to parse file entry because the MD5 hash is invalid # ALL-NEXT: warning: parsing line table prologue at 0x000002ae should have ended at 0x000002d9 but it ended at 0x000002e0 +# ALL-NEXT: warning: parsing line table prologue at 0x000002ec found an invalid directory or file table description at 0x00000315 +# ALL-NEXT: warning: failed to parse directory entry because skipping the form value failed. # ALL-NOT: warning: diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp index 097c160..bc86d17 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp @@ -120,4 +120,182 @@ TEST(DWARFFormValue, SignedConstantForms) { EXPECT_EQ(memcmp(Str.data(), "000102030405060708090a0b0c0d0e0f", 32), 0); } +using ParamType = std::tuple, uint32_t, bool>; + +struct FormSkipValueFixtureBase : public testing::TestWithParam { + void SetUp() { + std::tie(Fm, Version, AddrSize, Dwarf, InitialData, ExpectedSkipped, + ExpectedResult) = GetParam(); + } + + void doSkipValueTest() { + SCOPED_TRACE("Inputs: Form = " + std::to_string(Fm) + + ", Version = " + std::to_string(Version) + + ", AddrSize = " + std::to_string(uint32_t(AddrSize)) + + ", DwarfFormat = " + std::to_string(Dwarf)); + std::vector Buf(InitialData.data(), + InitialData.data() + InitialData.size()); + // The data extractor only adjusts the offset to the end of the buffer when + // attempting to read past the end, so the buffer must be bigger than the + // expected amount to be skipped to identify cases where more data than + // expected is skipped. + Buf.resize(ExpectedSkipped + 1); + DWARFDataExtractor Data(Buf, sys::IsLittleEndianHost, AddrSize); + uint64_t Offset = 0; + EXPECT_EQ(DWARFFormValue::skipValue(Fm, Data, &Offset, + {Version, AddrSize, Dwarf}), + ExpectedResult); + EXPECT_EQ(Offset, ExpectedSkipped); + } + + Form Fm; + uint16_t Version; + uint8_t AddrSize; + DwarfFormat Dwarf; + ArrayRef InitialData; + uint32_t ExpectedSkipped; + bool ExpectedResult; +}; + +const uint8_t LEBData[] = {0x80, 0x1}; +ArrayRef SampleLEB(LEBData, sizeof(LEBData)); +const uint32_t SampleLength = 0x80; +ArrayRef +SampleUnsigned(reinterpret_cast(&SampleLength), + sizeof(SampleLength)); +const uint8_t StringData[] = "abcdef"; +ArrayRef SampleString(StringData, sizeof(StringData)); +const uint8_t IndirectData8[] = {DW_FORM_data8}; +const uint8_t IndirectData16[] = {DW_FORM_data16}; +const uint8_t IndirectAddr[] = {DW_FORM_addr}; +const uint8_t IndirectIndirectData1[] = {DW_FORM_indirect, DW_FORM_data1}; +const uint8_t IndirectIndirectEnd[] = {DW_FORM_indirect}; + +// Gtest's paramterised tests only allow a maximum of 50 cases, so split the +// test into multiple identical parts to share the cases. +struct FormSkipValueFixture1 : FormSkipValueFixtureBase {}; +struct FormSkipValueFixture2 : FormSkipValueFixtureBase {}; +TEST_P(FormSkipValueFixture1, skipValuePart1) { doSkipValueTest(); } +TEST_P(FormSkipValueFixture2, skipValuePart2) { doSkipValueTest(); } + +INSTANTIATE_TEST_CASE_P( + SkipValueTestParams1, FormSkipValueFixture1, + testing::Values( + // Form, Version, AddrSize, DwarfFormat, InitialData, ExpectedSize, + // ExpectedResult. + ParamType(DW_FORM_exprloc, 0, 0, DWARF32, SampleLEB, + SampleLength + SampleLEB.size(), true), + ParamType(DW_FORM_block, 0, 0, DWARF32, SampleLEB, + SampleLength + SampleLEB.size(), true), + ParamType(DW_FORM_block1, 0, 0, DWARF32, SampleUnsigned, + SampleLength + 1, true), + ParamType(DW_FORM_block2, 0, 0, DWARF32, SampleUnsigned, + SampleLength + 2, true), + ParamType(DW_FORM_block4, 0, 0, DWARF32, SampleUnsigned, + SampleLength + 4, true), + ParamType(DW_FORM_string, 0, 0, DWARF32, SampleString, + SampleString.size(), true), + ParamType(DW_FORM_addr, 0, 42, DWARF32, SampleUnsigned, 0, false), + ParamType(DW_FORM_addr, 4, 0, DWARF32, SampleUnsigned, 0, false), + ParamType(DW_FORM_addr, 4, 42, DWARF32, SampleUnsigned, 42, true), + ParamType(DW_FORM_ref_addr, 0, 1, DWARF32, SampleUnsigned, 0, false), + ParamType(DW_FORM_ref_addr, 1, 0, DWARF32, SampleUnsigned, 0, false), + ParamType(DW_FORM_ref_addr, 1, 1, DWARF32, SampleUnsigned, 4, true), + ParamType(DW_FORM_ref_addr, 1, 1, DWARF64, SampleUnsigned, 8, true), + ParamType(DW_FORM_ref_addr, 2, 42, DWARF32, SampleUnsigned, 42, true), + ParamType(DW_FORM_ref_addr, 2, 42, DWARF64, SampleUnsigned, 42, true), + ParamType(DW_FORM_ref_addr, 3, 3, DWARF32, SampleUnsigned, 4, true), + ParamType(DW_FORM_ref_addr, 3, 3, DWARF64, SampleUnsigned, 8, true), + ParamType(DW_FORM_flag_present, 4, 4, DWARF32, SampleUnsigned, 0, true), + ParamType(DW_FORM_data1, 0, 0, DWARF32, SampleUnsigned, 1, true), + ParamType(DW_FORM_data2, 0, 0, DWARF32, SampleUnsigned, 2, true), + ParamType(DW_FORM_data4, 0, 0, DWARF32, SampleUnsigned, 4, true), + ParamType(DW_FORM_data8, 0, 0, DWARF32, SampleUnsigned, 8, true), + ParamType(DW_FORM_data16, 0, 0, DWARF32, SampleUnsigned, 16, true), + ParamType(DW_FORM_flag, 0, 0, DWARF32, SampleUnsigned, 1, true), + ParamType(DW_FORM_ref1, 0, 0, DWARF32, SampleUnsigned, 1, true), + ParamType(DW_FORM_ref2, 0, 0, DWARF32, SampleUnsigned, 2, true), + ParamType(DW_FORM_ref4, 0, 0, DWARF32, SampleUnsigned, 4, true), + ParamType(DW_FORM_ref8, 0, 0, DWARF32, SampleUnsigned, 8, true), + ParamType(DW_FORM_ref_sig8, 0, 0, DWARF32, SampleUnsigned, 8, true), + ParamType(DW_FORM_ref_sup4, 0, 0, DWARF32, SampleUnsigned, 4, true), + ParamType(DW_FORM_ref_sup8, 0, 0, DWARF32, SampleUnsigned, 8, true), + ParamType(DW_FORM_strx1, 0, 0, DWARF32, SampleUnsigned, 1, true), + ParamType(DW_FORM_strx2, 0, 0, DWARF32, SampleUnsigned, 2, true), + ParamType(DW_FORM_strx4, 0, 0, DWARF32, SampleUnsigned, 4, true), + ParamType(DW_FORM_addrx1, 0, 0, DWARF32, SampleUnsigned, 1, true), + ParamType(DW_FORM_addrx2, 0, 0, DWARF32, SampleUnsigned, 2, true), + ParamType(DW_FORM_addrx4, 0, 0, DWARF32, SampleUnsigned, 4, true), + ParamType(DW_FORM_sec_offset, 0, 1, DWARF32, SampleUnsigned, 0, false), + ParamType(DW_FORM_sec_offset, 1, 0, DWARF32, SampleUnsigned, 0, false), + ParamType(DW_FORM_sec_offset, 1, 1, DWARF32, SampleUnsigned, 4, true), + ParamType(DW_FORM_sec_offset, 1, 1, DWARF64, SampleUnsigned, 8, true), + ParamType(DW_FORM_strp, 0, 1, DWARF32, SampleUnsigned, 0, false), + ParamType(DW_FORM_strp, 1, 0, DWARF32, SampleUnsigned, 0, false), + ParamType(DW_FORM_strp, 1, 1, DWARF32, SampleUnsigned, 4, true), + ParamType(DW_FORM_strp, 1, 1, DWARF64, SampleUnsigned, 8, true), + ParamType(DW_FORM_strp_sup, 0, 1, DWARF32, SampleUnsigned, 0, false), + ParamType(DW_FORM_strp_sup, 1, 0, DWARF32, SampleUnsigned, 0, false), + ParamType(DW_FORM_strp_sup, 1, 1, DWARF32, SampleUnsigned, 4, true), + ParamType(DW_FORM_strp_sup, 1, 1, DWARF64, SampleUnsigned, 8, true)), ); + +INSTANTIATE_TEST_CASE_P( + SkipValueTestParams2, FormSkipValueFixture2, + testing::Values( + ParamType(DW_FORM_line_strp, 0, 1, DWARF32, SampleUnsigned, 0, false), + ParamType(DW_FORM_line_strp, 1, 0, DWARF32, SampleUnsigned, 0, false), + ParamType(DW_FORM_line_strp, 1, 1, DWARF32, SampleUnsigned, 4, true), + ParamType(DW_FORM_line_strp, 1, 1, DWARF64, SampleUnsigned, 8, true), + ParamType(DW_FORM_GNU_ref_alt, 0, 1, DWARF32, SampleUnsigned, 0, false), + ParamType(DW_FORM_GNU_ref_alt, 1, 0, DWARF32, SampleUnsigned, 0, false), + ParamType(DW_FORM_GNU_ref_alt, 1, 1, DWARF32, SampleUnsigned, 4, true), + ParamType(DW_FORM_GNU_ref_alt, 1, 1, DWARF64, SampleUnsigned, 8, true), + ParamType(DW_FORM_GNU_strp_alt, 0, 1, DWARF32, SampleUnsigned, 0, + false), + ParamType(DW_FORM_GNU_strp_alt, 1, 0, DWARF32, SampleUnsigned, 0, + false), + ParamType(DW_FORM_GNU_strp_alt, 1, 1, DWARF32, SampleUnsigned, 4, true), + ParamType(DW_FORM_GNU_strp_alt, 1, 1, DWARF64, SampleUnsigned, 8, true), + ParamType(DW_FORM_sdata, 0, 0, DWARF32, SampleLEB, SampleLEB.size(), + true), + ParamType(DW_FORM_udata, 0, 0, DWARF32, SampleLEB, SampleLEB.size(), + true), + ParamType(DW_FORM_ref_udata, 0, 0, DWARF32, SampleLEB, SampleLEB.size(), + true), + ParamType(DW_FORM_strx, 0, 0, DWARF32, SampleLEB, SampleLEB.size(), + true), + ParamType(DW_FORM_addrx, 0, 0, DWARF32, SampleLEB, SampleLEB.size(), + true), + ParamType(DW_FORM_loclistx, 0, 0, DWARF32, SampleLEB, SampleLEB.size(), + true), + ParamType(DW_FORM_rnglistx, 0, 0, DWARF32, SampleLEB, SampleLEB.size(), + true), + ParamType(DW_FORM_GNU_addr_index, 0, 0, DWARF32, SampleLEB, + SampleLEB.size(), true), + ParamType(DW_FORM_GNU_str_index, 0, 0, DWARF32, SampleLEB, + SampleLEB.size(), true), + ParamType(DW_FORM_indirect, 0, 0, DWARF32, + ArrayRef(IndirectData8, sizeof(IndirectData8)), 9, + true), + ParamType(DW_FORM_indirect, 0, 0, DWARF32, + ArrayRef(IndirectData16, sizeof(IndirectData16)), 17, + true), + ParamType(DW_FORM_indirect, 4, 0, DWARF32, + ArrayRef(IndirectAddr, sizeof(IndirectAddr)), 1, + false), + ParamType(DW_FORM_indirect, 4, 4, DWARF32, + ArrayRef(IndirectAddr, sizeof(IndirectAddr)), 5, + true), + ParamType(DW_FORM_indirect, 4, 4, DWARF32, + ArrayRef(IndirectIndirectData1, + sizeof(IndirectIndirectData1)), + 3, true), + ParamType(DW_FORM_indirect, 4, 4, DWARF32, + ArrayRef(IndirectIndirectEnd, + sizeof(IndirectIndirectEnd)), + 2, false), + ParamType(/*Unknown=*/Form(0xff), 4, 4, DWARF32, SampleUnsigned, 0, + false)), ); + } // end anonymous namespace -- 2.7.4