ipc: Update Message::FindNext to parse brokered attachments.
[chromium-blink-merge.git] / tools / ipc_fuzzer / message_lib / message_file_reader.cc
blobb8c2bf431497a76418efb473318668683f90060b
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.
5 #include <limits.h>
7 #include "base/files/file_path.h"
8 #include "base/files/memory_mapped_file.h"
9 #include "base/logging.h"
10 #include "base/strings/string_piece.h"
11 #include "ipc/ipc_message.h"
12 #include "tools/ipc_fuzzer/message_lib/message_cracker.h"
13 #include "tools/ipc_fuzzer/message_lib/message_file.h"
14 #include "tools/ipc_fuzzer/message_lib/message_file_format.h"
15 #include "tools/ipc_fuzzer/message_lib/message_names.h"
17 namespace ipc_fuzzer {
19 namespace {
21 // Helper class to read IPC message file into a MessageVector and
22 // fix message types.
23 class Reader {
24 public:
25 Reader(const base::FilePath& path);
26 bool Read(MessageVector* messages);
28 private:
29 template <typename T>
30 bool CutObject(const T** object);
32 // Reads the header, checks magic and version.
33 bool ReadHeader();
35 bool MapFile();
36 bool ReadMessages();
38 // Last part of the file is a string table for message names.
39 bool ReadStringTable();
41 // Reads type <-> name mapping into name_map_. References string table.
42 bool ReadNameTable();
44 // Removes obsolete messages from the vector.
45 bool RemoveUnknownMessages();
47 // Does type -> name -> correct_type fixup.
48 void FixMessageTypes();
50 // Raw data.
51 base::FilePath path_;
52 base::MemoryMappedFile mapped_file_;
53 base::StringPiece file_data_;
54 base::StringPiece string_table_;
56 // Parsed data.
57 const FileHeader* header_;
58 MessageVector* messages_;
59 MessageNames name_map_;
61 DISALLOW_COPY_AND_ASSIGN(Reader);
64 Reader::Reader(const base::FilePath& path)
65 : path_(path),
66 header_(NULL),
67 messages_(NULL) {
70 template <typename T>
71 bool Reader::CutObject(const T** object) {
72 if (file_data_.size() < sizeof(T)) {
73 LOG(ERROR) << "Unexpected EOF.";
74 return false;
76 *object = reinterpret_cast<const T*>(file_data_.data());
77 file_data_.remove_prefix(sizeof(T));
78 return true;
81 bool Reader::ReadHeader() {
82 if (!CutObject<FileHeader>(&header_))
83 return false;
84 if (header_->magic != FileHeader::kMagicValue) {
85 LOG(ERROR) << path_.value() << " is not an IPC message file.";
86 return false;
88 if (header_->version != FileHeader::kCurrentVersion) {
89 LOG(ERROR) << "Wrong version for message file " << path_.value() << ". "
90 << "File version is " << header_->version << ", "
91 << "current version is " << FileHeader::kCurrentVersion << ".";
92 return false;
94 return true;
97 bool Reader::MapFile() {
98 if (!mapped_file_.Initialize(path_)) {
99 LOG(ERROR) << "Failed to map testcase: " << path_.value();
100 return false;
102 const char* data = reinterpret_cast<const char*>(mapped_file_.data());
103 file_data_.set(data, mapped_file_.length());
104 return true;
107 bool Reader::ReadMessages() {
108 for (size_t i = 0; i < header_->message_count; ++i) {
109 const char* begin = file_data_.begin();
110 const char* end = file_data_.end();
111 Message::NextMessageInfo info;
112 IPC::Message::FindNext(begin, end, &info);
113 if (!info.message_found) {
114 LOG(ERROR) << "Failed to parse message.";
115 return false;
118 CHECK_EQ(info.message_end, info.pickle_end);
119 size_t msglen = info.message_end - begin;
120 if (msglen > INT_MAX) {
121 LOG(ERROR) << "Message too large.";
122 return false;
125 // Copy is necessary to fix message type later.
126 IPC::Message const_message(begin, msglen);
127 IPC::Message* message = new IPC::Message(const_message);
128 messages_->push_back(message);
129 file_data_.remove_prefix(msglen);
131 return true;
134 bool Reader::ReadStringTable() {
135 size_t name_count = header_->name_count;
136 if (!name_count)
137 return true;
138 if (name_count > file_data_.size() / sizeof(NameTableEntry)) {
139 LOG(ERROR) << "Invalid name table size: " << name_count;
140 return false;
143 size_t string_table_offset = name_count * sizeof(NameTableEntry);
144 string_table_ = file_data_.substr(string_table_offset);
145 if (string_table_.empty()) {
146 LOG(ERROR) << "Missing string table.";
147 return false;
149 if (string_table_.end()[-1] != '\0') {
150 LOG(ERROR) << "String table doesn't end with NUL.";
151 return false;
153 return true;
156 bool Reader::ReadNameTable() {
157 for (size_t i = 0; i < header_->name_count; ++i) {
158 const NameTableEntry* entry;
159 if (!CutObject<NameTableEntry>(&entry))
160 return false;
161 size_t offset = entry->string_table_offset;
162 if (offset >= string_table_.size()) {
163 LOG(ERROR) << "Invalid string table offset: " << offset;
164 return false;
166 name_map_.Add(entry->type, std::string(string_table_.data() + offset));
168 return true;
171 bool Reader::RemoveUnknownMessages() {
172 MessageVector::iterator it = messages_->begin();
173 while (it != messages_->end()) {
174 uint32 type = (*it)->type();
175 if (!name_map_.TypeExists(type)) {
176 LOG(ERROR) << "Missing name table entry for type " << type;
177 return false;
179 const std::string& name = name_map_.TypeToName(type);
180 if (!MessageNames::GetInstance()->NameExists(name)) {
181 LOG(WARNING) << "Unknown message " << name;
182 it = messages_->erase(it);
183 } else {
184 ++it;
187 return true;
190 // Message types are based on line numbers, so a minor edit of *_messages.h
191 // changes the types of messages in that file. The types are fixed here to
192 // increase the lifetime of message files. This is only a partial fix because
193 // message arguments and structure layouts can change as well.
194 void Reader::FixMessageTypes() {
195 for (MessageVector::iterator it = messages_->begin();
196 it != messages_->end(); ++it) {
197 uint32 type = (*it)->type();
198 const std::string& name = name_map_.TypeToName(type);
199 uint32 correct_type = MessageNames::GetInstance()->NameToType(name);
200 if (type != correct_type)
201 MessageCracker::SetMessageType(*it, correct_type);
205 bool Reader::Read(MessageVector* messages) {
206 messages_ = messages;
208 if (!MapFile())
209 return false;
210 if (!ReadHeader())
211 return false;
212 if (!ReadMessages())
213 return false;
214 if (!ReadStringTable())
215 return false;
216 if (!ReadNameTable())
217 return false;
218 if (!RemoveUnknownMessages())
219 return false;
220 FixMessageTypes();
222 return true;
225 } // namespace
227 bool MessageFile::Read(const base::FilePath& path, MessageVector* messages) {
228 Reader reader(path);
229 return reader.Read(messages);
232 } // namespace ipc_fuzzer