[lldb] reject `.debug_arange` sections with nonzero segment size
authorLuke Drummond <luke.drummond@codeplay.com>
Tue, 10 Mar 2020 15:05:53 +0000 (15:05 +0000)
committerLuke Drummond <luke.drummond@codeplay.com>
Thu, 12 Mar 2020 12:22:50 +0000 (12:22 +0000)
If a producer emits a nonzero segment size, `lldb` will silently read
incorrect values and crash, or do something worse later as the tuple
size is expected to be 2, rather than 3.

Neither LLVM, nor GCC produce segmented aranges, but this dangerous case
should still be checked and handled.

Reviewed by: clayborg, labath
Differential Revision: https://reviews.llvm.org/D75925
Subscribers: lldb-commits
Tags: #lldb

lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp
lldb/unittests/SymbolFile/DWARF/SymbolFileDWARFTests.cpp

index b3f056d..728cefe 100644 (file)
@@ -63,7 +63,8 @@ llvm::Error DWARFDebugArangeSet::extract(const DWARFDataExtractor &data,
   // 1 - the version looks good
   // 2 - the address byte size looks plausible
   // 3 - the length seems to make sense
-  // size looks plausible
+  // 4 - size looks plausible
+  // 5 - the arange tuples do not contain a segment field
   if (m_header.version < 2 || m_header.version > 5)
     return llvm::make_error<llvm::object::GenericBinaryError>(
         "Invalid arange header version");
@@ -81,6 +82,10 @@ llvm::Error DWARFDebugArangeSet::extract(const DWARFDataExtractor &data,
     return llvm::make_error<llvm::object::GenericBinaryError>(
         "Invalid arange header length");
 
+  if (m_header.seg_size)
+    return llvm::make_error<llvm::object::GenericBinaryError>(
+        "segmented arange entries are not supported");
+
   // The first tuple following the header in each set begins at an offset
   // that is a multiple of the size of a single tuple (that is, twice the
   // size of an address). The header is padded, if necessary, to the
index c971eda..f1010a1 100644 (file)
@@ -18,6 +18,7 @@
 #include "Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h"
 #include "Plugins/SymbolFile/DWARF/DWARFDataExtractor.h"
 #include "Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h"
 #include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h"
 #include "Plugins/SymbolFile/PDB/SymbolFilePDB.h"
 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
@@ -310,3 +311,38 @@ TEST_F(SymbolFileDWARFTests, TestAbbrevMissingTerminator) {
   EXPECT_EQ("abbreviation declaration attribute list not terminated with a "
             "null entry", llvm::toString(std::move(error)));
 }
+
+TEST_F(SymbolFileDWARFTests, ParseArangesNonzeroSegmentSize) {
+  // This `.debug_aranges` table header is a valid 32bit big-endian section
+  // according to the DWARFv5 spec:6.2.1, but contains segment selectors which
+  // are not supported by lldb, and should be gracefully rejected
+  const unsigned char binary_data[] = {
+      0, 0, 0, 41, // unit_length (length field not including this field itself)
+      0, 2,        // DWARF version number (half)
+      0, 0, 0, 0, // offset into the .debug_info_table (ignored for the purposes
+                  // of this test
+      4,          // address size
+      1,          // segment size
+      // alignment for the first tuple which "begins at an offset that is a
+      // multiple of the size of a single tuple". Tuples are nine bytes in this
+      // example.
+      0, 0, 0, 0, 0, 0,
+      // BEGIN TUPLES
+      1, 0, 0, 0, 4, 0, 0, 0,
+      1, // a 1byte object starting at address 4 in segment 1
+      0, 0, 0, 0, 4, 0, 0, 0,
+      1, // a 1byte object starting at address 4 in segment 0
+      // END TUPLES
+      0, 0, 0, 0, 0, 0, 0, 0, 0 // terminator
+  };
+  DWARFDataExtractor data;
+  data.SetData(static_cast<const void *>(binary_data), sizeof binary_data,
+               lldb::ByteOrder::eByteOrderBig);
+  DWARFDebugArangeSet debug_aranges;
+  offset_t off = 0;
+  llvm::Error error = debug_aranges.extract(data, &off);
+  EXPECT_TRUE(bool(error));
+  EXPECT_EQ("segmented arange entries are not supported",
+            llvm::toString(std::move(error)));
+  EXPECT_EQ(off, 12); // Parser should read no further than the segment size
+}