diff --git a/minidump/minidump.gyp b/minidump/minidump.gyp index 3135de3d..e36006c9 100644 --- a/minidump/minidump.gyp +++ b/minidump/minidump.gyp @@ -33,6 +33,8 @@ '..', ], 'sources': [ + 'minidump_byte_array_writer.cc', + 'minidump_byte_array_writer.h', 'minidump_context.h', 'minidump_context_writer.cc', 'minidump_context_writer.h', diff --git a/minidump/minidump_byte_array_writer.cc b/minidump/minidump_byte_array_writer.cc new file mode 100644 index 00000000..05b698db --- /dev/null +++ b/minidump/minidump_byte_array_writer.cc @@ -0,0 +1,73 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "minidump/minidump_byte_array_writer.h" + +#include "base/logging.h" +#include "util/file/file_writer.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpByteArrayWriter::MinidumpByteArrayWriter() + : minidump_array_(new MinidumpByteArray()) {} + +MinidumpByteArrayWriter::~MinidumpByteArrayWriter() = default; + +void MinidumpByteArrayWriter::set_data(const uint8_t* data, size_t size) { + data_.clear(); + data_.insert(data_.begin(), data, data + size); +} + +bool MinidumpByteArrayWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + size_t size = data_.size(); + if (!AssignIfInRange(&minidump_array_->length, size)) { + LOG(ERROR) << "data size " << size << " is out of range"; + return false; + } + + return true; +} + +size_t MinidumpByteArrayWriter::SizeOfObject() { + DCHECK_EQ(state(), kStateFrozen); + + return sizeof(*minidump_array_) + data_.size(); +} + +bool MinidumpByteArrayWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = minidump_array_.get(); + iov.iov_len = sizeof(*minidump_array_); + + std::vector iovecs(1, iov); + + if (!data_.empty()) { + iov.iov_base = data_.data(); + iov.iov_len = data_.size(); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +} // namespace crashpad diff --git a/minidump/minidump_byte_array_writer.h b/minidump/minidump_byte_array_writer.h new file mode 100644 index 00000000..c399f035 --- /dev/null +++ b/minidump/minidump_byte_array_writer.h @@ -0,0 +1,65 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_BYTE_ARRAY_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_BYTE_ARRAY_WRITER_H_ + +#include +#include + +#include "base/macros.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +//! \brief Writes a variable-length byte array for a minidump into a +//! \sa MinidumpByteArray. +class MinidumpByteArrayWriter final : public internal::MinidumpWritable { + public: + MinidumpByteArrayWriter(); + ~MinidumpByteArrayWriter() override; + + //! \brief Sets the data to be written. + //! + //! \note Valid in #kStateMutable. + void set_data(const std::vector& data) { data_ = data; } + + //! \brief Sets the data to be written. + //! + //! \note Valid in #kStateMutable. + void set_data(const uint8_t* data, size_t size); + + //! \brief Gets the data to be written. + //! + //! \note Valid in any state. + const std::vector& data() const { return data_; } + + protected: + // MinidumpWritable: + + bool Freeze() override; + size_t SizeOfObject() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + std::unique_ptr minidump_array_; + std::vector data_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpByteArrayWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_BYTE_ARRAY_WRITER_H_ diff --git a/minidump/minidump_byte_array_writer_test.cc b/minidump/minidump_byte_array_writer_test.cc new file mode 100644 index 00000000..f20ad35a --- /dev/null +++ b/minidump/minidump_byte_array_writer_test.cc @@ -0,0 +1,80 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "minidump/minidump_byte_array_writer.h" + +#include + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(MinidumpByteArrayWriter, Write) { + const std::vector kTests[] = { + {'h', 'e', 'l', 'l', 'o'}, + {0x42, 0x99, 0x00, 0xbe}, + {0x00}, + {}, + }; + + for (size_t i = 0; i < arraysize(kTests); ++i) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, i)); + + StringFile string_file; + + crashpad::MinidumpByteArrayWriter writer; + writer.set_data(kTests[i]); + EXPECT_TRUE(writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), + sizeof(MinidumpByteArray) + kTests[i].size()); + + auto byte_array = std::make_unique(); + EXPECT_EQ(string_file.Seek(0, SEEK_SET), 0); + string_file.Read(byte_array.get(), sizeof(*byte_array)); + + EXPECT_EQ(byte_array->length, kTests[i].size()); + + std::vector data(byte_array->length); + string_file.Read(data.data(), byte_array->length); + + EXPECT_EQ(data, kTests[i]); + } +} + +TEST(MinidumpByteArrayWriter, SetData) { + const std::vector kTests[] = { + {1, 2, 3, 4, 5}, + {0x0}, + {}, + }; + + for (size_t i = 0; i < arraysize(kTests); ++i) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, i)); + + crashpad::MinidumpByteArrayWriter writer; + writer.set_data(kTests[i].data(), kTests[i].size()); + EXPECT_EQ(writer.data(), kTests[i]); + } +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/minidump/minidump_extensions.h b/minidump/minidump_extensions.h index f3615669..ad69aecb 100644 --- a/minidump/minidump_extensions.h +++ b/minidump/minidump_extensions.h @@ -118,6 +118,17 @@ struct ALIGNAS(4) PACKED MinidumpUTF8String { uint8_t Buffer[0]; }; +//! \brief A variable-length array of bytes carried within a minidump file. +//! The data have no intrinsic type and should be interpreted according +//! to their referencing context. +struct ALIGNAS(4) PACKED MinidumpByteArray { + //! \brief The length of the #data field. + uint32_t length; + + //! \brief The bytes of data. + uint8_t data[0]; +}; + //! \brief CPU type values for MINIDUMP_SYSTEM_INFO::ProcessorArchitecture. //! //! \sa \ref PROCESSOR_ARCHITECTURE_x "PROCESSOR_ARCHITECTURE_*" diff --git a/minidump/minidump_test.gyp b/minidump/minidump_test.gyp index 80f803db..beb41519 100644 --- a/minidump/minidump_test.gyp +++ b/minidump/minidump_test.gyp @@ -29,6 +29,8 @@ '..', ], 'sources': [ + 'test/minidump_byte_array_writer_test_util.cc', + 'test/minidump_byte_array_writer_test_util.h', 'test/minidump_context_test_util.cc', 'test/minidump_context_test_util.h', 'test/minidump_file_writer_test_util.cc', @@ -62,6 +64,7 @@ '..', ], 'sources': [ + 'minidump_byte_array_writer_test.cc', 'minidump_context_writer_test.cc', 'minidump_crashpad_info_writer_test.cc', 'minidump_exception_writer_test.cc', diff --git a/minidump/test/minidump_byte_array_writer_test_util.cc b/minidump/test/minidump_byte_array_writer_test_util.cc new file mode 100644 index 00000000..01e9d0a6 --- /dev/null +++ b/minidump/test/minidump_byte_array_writer_test_util.cc @@ -0,0 +1,36 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "minidump/test/minidump_byte_array_writer_test_util.h" + +#include "minidump/test/minidump_writable_test_util.h" + +namespace crashpad { +namespace test { + +std::vector MinidumpByteArrayAtRVA(const std::string& file_contents, + RVA rva) { + auto* minidump_byte_array = + MinidumpWritableAtRVA(file_contents, rva); + if (!minidump_byte_array) { + return {}; + } + auto* data = static_cast(minidump_byte_array->data); + const uint8_t* data_end = data + minidump_byte_array->length; + return std::vector(data, data_end); +} + + +} // namespace test +} // namespace crashpad diff --git a/minidump/test/minidump_byte_array_writer_test_util.h b/minidump/test/minidump_byte_array_writer_test_util.h new file mode 100644 index 00000000..d2c926b2 --- /dev/null +++ b/minidump/test/minidump_byte_array_writer_test_util.h @@ -0,0 +1,43 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MINIDUMP_TEST_MINIDUMP_BYTE_ARRAY_WRITER_TEST_UTIL_H_ +#define MINIDUMP_TEST_MINIDUMP_BYTE_ARRAY_WRITER_TEST_UTIL_H_ + +#include +#include +#include + +#include +#include + +namespace crashpad { +namespace test { + +//! \brief Returns the bytes referenced by a MinidumpByteArray object located +//! in a minidump file at the specified RVA. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] rva The offset in the minidump file of the MinidumpByteArray. +//! +//! \return The MinidumpByteArray::data referenced by the \a rva. Note that +//! this function does not check that the data are within the bounds of +//! the \a file_contents. +std::vector MinidumpByteArrayAtRVA(const std::string& file_contents, + RVA rva); + +} // namespace test +} // namespace crashpad + +#endif // MINIDUMP_TEST_MINIDUMP_BYTE_ARRAY_WRITER_TEST_UTIL_H_