1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <limits.h>
6#include <set>
7
8#include "base/files/file.h"
9#include "base/logging.h"
10#include "tools/ipc_fuzzer/message_lib/message_file.h"
11#include "tools/ipc_fuzzer/message_lib/message_file_format.h"
12#include "tools/ipc_fuzzer/message_lib/message_names.h"
13
14namespace ipc_fuzzer {
15
16namespace {
17
18// Helper class to write a MessageVector + message names to a file.
19class Writer {
20 public:
21  Writer(const base::FilePath& path);
22  ~Writer() {}
23  bool Write(const MessageVector& messages);
24
25 private:
26  bool OpenFile();
27
28  // Helper to append data to file_.
29  bool WriteBlob(const void *buffer, size_t size);
30
31  // Collects a set of MessageVector message types. Corresponding message
32  // names need to be included in the file.
33  bool CollectMessageTypes();
34
35  bool WriteHeader();
36  bool WriteMessages();
37
38  // Each name table entry is a message type + string table offset.
39  bool WriteNameTable();
40
41  // String table contains the actual message names.
42  bool WriteStringTable();
43
44  typedef std::set<uint32> TypesSet;
45  base::FilePath path_;
46  base::File file_;
47  const MessageVector* messages_;
48  TypesSet types_;
49
50  DISALLOW_COPY_AND_ASSIGN(Writer);
51};
52
53Writer::Writer(const base::FilePath& path) : path_(path), messages_(NULL) {
54}
55
56bool Writer::OpenFile() {
57  file_.Initialize(path_,
58                   base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
59  if (!file_.IsValid()) {
60    LOG(ERROR) << "Failed to create IPC message file: " << path_.value();
61    return false;
62  }
63  return true;
64}
65
66bool Writer::WriteBlob(const void *buffer, size_t size) {
67  if (size > INT_MAX)
68    return false;
69  const char* char_buffer = static_cast<const char*>(buffer);
70  int ret = file_.WriteAtCurrentPos(char_buffer, size);
71  if (ret != static_cast<int>(size)) {
72    LOG(ERROR) << "Failed to write " << size << " bytes.";
73    return false;
74  }
75  return true;
76}
77
78bool Writer::CollectMessageTypes() {
79  for (size_t i = 0; i < messages_->size(); ++i) {
80    uint32_t type = (*messages_)[i]->type();
81    if (!MessageNames::GetInstance()->TypeExists(type)) {
82      LOG(ERROR) << "Unknown message type: " << type;
83      return false;
84    }
85    types_.insert(type);
86  }
87  return true;
88}
89
90bool Writer::WriteHeader() {
91  FileHeader header;
92  if (messages_->size() > UINT_MAX)
93    return false;
94  header.magic = FileHeader::kMagicValue;
95  header.version = FileHeader::kCurrentVersion;
96  header.message_count = messages_->size();
97  header.name_count = types_.size();
98  if (!WriteBlob(&header, sizeof(FileHeader)))
99    return false;
100  return true;
101}
102
103bool Writer::WriteMessages() {
104  for (size_t i = 0; i < messages_->size(); ++i) {
105    IPC::Message* message = (*messages_)[i];
106    if (!WriteBlob(message->data(), message->size()))
107      return false;
108  }
109  return true;
110}
111
112bool Writer::WriteNameTable() {
113  size_t string_table_offset = 0;
114  NameTableEntry entry;
115
116  for (TypesSet::iterator it = types_.begin(); it != types_.end(); ++it) {
117    if (string_table_offset > UINT_MAX)
118      return false;
119    entry.type = *it;
120    entry.string_table_offset = string_table_offset;
121    if (!WriteBlob(&entry, sizeof(NameTableEntry)))
122      return false;
123    const std::string& name = MessageNames::GetInstance()->TypeToName(*it);
124    string_table_offset += name.length() + 1;
125  }
126  return true;
127}
128
129bool Writer::WriteStringTable() {
130  for (TypesSet::iterator it = types_.begin(); it != types_.end(); ++it) {
131    const std::string& name = MessageNames::GetInstance()->TypeToName(*it);
132    if (!WriteBlob(name.c_str(), name.length() + 1))
133      return false;
134  }
135  return true;
136}
137
138bool Writer::Write(const MessageVector& messages) {
139  messages_ = &messages;
140
141  if (!OpenFile())
142    return false;
143  if (!CollectMessageTypes())
144    return false;
145  if (!WriteHeader())
146    return false;
147  if (!WriteMessages())
148    return false;
149  if (!WriteNameTable())
150    return false;
151  if (!WriteStringTable())
152    return false;
153
154  return true;
155}
156
157}  // namespace
158
159bool MessageFile::Write(const base::FilePath& path,
160                        const MessageVector& messages) {
161  Writer writer(path);
162  return writer.Write(messages);
163}
164
165}  // namespace ipc_fuzzer
166