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 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" 16 17 namespace ipc_fuzzer { 18 19 namespace { 20 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); 27 28 private: 29 template <typename T> 30 bool CutObject(const T** object); 31 32 // Reads the header, checks magic and version. 33 bool ReadHeader(); 34 35 bool MapFile(); 36 bool ReadMessages(); 37 38 // Last part of the file is a string table for message names. 39 bool ReadStringTable(); 40 41 // Reads type <-> name mapping into name_map_. References string table. 42 bool ReadNameTable(); 43 44 // Removes obsolete messages from the vector. 45 bool RemoveUnknownMessages(); 46 47 // Does type -> name -> correct_type fixup. 48 void FixMessageTypes(); 49 50 // Raw data. 51 base::FilePath path_; 52 base::MemoryMappedFile mapped_file_; 53 base::StringPiece file_data_; 54 base::StringPiece string_table_; 55 56 // Parsed data. 57 const FileHeader* header_; 58 MessageVector* messages_; 59 MessageNames name_map_; 60 61 DISALLOW_COPY_AND_ASSIGN(Reader); 62 }; 63 64 Reader::Reader(const base::FilePath& path) 65 : path_(path), 66 header_(NULL), 67 messages_(NULL) { 68 } 69 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; 75 } 76 *object = reinterpret_cast<const T*>(file_data_.data()); 77 file_data_.remove_prefix(sizeof(T)); 78 return true; 79 } 80 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; 87 } 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; 93 } 94 return true; 95 } 96 97 bool Reader::MapFile() { 98 if (!mapped_file_.Initialize(path_)) { 99 LOG(ERROR) << "Failed to map testcase: " << path_.value(); 100 return false; 101 } 102 const char* data = reinterpret_cast<const char*>(mapped_file_.data()); 103 file_data_.set(data, mapped_file_.length()); 104 return true; 105 } 106 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 const char* message_tail = IPC::Message::FindNext(begin, end); 112 if (!message_tail) { 113 LOG(ERROR) << "Failed to parse message."; 114 return false; 115 } 116 117 size_t msglen = message_tail - begin; 118 if (msglen > INT_MAX) { 119 LOG(ERROR) << "Message too large."; 120 return false; 121 } 122 123 // Copy is necessary to fix message type later. 124 IPC::Message const_message(begin, msglen); 125 IPC::Message* message = new IPC::Message(const_message); 126 messages_->push_back(message); 127 file_data_.remove_prefix(msglen); 128 } 129 return true; 130 } 131 132 bool Reader::ReadStringTable() { 133 size_t name_count = header_->name_count; 134 if (!name_count) 135 return true; 136 if (name_count > file_data_.size() / sizeof(NameTableEntry)) { 137 LOG(ERROR) << "Invalid name table size: " << name_count; 138 return false; 139 } 140 141 size_t string_table_offset = name_count * sizeof(NameTableEntry); 142 string_table_ = file_data_.substr(string_table_offset); 143 if (string_table_.empty()) { 144 LOG(ERROR) << "Missing string table."; 145 return false; 146 } 147 if (string_table_.end()[-1] != '\0') { 148 LOG(ERROR) << "String table doesn't end with NUL."; 149 return false; 150 } 151 return true; 152 } 153 154 bool Reader::ReadNameTable() { 155 for (size_t i = 0; i < header_->name_count; ++i) { 156 const NameTableEntry* entry; 157 if (!CutObject<NameTableEntry>(&entry)) 158 return false; 159 size_t offset = entry->string_table_offset; 160 if (offset >= string_table_.size()) { 161 LOG(ERROR) << "Invalid string table offset: " << offset; 162 return false; 163 } 164 name_map_.Add(entry->type, std::string(string_table_.data() + offset)); 165 } 166 return true; 167 } 168 169 bool Reader::RemoveUnknownMessages() { 170 MessageVector::iterator it = messages_->begin(); 171 while (it != messages_->end()) { 172 uint32 type = (*it)->type(); 173 if (!name_map_.TypeExists(type)) { 174 LOG(ERROR) << "Missing name table entry for type " << type; 175 return false; 176 } 177 const std::string& name = name_map_.TypeToName(type); 178 if (!MessageNames::GetInstance()->NameExists(name)) { 179 LOG(WARNING) << "Unknown message " << name; 180 it = messages_->erase(it); 181 } else { 182 ++it; 183 } 184 } 185 return true; 186 } 187 188 // Message types are based on line numbers, so a minor edit of *_messages.h 189 // changes the types of messages in that file. The types are fixed here to 190 // increase the lifetime of message files. This is only a partial fix because 191 // message arguments and structure layouts can change as well. 192 void Reader::FixMessageTypes() { 193 for (MessageVector::iterator it = messages_->begin(); 194 it != messages_->end(); ++it) { 195 uint32 type = (*it)->type(); 196 const std::string& name = name_map_.TypeToName(type); 197 uint32 correct_type = MessageNames::GetInstance()->NameToType(name); 198 if (type != correct_type) 199 MessageCracker::SetMessageType(*it, correct_type); 200 } 201 } 202 203 bool Reader::Read(MessageVector* messages) { 204 messages_ = messages; 205 206 if (!MapFile()) 207 return false; 208 if (!ReadHeader()) 209 return false; 210 if (!ReadMessages()) 211 return false; 212 if (!ReadStringTable()) 213 return false; 214 if (!ReadNameTable()) 215 return false; 216 if (!RemoveUnknownMessages()) 217 return false; 218 FixMessageTypes(); 219 220 return true; 221 } 222 223 } // namespace 224 225 bool MessageFile::Read(const base::FilePath& path, MessageVector* messages) { 226 Reader reader(path); 227 return reader.Read(messages); 228 } 229 230 } // namespace ipc_fuzzer 231