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.
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
{
21 // Helper class to read IPC message file into a MessageVector and
25 Reader(const base::FilePath
& path
);
26 bool Read(MessageVector
* messages
);
30 bool CutObject(const T
** object
);
32 // Reads the header, checks magic and version.
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.
44 // Removes obsolete messages from the vector.
45 bool RemoveUnknownMessages();
47 // Does type -> name -> correct_type fixup.
48 void FixMessageTypes();
52 base::MemoryMappedFile mapped_file_
;
53 base::StringPiece file_data_
;
54 base::StringPiece string_table_
;
57 const FileHeader
* header_
;
58 MessageVector
* messages_
;
59 MessageNames name_map_
;
61 DISALLOW_COPY_AND_ASSIGN(Reader
);
64 Reader::Reader(const base::FilePath
& path
)
71 bool Reader::CutObject(const T
** object
) {
72 if (file_data_
.size() < sizeof(T
)) {
73 LOG(ERROR
) << "Unexpected EOF.";
76 *object
= reinterpret_cast<const T
*>(file_data_
.data());
77 file_data_
.remove_prefix(sizeof(T
));
81 bool Reader::ReadHeader() {
82 if (!CutObject
<FileHeader
>(&header_
))
84 if (header_
->magic
!= FileHeader::kMagicValue
) {
85 LOG(ERROR
) << path_
.value() << " is not an IPC message file.";
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
<< ".";
97 bool Reader::MapFile() {
98 if (!mapped_file_
.Initialize(path_
)) {
99 LOG(ERROR
) << "Failed to map testcase: " << path_
.value();
102 const char* data
= reinterpret_cast<const char*>(mapped_file_
.data());
103 file_data_
.set(data
, mapped_file_
.length());
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
);
113 LOG(ERROR
) << "Failed to parse message.";
117 size_t msglen
= message_tail
- begin
;
118 if (msglen
> INT_MAX
) {
119 LOG(ERROR
) << "Message too large.";
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
);
132 bool Reader::ReadStringTable() {
133 size_t name_count
= header_
->name_count
;
136 if (name_count
> file_data_
.size() / sizeof(NameTableEntry
)) {
137 LOG(ERROR
) << "Invalid name table size: " << name_count
;
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.";
147 if (string_table_
.end()[-1] != '\0') {
148 LOG(ERROR
) << "String table doesn't end with NUL.";
154 bool Reader::ReadNameTable() {
155 for (size_t i
= 0; i
< header_
->name_count
; ++i
) {
156 const NameTableEntry
* entry
;
157 if (!CutObject
<NameTableEntry
>(&entry
))
159 size_t offset
= entry
->string_table_offset
;
160 if (offset
>= string_table_
.size()) {
161 LOG(ERROR
) << "Invalid string table offset: " << offset
;
164 name_map_
.Add(entry
->type
, std::string(string_table_
.data() + offset
));
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
;
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
);
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
);
203 bool Reader::Read(MessageVector
* messages
) {
204 messages_
= messages
;
212 if (!ReadStringTable())
214 if (!ReadNameTable())
216 if (!RemoveUnknownMessages())
225 bool MessageFile::Read(const base::FilePath
& path
, MessageVector
* messages
) {
227 return reader
.Read(messages
);
230 } // namespace ipc_fuzzer