Chromium Code Reviews| 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..7f033d2685f9c4d50e8007acd970c40d389fdd4c |
| --- /dev/null |
| +++ b/tools/ipc_fuzzer/message_lib/message_file_reader.cc |
| @@ -0,0 +1,190 @@ |
| +// 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); |
| + |
| + 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(); |
| + |
| + // Does type -> name -> correct_type fixup. |
| + bool 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::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); |
| + |
|
Tom Sepez
2013/12/06 18:57:16
nit: (micro-nit, actually) no blank line here.
aedla
2013/12/09 18:08:32
Done.
|
| + file_data_.remove_prefix(msglen); |
| + } |
| + return true; |
| +} |
| + |
| +bool Reader::ReadStringTable() { |
| + size_t name_count = header_->name_count; |
| + if (!name_count) |
|
Tom Sepez
2013/12/06 18:57:16
I'd consider this an error, since all the fixups b
aedla
2013/12/09 18:08:32
I'd like to support empty testcases, which have me
|
| + 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::FixMessageTypes() { |
| + for (size_t i = 0; i < messages_->size(); ++i) { |
|
Tom Sepez
2013/12/06 18:57:16
Probably a comment about why this is necessary, an
aedla
2013/12/09 18:08:32
Done.
|
| + IPC::Message* message = (*messages_)[i]; |
| + uint32 type = message->type(); |
| + if (!name_map_.TypeExists(type)) { |
| + LOG(ERROR) << "Missing name table entry for type " << type; |
| + return false; |
|
Tom Sepez
2013/12/06 18:57:16
You can't bail here. Someone might have removed t
aedla
2013/12/09 18:08:32
Ah, this particular case happens when the file doe
|
| + } |
| + const std::string& name = name_map_.TypeToName(type); |
| + uint32 correct_type = MessageNames::Get().NameToType(name); |
| + if (type != correct_type) |
| + MessageCracker::SetMessageType(message, correct_type); |
| + } |
| + return true; |
| +} |
| + |
| +bool Reader::Read(MessageVector* messages) { |
| + messages_ = messages; |
| + |
| + if (!MapFile()) |
| + return false; |
| + if (!CutObject<FileHeader>(&header_)) |
| + return false; |
| + if (!ReadMessages()) |
| + return false; |
| + if (!ReadStringTable()) |
| + return false; |
| + if (!ReadNameTable()) |
| + return false; |
| + if (!FixMessageTypes()) |
| + return false; |
| + |
| + return true; |
| +} |
| + |
| +} // namespace |
| + |
| +bool MessageFile::Read(const base::FilePath& path, MessageVector* messages) { |
| + Reader reader(path); |
| + return reader.Read(messages); |
| +} |
| + |
| +} // namespace ipc_fuzzer |