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 IPC::Message::NextMessageInfo info
;
112 IPC::Message::FindNext(begin
, end
, &info
);
113 if (!info
.message_found
) {
114 LOG(ERROR
) << "Failed to parse message.";
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.";
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
);
134 bool Reader::ReadStringTable() {
135 size_t name_count
= header_
->name_count
;
138 if (name_count
> file_data_
.size() / sizeof(NameTableEntry
)) {
139 LOG(ERROR
) << "Invalid name table size: " << name_count
;
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.";
149 if (string_table_
.end()[-1] != '\0') {
150 LOG(ERROR
) << "String table doesn't end with NUL.";
156 bool Reader::ReadNameTable() {
157 for (size_t i
= 0; i
< header_
->name_count
; ++i
) {
158 const NameTableEntry
* entry
;
159 if (!CutObject
<NameTableEntry
>(&entry
))
161 size_t offset
= entry
->string_table_offset
;
162 if (offset
>= string_table_
.size()) {
163 LOG(ERROR
) << "Invalid string table offset: " << offset
;
166 name_map_
.Add(entry
->type
, std::string(string_table_
.data() + offset
));
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
;
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
);
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
;
214 if (!ReadStringTable())
216 if (!ReadNameTable())
218 if (!RemoveUnknownMessages())
227 bool MessageFile::Read(const base::FilePath
& path
, MessageVector
* messages
) {
229 return reader
.Read(messages
);
232 } // namespace ipc_fuzzer