| // Copyright 2021 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 "util/ios/ios_intermediate_dump_reader.h" |
| |
| #include <memory> |
| #include <stack> |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "util/file/filesystem.h" |
| #include "util/ios/ios_intermediate_dump_data.h" |
| #include "util/ios/ios_intermediate_dump_format.h" |
| #include "util/ios/ios_intermediate_dump_list.h" |
| #include "util/ios/ios_intermediate_dump_object.h" |
| #include "util/ios/ios_intermediate_dump_writer.h" |
| |
| namespace crashpad { |
| namespace internal { |
| |
| bool IOSIntermediateDumpReader::Initialize(const base::FilePath& path) { |
| ScopedFileHandle handle(LoggingOpenFileForRead(path)); |
| auto reader = std::make_unique<WeakFileHandleFileReader>(handle.get()); |
| |
| // In the event a crash is introduced by this intermediate dump, don't ever |
| // read a file twice. To ensure this doesn't happen, immediately unlink. |
| LoggingRemoveFile(path); |
| |
| // Don't initialize invalid or empty files. |
| FileOffset size = LoggingFileSizeByHandle(handle.get()); |
| if (!handle.is_valid() || size == 0) { |
| return false; |
| } |
| |
| if (!Parse(reader.get(), size)) { |
| LOG(ERROR) << "Intermediate dump parsing failed"; |
| } |
| |
| return true; |
| } |
| |
| bool IOSIntermediateDumpReader::Parse(FileReaderInterface* reader, |
| FileOffset file_size) { |
| std::stack<IOSIntermediateDumpObject*> stack; |
| stack.push(&minidump_); |
| using Command = IOSIntermediateDumpWriter::CommandType; |
| using Type = IOSIntermediateDumpObject::Type; |
| |
| Command command; |
| if (!reader->ReadExactly(&command, sizeof(Command)) || |
| command != Command::kRootMapStart) { |
| LOG(ERROR) << "Unexpected start to root map."; |
| return false; |
| } |
| |
| while (reader->ReadExactly(&command, sizeof(Command))) { |
| IOSIntermediateDumpObject* parent = stack.top(); |
| switch (command) { |
| case Command::kMapStart: { |
| std::unique_ptr<IOSIntermediateDumpMap> new_map( |
| new IOSIntermediateDumpMap()); |
| if (parent->GetType() == Type::kMap) { |
| const auto parent_map = static_cast<IOSIntermediateDumpMap*>(parent); |
| stack.push(new_map.get()); |
| IntermediateDumpKey key; |
| if (!reader->ReadExactly(&key, sizeof(key))) |
| return false; |
| if (key == IntermediateDumpKey::kInvalid) |
| return false; |
| parent_map->map_[key] = std::move(new_map); |
| } else if (parent->GetType() == Type::kList) { |
| const auto parent_list = |
| static_cast<IOSIntermediateDumpList*>(parent); |
| stack.push(new_map.get()); |
| parent_list->push_back(std::move(new_map)); |
| } else { |
| LOG(ERROR) << "Unexpected parent (not a map or list)."; |
| return false; |
| } |
| break; |
| } |
| case Command::kArrayStart: { |
| auto new_list = std::make_unique<IOSIntermediateDumpList>(); |
| if (parent->GetType() != Type::kMap) { |
| LOG(ERROR) << "Attempting to push an array not in a map."; |
| return false; |
| } |
| |
| IntermediateDumpKey key; |
| if (!reader->ReadExactly(&key, sizeof(key))) |
| return false; |
| if (key == IntermediateDumpKey::kInvalid) |
| return false; |
| |
| auto parent_map = static_cast<IOSIntermediateDumpMap*>(parent); |
| stack.push(new_list.get()); |
| parent_map->map_[key] = std::move(new_list); |
| break; |
| } |
| case Command::kMapEnd: |
| if (stack.size() < 2) { |
| LOG(ERROR) << "Attempting to pop off main map."; |
| return false; |
| } |
| |
| if (parent->GetType() != Type::kMap) { |
| LOG(ERROR) << "Unexpected map end not in a map."; |
| return false; |
| } |
| stack.pop(); |
| break; |
| case Command::kArrayEnd: |
| if (stack.size() < 2) { |
| LOG(ERROR) << "Attempting to pop off main map."; |
| return false; |
| } |
| if (parent->GetType() != Type::kList) { |
| LOG(ERROR) << "Unexpected list end not in a list."; |
| return false; |
| } |
| stack.pop(); |
| break; |
| case Command::kProperty: { |
| if (parent->GetType() != Type::kMap) { |
| LOG(ERROR) << "Attempting to add a property not in a map."; |
| return false; |
| } |
| IntermediateDumpKey key; |
| if (!reader->ReadExactly(&key, sizeof(key))) |
| return false; |
| if (key == IntermediateDumpKey::kInvalid) |
| return false; |
| |
| off_t value_length; |
| if (!reader->ReadExactly(&value_length, sizeof(value_length))) { |
| return false; |
| } |
| |
| constexpr int kMaximumPropertyLength = 64 * 1024 * 1024; // 64MB. |
| if (value_length > kMaximumPropertyLength) { |
| LOG(ERROR) << "Attempting to read a property that's too big: " |
| << value_length; |
| return false; |
| } |
| |
| std::vector<uint8_t> data(value_length); |
| if (!reader->ReadExactly(data.data(), value_length)) { |
| return false; |
| } |
| auto parent_map = static_cast<IOSIntermediateDumpMap*>(parent); |
| if (parent_map->map_.find(key) != parent_map->map_.end()) { |
| LOG(ERROR) << "Inserting duplicate key"; |
| } |
| parent_map->map_[key] = |
| std::make_unique<IOSIntermediateDumpData>(std::move(data)); |
| break; |
| } |
| case Command::kRootMapEnd: { |
| if (stack.size() != 1) { |
| LOG(ERROR) << "Unexpected end of root map."; |
| return false; |
| } |
| |
| if (reader->Seek(0, SEEK_CUR) != file_size) { |
| LOG(ERROR) << "Root map ended before end of file."; |
| return false; |
| } |
| return true; |
| } |
| default: |
| LOG(ERROR) << "Failed to parse serialized intermediate minidump."; |
| return false; |
| } |
| } |
| |
| LOG(ERROR) << "Unexpected end of root map."; |
| return false; |
| } |
| |
| } // namespace internal |
| } // namespace crashpad |