// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include "base/files/file_path.h" #include "base/files/memory_mapped_file.h" #include "base/logging.h" #include "base/macros.h" #include "base/strings/string_piece.h" #include "ipc/ipc_message.h" #include "tools/ipc_fuzzer/message_lib/message_cracker.h" #include "tools/ipc_fuzzer/message_lib/message_file.h" #include "tools/ipc_fuzzer/message_lib/message_file_format.h" #include "tools/ipc_fuzzer/message_lib/message_names.h" namespace ipc_fuzzer { namespace { // Helper class to read IPC message file into a MessageVector and // fix message types. class Reader { public: Reader(const base::FilePath& path); bool Read(MessageVector* messages); private: template bool CutObject(const T** object); // Reads the header, checks magic and version. bool ReadHeader(); bool MapFile(); bool ReadMessages(); // Last part of the file is a string table for message names. bool ReadStringTable(); // Reads type <-> name mapping into name_map_. References string table. bool ReadNameTable(); // Removes obsolete messages from the vector. bool RemoveUnknownMessages(); // Does type -> name -> correct_type fixup. void FixMessageTypes(); // Raw data. base::FilePath path_; base::MemoryMappedFile mapped_file_; base::StringPiece file_data_; base::StringPiece string_table_; // Parsed data. const FileHeader* header_; MessageVector* messages_; MessageNames name_map_; DISALLOW_COPY_AND_ASSIGN(Reader); }; Reader::Reader(const base::FilePath& path) : path_(path), header_(NULL), messages_(NULL) { } template bool Reader::CutObject(const T** object) { if (file_data_.size() < sizeof(T)) { LOG(ERROR) << "Unexpected EOF."; return false; } *object = reinterpret_cast(file_data_.data()); file_data_.remove_prefix(sizeof(T)); return true; } bool Reader::ReadHeader() { if (!CutObject(&header_)) return false; if (header_->magic != FileHeader::kMagicValue) { LOG(ERROR) << path_.value() << " is not an IPC message file."; return false; } if (header_->version != FileHeader::kCurrentVersion) { LOG(ERROR) << "Wrong version for message file " << path_.value() << ". " << "File version is " << header_->version << ", " << "current version is " << FileHeader::kCurrentVersion << "."; return false; } return true; } bool Reader::MapFile() { if (!mapped_file_.Initialize(path_)) { LOG(ERROR) << "Failed to map testcase: " << path_.value(); return false; } const char* data = reinterpret_cast(mapped_file_.data()); file_data_.set(data, mapped_file_.length()); return true; } bool Reader::ReadMessages() { for (size_t i = 0; i < header_->message_count; ++i) { const char* begin = file_data_.begin(); const char* end = file_data_.end(); IPC::Message::NextMessageInfo info; IPC::Message::FindNext(begin, end, &info); if (!info.message_found) { LOG(ERROR) << "Failed to parse message."; return false; } CHECK_EQ(info.message_end, info.pickle_end); size_t msglen = info.message_end - begin; if (msglen > INT_MAX) { LOG(ERROR) << "Message too large."; return false; } // Copy is necessary to fix message type later. IPC::Message const_message(begin, msglen); IPC::Message* message = new IPC::Message(const_message); messages_->push_back(message); file_data_.remove_prefix(msglen); } return true; } bool Reader::ReadStringTable() { size_t name_count = header_->name_count; if (!name_count) return true; if (name_count > file_data_.size() / sizeof(NameTableEntry)) { LOG(ERROR) << "Invalid name table size: " << name_count; return false; } size_t string_table_offset = name_count * sizeof(NameTableEntry); string_table_ = file_data_.substr(string_table_offset); if (string_table_.empty()) { LOG(ERROR) << "Missing string table."; return false; } if (string_table_.end()[-1] != '\0') { LOG(ERROR) << "String table doesn't end with NUL."; return false; } return true; } bool Reader::ReadNameTable() { for (size_t i = 0; i < header_->name_count; ++i) { const NameTableEntry* entry; if (!CutObject(&entry)) return false; size_t offset = entry->string_table_offset; if (offset >= string_table_.size()) { LOG(ERROR) << "Invalid string table offset: " << offset; return false; } name_map_.Add(entry->type, std::string(string_table_.data() + offset)); } return true; } bool Reader::RemoveUnknownMessages() { MessageVector::iterator it = messages_->begin(); while (it != messages_->end()) { uint32_t type = (*it)->type(); if (!name_map_.TypeExists(type)) { LOG(ERROR) << "Missing name table entry for type " << type; return false; } const std::string& name = name_map_.TypeToName(type); if (!MessageNames::GetInstance()->NameExists(name)) { LOG(WARNING) << "Unknown message " << name; it = messages_->erase(it); } else { ++it; } } return true; } // Message types are based on line numbers, so a minor edit of *_messages.h // changes the types of messages in that file. The types are fixed here to // increase the lifetime of message files. This is only a partial fix because // message arguments and structure layouts can change as well. void Reader::FixMessageTypes() { for (MessageVector::iterator it = messages_->begin(); it != messages_->end(); ++it) { uint32_t type = (*it)->type(); const std::string& name = name_map_.TypeToName(type); uint32_t correct_type = MessageNames::GetInstance()->NameToType(name); if (type != correct_type) MessageCracker::SetMessageType(*it, correct_type); } } bool Reader::Read(MessageVector* messages) { messages_ = messages; if (!MapFile()) return false; if (!ReadHeader()) return false; if (!ReadMessages()) return false; if (!ReadStringTable()) return false; if (!ReadNameTable()) return false; if (!RemoveUnknownMessages()) return false; FixMessageTypes(); return true; } } // namespace bool MessageFile::Read(const base::FilePath& path, MessageVector* messages) { Reader reader(path); return reader.Read(messages); } } // namespace ipc_fuzzer