Home | History | Annotate | Download | only in message_lib
      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