| // 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 <stddef.h> |
| #include <stdint.h> |
| #include <set> |
| |
| #include "base/files/file.h" |
| #include "base/logging.h" |
| #include "base/macros.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 write a MessageVector + message names to a file. |
| class Writer { |
| public: |
| Writer(const base::FilePath& path); |
| ~Writer() {} |
| bool Write(const MessageVector& messages); |
| |
| private: |
| bool OpenFile(); |
| |
| // Helper to append data to file_. |
| bool WriteBlob(const void *buffer, size_t size); |
| |
| // Collects a set of MessageVector message types. Corresponding message |
| // names need to be included in the file. |
| bool CollectMessageTypes(); |
| |
| bool WriteHeader(); |
| bool WriteMessages(); |
| |
| // Each name table entry is a message type + string table offset. |
| bool WriteNameTable(); |
| |
| // String table contains the actual message names. |
| bool WriteStringTable(); |
| |
| typedef std::set<uint32_t> TypesSet; |
| base::FilePath path_; |
| base::File file_; |
| const MessageVector* messages_; |
| TypesSet types_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Writer); |
| }; |
| |
| Writer::Writer(const base::FilePath& path) : path_(path), messages_(NULL) { |
| } |
| |
| bool Writer::OpenFile() { |
| file_.Initialize(path_, |
| base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); |
| if (!file_.IsValid()) { |
| LOG(ERROR) << "Failed to create IPC message file: " << path_.value(); |
| return false; |
| } |
| return true; |
| } |
| |
| bool Writer::WriteBlob(const void *buffer, size_t size) { |
| if (size > INT_MAX) |
| return false; |
| const char* char_buffer = static_cast<const char*>(buffer); |
| int ret = file_.WriteAtCurrentPos(char_buffer, size); |
| if (ret != static_cast<int>(size)) { |
| LOG(ERROR) << "Failed to write " << size << " bytes."; |
| return false; |
| } |
| return true; |
| } |
| |
| bool Writer::CollectMessageTypes() { |
| for (size_t i = 0; i < messages_->size(); ++i) { |
| uint32_t type = (*messages_)[i]->type(); |
| if (!MessageNames::GetInstance()->TypeExists(type)) { |
| LOG(ERROR) << "Unknown message type: " << type; |
| return false; |
| } |
| types_.insert(type); |
| } |
| return true; |
| } |
| |
| bool Writer::WriteHeader() { |
| FileHeader header; |
| if (messages_->size() > UINT_MAX) |
| return false; |
| header.magic = FileHeader::kMagicValue; |
| header.version = FileHeader::kCurrentVersion; |
| header.message_count = messages_->size(); |
| header.name_count = types_.size(); |
| if (!WriteBlob(&header, sizeof(FileHeader))) |
| return false; |
| return true; |
| } |
| |
| bool Writer::WriteMessages() { |
| for (size_t i = 0; i < messages_->size(); ++i) { |
| IPC::Message* message = (*messages_)[i].get(); |
| if (!WriteBlob(message->data(), message->size())) |
| return false; |
| } |
| return true; |
| } |
| |
| bool Writer::WriteNameTable() { |
| size_t string_table_offset = 0; |
| NameTableEntry entry; |
| |
| for (TypesSet::iterator it = types_.begin(); it != types_.end(); ++it) { |
| if (string_table_offset > UINT_MAX) |
| return false; |
| entry.type = *it; |
| entry.string_table_offset = string_table_offset; |
| if (!WriteBlob(&entry, sizeof(NameTableEntry))) |
| return false; |
| const std::string& name = MessageNames::GetInstance()->TypeToName(*it); |
| string_table_offset += name.length() + 1; |
| } |
| return true; |
| } |
| |
| bool Writer::WriteStringTable() { |
| for (TypesSet::iterator it = types_.begin(); it != types_.end(); ++it) { |
| const std::string& name = MessageNames::GetInstance()->TypeToName(*it); |
| if (!WriteBlob(name.c_str(), name.length() + 1)) |
| return false; |
| } |
| return true; |
| } |
| |
| bool Writer::Write(const MessageVector& messages) { |
| messages_ = &messages; |
| |
| if (!OpenFile()) |
| return false; |
| if (!CollectMessageTypes()) |
| return false; |
| if (!WriteHeader()) |
| return false; |
| if (!WriteMessages()) |
| return false; |
| if (!WriteNameTable()) |
| return false; |
| if (!WriteStringTable()) |
| return false; |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| bool MessageFile::Write(const base::FilePath& path, |
| const MessageVector& messages) { |
| Writer writer(path); |
| return writer.Write(messages); |
| } |
| |
| } // namespace ipc_fuzzer |