| Index: tools/ipc_fuzzer/message_lib/message_file_reader.cc
|
| diff --git a/tools/ipc_fuzzer/message_lib/message_file_reader.cc b/tools/ipc_fuzzer/message_lib/message_file_reader.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e93c460b7e287dbfc3012b789db3b505f7b44f25
|
| --- /dev/null
|
| +++ b/tools/ipc_fuzzer/message_lib/message_file_reader.cc
|
| @@ -0,0 +1,230 @@
|
| +// 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 <limits.h>
|
| +
|
| +#include "base/files/file_path.h"
|
| +#include "base/files/memory_mapped_file.h"
|
| +#include "base/logging.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 <typename T>
|
| + 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 <typename T>
|
| +bool Reader::CutObject(const T** object) {
|
| + if (file_data_.size() < sizeof(T)) {
|
| + LOG(ERROR) << "Unexpected EOF.";
|
| + return false;
|
| + }
|
| + *object = reinterpret_cast<const T*>(file_data_.data());
|
| + file_data_.remove_prefix(sizeof(T));
|
| + return true;
|
| +}
|
| +
|
| +bool Reader::ReadHeader() {
|
| + if (!CutObject<FileHeader>(&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<const char*>(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();
|
| + const char* message_tail = IPC::Message::FindNext(begin, end);
|
| + if (!message_tail) {
|
| + LOG(ERROR) << "Failed to parse message.";
|
| + return false;
|
| + }
|
| +
|
| + size_t msglen = message_tail - 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<NameTableEntry>(&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 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 type = (*it)->type();
|
| + const std::string& name = name_map_.TypeToName(type);
|
| + uint32 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
|
|
|