From 2ef15d82e9086b97a18d7acbcbca31a81450105c Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Mon, 25 Mar 2019 14:02:16 +0000 Subject: Reapply minidump changes reverted in r356806 The changes were reverted due to ubsan errors (unaligned accesses). Here I fix those errors by first copying the data into aligned storage. Besides fixing alignment issues, this also fixes reading of minidump strings on big-endian systems. llvm-svn: 356896 --- .../postmortem/minidump-new/TestMiniDumpUUID.py | 134 +++++++++++++++++++++ .../linux-arm-uuids-elf-build-id-16.dmp | Bin 0 -> 460 bytes .../linux-arm-uuids-elf-build-id-20.dmp | Bin 0 -> 468 bytes .../linux-arm-uuids-elf-build-id-zero.dmp | Bin 0 -> 492 bytes .../minidump-new/linux-arm-uuids-no-age.dmp | Bin 0 -> 470 bytes .../minidump-new/linux-arm-uuids-with-age.dmp | Bin 0 -> 470 bytes .../minidump-new/linux-arm-zero-uuids.dmp | Bin 0 -> 534 bytes .../minidump-new/macos-arm-uuids-no-age.dmp | Bin 0 -> 470 bytes .../Plugins/Process/minidump/MinidumpParser.cpp | 49 ++++++-- .../Plugins/Process/minidump/MinidumpTypes.cpp | 26 ++-- 10 files changed, 185 insertions(+), 24 deletions(-) create mode 100644 lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/TestMiniDumpUUID.py create mode 100644 lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-elf-build-id-16.dmp create mode 100644 lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-elf-build-id-20.dmp create mode 100644 lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-elf-build-id-zero.dmp create mode 100644 lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-no-age.dmp create mode 100644 lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-with-age.dmp create mode 100644 lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-zero-uuids.dmp create mode 100644 lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/macos-arm-uuids-no-age.dmp diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/TestMiniDumpUUID.py b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/TestMiniDumpUUID.py new file mode 100644 index 00000000000..1e4a98363e3 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/TestMiniDumpUUID.py @@ -0,0 +1,134 @@ +""" +Test basics of Minidump debugging. +""" + +from __future__ import print_function +from six import iteritems + +import shutil + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class MiniDumpUUIDTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + NO_DEBUG_INFO_TESTCASE = True + + def setUp(self): + super(MiniDumpUUIDTestCase, self).setUp() + self._initial_platform = lldb.DBG.GetSelectedPlatform() + + def tearDown(self): + lldb.DBG.SetSelectedPlatform(self._initial_platform) + super(MiniDumpUUIDTestCase, self).tearDown() + + def verify_module(self, module, verify_path, verify_uuid): + uuid = module.GetUUIDString() + self.assertEqual(verify_path, module.GetFileSpec().fullpath) + self.assertEqual(verify_uuid, uuid) + + def test_zero_uuid_modules(self): + """ + Test multiple modules having a MINIDUMP_MODULE.CvRecord that is valid, + but contains a PDB70 value whose age is zero and whose UUID values are + all zero. Prior to a fix all such modules would be duplicated to the + first one since the UUIDs claimed to be valid and all zeroes. Now we + ensure that the UUID is not valid for each module and that we have + each of the modules in the target after loading the core + """ + self.dbg.CreateTarget(None) + self.target = self.dbg.GetSelectedTarget() + self.process = self.target.LoadCore("linux-arm-zero-uuids.dmp") + modules = self.target.modules + self.assertEqual(2, len(modules)) + self.verify_module(modules[0], "/file/does/not/exist/a", None) + self.verify_module(modules[1], "/file/does/not/exist/b", None) + + def test_uuid_modules_no_age(self): + """ + Test multiple modules having a MINIDUMP_MODULE.CvRecord that is valid, + and contains a PDB70 value whose age is zero and whose UUID values are + valid. Ensure we decode the UUID and don't include the age field in the UUID. + """ + self.dbg.CreateTarget(None) + self.target = self.dbg.GetSelectedTarget() + self.process = self.target.LoadCore("linux-arm-uuids-no-age.dmp") + modules = self.target.modules + self.assertEqual(2, len(modules)) + self.verify_module(modules[0], "/tmp/a", "01020304-0506-0708-090A-0B0C0D0E0F10") + self.verify_module(modules[1], "/tmp/b", "0A141E28-323C-4650-5A64-6E78828C96A0") + + def test_uuid_modules_no_age_apple(self): + """ + Test multiple modules having a MINIDUMP_MODULE.CvRecord that is valid, + and contains a PDB70 value whose age is zero and whose UUID values are + valid. Ensure we decode the UUID and don't include the age field in the UUID. + Also ensure that the first uint32_t is byte swapped, along with the next + two uint16_t values. Breakpad incorrectly byte swaps these values when it + saves Darwin minidump files. + """ + self.dbg.CreateTarget(None) + self.target = self.dbg.GetSelectedTarget() + self.process = self.target.LoadCore("macos-arm-uuids-no-age.dmp") + modules = self.target.modules + self.assertEqual(2, len(modules)) + self.verify_module(modules[0], "/tmp/a", "04030201-0605-0807-090A-0B0C0D0E0F10") + self.verify_module(modules[1], "/tmp/b", "281E140A-3C32-5046-5A64-6E78828C96A0") + + def test_uuid_modules_with_age(self): + """ + Test multiple modules having a MINIDUMP_MODULE.CvRecord that is valid, + and contains a PDB70 value whose age is valid and whose UUID values are + valid. Ensure we decode the UUID and include the age field in the UUID. + """ + self.dbg.CreateTarget(None) + self.target = self.dbg.GetSelectedTarget() + self.process = self.target.LoadCore("linux-arm-uuids-with-age.dmp") + modules = self.target.modules + self.assertEqual(2, len(modules)) + self.verify_module(modules[0], "/tmp/a", "01020304-0506-0708-090A-0B0C0D0E0F10-10101010") + self.verify_module(modules[1], "/tmp/b", "0A141E28-323C-4650-5A64-6E78828C96A0-20202020") + + def test_uuid_modules_elf_build_id_16(self): + """ + Test multiple modules having a MINIDUMP_MODULE.CvRecord that is valid, + and contains a ELF build ID whose value is valid and is 16 bytes long. + """ + self.dbg.CreateTarget(None) + self.target = self.dbg.GetSelectedTarget() + self.process = self.target.LoadCore("linux-arm-uuids-elf-build-id-16.dmp") + modules = self.target.modules + self.assertEqual(2, len(modules)) + self.verify_module(modules[0], "/tmp/a", "01020304-0506-0708-090A-0B0C0D0E0F10") + self.verify_module(modules[1], "/tmp/b", "0A141E28-323C-4650-5A64-6E78828C96A0") + + def test_uuid_modules_elf_build_id_20(self): + """ + Test multiple modules having a MINIDUMP_MODULE.CvRecord that is valid, + and contains a ELF build ID whose value is valid and is 20 bytes long. + """ + self.dbg.CreateTarget(None) + self.target = self.dbg.GetSelectedTarget() + self.process = self.target.LoadCore("linux-arm-uuids-elf-build-id-20.dmp") + modules = self.target.modules + self.assertEqual(2, len(modules)) + self.verify_module(modules[0], "/tmp/a", "01020304-0506-0708-090A-0B0C0D0E0F10-11121314") + self.verify_module(modules[1], "/tmp/b", "0A141E28-323C-4650-5A64-6E78828C96A0-AAB4BEC8") + + def test_uuid_modules_elf_build_id_zero(self): + """ + Test multiple modules having a MINIDUMP_MODULE.CvRecord that is valid, + and contains a ELF build ID whose value is all zero. + """ + self.dbg.CreateTarget(None) + self.target = self.dbg.GetSelectedTarget() + self.process = self.target.LoadCore("linux-arm-uuids-elf-build-id-zero.dmp") + modules = self.target.modules + self.assertEqual(2, len(modules)) + self.verify_module(modules[0], "/not/exist/a", None) + self.verify_module(modules[1], "/not/exist/b", None) diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-elf-build-id-16.dmp b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-elf-build-id-16.dmp new file mode 100644 index 00000000000..df24fef6d38 Binary files /dev/null and b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-elf-build-id-16.dmp differ diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-elf-build-id-20.dmp b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-elf-build-id-20.dmp new file mode 100644 index 00000000000..d1a39827b5e Binary files /dev/null and b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-elf-build-id-20.dmp differ diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-elf-build-id-zero.dmp b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-elf-build-id-zero.dmp new file mode 100644 index 00000000000..238c0f97bf9 Binary files /dev/null and b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-elf-build-id-zero.dmp differ diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-no-age.dmp b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-no-age.dmp new file mode 100644 index 00000000000..1ce4ce2cffc Binary files /dev/null and b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-no-age.dmp differ diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-with-age.dmp b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-with-age.dmp new file mode 100644 index 00000000000..ee82b9bd704 Binary files /dev/null and b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-uuids-with-age.dmp differ diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-zero-uuids.dmp b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-zero-uuids.dmp new file mode 100644 index 00000000000..d8e1f4a70c9 Binary files /dev/null and b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-arm-zero-uuids.dmp differ diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/macos-arm-uuids-no-age.dmp b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/macos-arm-uuids-no-age.dmp new file mode 100644 index 00000000000..1cc6e56987a Binary files /dev/null and b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/macos-arm-uuids-no-age.dmp differ diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp index 0ec8ddcdcfb..d18244f7610 100644 --- a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp +++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp @@ -165,23 +165,46 @@ UUID MinidumpParser::GetModuleUUID(const MinidumpModule *module) { static_cast(static_cast(*signature)); if (cv_signature == CvSignature::Pdb70) { - // PDB70 record const CvRecordPdb70 *pdb70_uuid = nullptr; Status error = consumeObject(cv_record, pdb70_uuid); - if (!error.Fail()) { - auto arch = GetArchitecture(); - // For Apple targets we only need a 16 byte UUID so that we can match - // the UUID in the Module to actual UUIDs from the built binaries. The - // "Age" field is zero in breakpad minidump files for Apple targets, so - // we restrict the UUID to the "Uuid" field so we have a UUID we can use - // to match. - if (arch.GetTriple().getVendor() == llvm::Triple::Apple) - return UUID::fromData(pdb70_uuid->Uuid, sizeof(pdb70_uuid->Uuid)); - else - return UUID::fromData(pdb70_uuid, sizeof(*pdb70_uuid)); + if (error.Fail()) + return UUID(); + // If the age field is not zero, then include the entire pdb70_uuid struct + if (pdb70_uuid->Age != 0) + return UUID::fromData(pdb70_uuid, sizeof(*pdb70_uuid)); + + // Many times UUIDs are all zeroes. This can cause more than one module + // to claim it has a valid UUID of all zeroes and causes the files to all + // merge into the first module that claims this valid zero UUID. + bool all_zeroes = true; + for (size_t i = 0; all_zeroes && i < sizeof(pdb70_uuid->Uuid); ++i) + all_zeroes = pdb70_uuid->Uuid[i] == 0; + if (all_zeroes) + return UUID(); + + if (GetArchitecture().GetTriple().getVendor() == llvm::Triple::Apple) { + // Breakpad incorrectly byte swaps the first 32 bit and next 2 16 bit + // values in the UUID field. Undo this so we can match things up + // with our symbol files + uint8_t apple_uuid[16]; + // Byte swap the first 32 bits + apple_uuid[0] = pdb70_uuid->Uuid[3]; + apple_uuid[1] = pdb70_uuid->Uuid[2]; + apple_uuid[2] = pdb70_uuid->Uuid[1]; + apple_uuid[3] = pdb70_uuid->Uuid[0]; + // Byte swap the next 16 bit value + apple_uuid[4] = pdb70_uuid->Uuid[5]; + apple_uuid[5] = pdb70_uuid->Uuid[4]; + // Byte swap the next 16 bit value + apple_uuid[6] = pdb70_uuid->Uuid[7]; + apple_uuid[7] = pdb70_uuid->Uuid[6]; + for (size_t i = 8; i < sizeof(pdb70_uuid->Uuid); ++i) + apple_uuid[i] = pdb70_uuid->Uuid[i]; + return UUID::fromData(apple_uuid, sizeof(apple_uuid)); } + return UUID::fromData(pdb70_uuid->Uuid, sizeof(pdb70_uuid->Uuid)); } else if (cv_signature == CvSignature::ElfBuildId) - return UUID::fromData(cv_record); + return UUID::fromOptionalData(cv_record); return UUID(); } diff --git a/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp b/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp index 5c010cdd981..4abf36efe56 100644 --- a/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp +++ b/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp @@ -37,26 +37,30 @@ llvm::Optional lldb_private::minidump::parseMinidumpString(llvm::ArrayRef &data) { std::string result; - const uint32_t *source_length_ptr; - Status error = consumeObject(data, source_length_ptr); + const llvm::support::ulittle32_t *source_length; + Status error = consumeObject(data, source_length); - // Copy non-aligned source_length data into aligned memory. - uint32_t source_length; - std::memcpy(&source_length, source_length_ptr, sizeof(source_length)); - - if (error.Fail() || source_length > data.size() || source_length % 2 != 0) + if (error.Fail() || *source_length > data.size() || *source_length % 2 != 0) return llvm::None; - auto source_start = reinterpret_cast(data.data()); + auto *source_start = + reinterpret_cast(data.data()); // source_length is the length of the string in bytes we need the length of // the string in UTF-16 characters/code points (16 bits per char) that's why // it's divided by 2 - const auto source_end = source_start + source_length / 2; + uint32_t utf16_length = *source_length / 2; + + // Correct the endianness and alignment of the string. + llvm::SmallVector utf16(utf16_length, 0); + std::copy_n(source_start, utf16_length, utf16.begin()); + + const llvm::UTF16 *utf16_start = utf16.begin(); + // resize to worst case length - result.resize(UNI_MAX_UTF8_BYTES_PER_CODE_POINT * source_length / 2); + result.resize(UNI_MAX_UTF8_BYTES_PER_CODE_POINT * utf16_length); auto result_start = reinterpret_cast(&result[0]); const auto result_end = result_start + result.size(); - llvm::ConvertUTF16toUTF8(&source_start, source_end, &result_start, result_end, + llvm::ConvertUTF16toUTF8(&utf16_start, utf16.end(), &result_start, result_end, llvm::strictConversion); const auto result_size = std::distance(reinterpret_cast(&result[0]), result_start); -- cgit v1.2.1