// 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 #include #include #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(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 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 new_map( new IOSIntermediateDumpMap()); if (parent->GetType() == Type::kMap) { const auto parent_map = static_cast(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(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(); 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(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 data(value_length); if (!reader->ReadExactly(data.data(), value_length)) { return false; } auto parent_map = static_cast(parent); if (parent_map->map_.find(key) != parent_map->map_.end()) { LOG(ERROR) << "Inserting duplicate key"; } parent_map->map_[key] = std::make_unique(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