1 : // Copyright 2015 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/minidump/minidump.h"
16 :
17 : #include <stdint.h>
18 : #include <set>
19 : #include <string>
20 : #include <vector>
21 :
22 : #include "base/files/file_util.h"
23 : #include "base/files/scoped_temp_dir.h"
24 : #include "base/strings/utf_string_conversions.h"
25 : #include "gtest/gtest.h"
26 : #include "syzygy/core/unittest_util.h"
27 : #include "syzygy/minidump/unittest_util.h"
28 :
29 : namespace minidump {
30 :
31 : class FileMinidumpTest : public testing::Test {
32 : public:
33 E : void SetUp() override {
34 E : ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
35 E : dump_file_ = temp_dir_.path().Append(L"minidump.dmp");
36 E : }
37 :
38 E : const base::FilePath& dump_file() const { return dump_file_; }
39 :
40 : private:
41 : base::FilePath dump_file_;
42 : base::ScopedTempDir temp_dir_;
43 : };
44 :
45 : class ScopedMinidumpBuffer {
46 : public:
47 : template <typename ElementType>
48 E : void Append(const ElementType& element) {
49 E : Append(&element, sizeof(element));
50 E : }
51 E : void Append(const void* data, size_t data_len) {
52 E : const uint8_t* buf = reinterpret_cast<const uint8_t*>(data);
53 :
54 E : buf_.insert(buf_.end(), buf, buf + data_len);
55 E : }
56 :
57 E : const uint8_t* data() const { return buf_.data(); }
58 :
59 E : size_t len() const { return buf_.size(); }
60 :
61 : private:
62 : std::vector<uint8_t> buf_;
63 : };
64 :
65 E : TEST_F(FileMinidumpTest, OpenSuccedsForValidFile) {
66 E : FileMinidump minidump;
67 :
68 E : ASSERT_TRUE(minidump.Open(testing::TestMinidumps::GetNotepad32Dump()));
69 E : ASSERT_LE(1U, minidump.directory().size());
70 E : }
71 :
72 E : TEST_F(FileMinidumpTest, OpenFailsForInvalidFile) {
73 E : FileMinidump minidump;
74 :
75 : // Try opening a non-existing file.
76 E : ASSERT_FALSE(minidump.Open(dump_file()));
77 E : }
78 :
79 E : TEST_F(FileMinidumpTest, FindNextStream) {
80 E : FileMinidump minidump;
81 :
82 E : ASSERT_TRUE(minidump.Open(testing::TestMinidumps::GetNotepad32Dump()));
83 :
84 : Minidump::Stream sys_info =
85 E : minidump.FindNextStream(nullptr, SystemInfoStream);
86 E : ASSERT_TRUE(sys_info.IsValid());
87 :
88 E : MINIDUMP_SYSTEM_INFO info = {};
89 E : EXPECT_TRUE(sys_info.ReadAndAdvanceElement(&info));
90 :
91 : Minidump::Stream invalid =
92 E : minidump.FindNextStream(&sys_info, SystemInfoStream);
93 E : EXPECT_FALSE(invalid.IsValid());
94 E : }
95 :
96 E : TEST_F(FileMinidumpTest, ReadThreadInfo) {
97 E : FileMinidump minidump;
98 :
99 E : ASSERT_TRUE(minidump.Open(testing::TestMinidumps::GetNotepad32Dump()));
100 :
101 : Minidump::Stream thread_list =
102 E : minidump.FindNextStream(nullptr, ThreadListStream);
103 E : ASSERT_TRUE(thread_list.IsValid());
104 :
105 E : ULONG32 num_threads = 0;
106 E : ASSERT_TRUE(thread_list.ReadAndAdvanceElement(&num_threads));
107 :
108 E : for (size_t i = 0; i < num_threads; ++i) {
109 E : MINIDUMP_THREAD thread = {};
110 E : ASSERT_TRUE(thread_list.ReadAndAdvanceElement(&thread));
111 :
112 E : Minidump::Stream thread_memory = minidump.GetStreamFor(thread.Stack.Memory);
113 E : EXPECT_TRUE(thread_memory.IsValid());
114 :
115 : Minidump::Stream thread_context =
116 E : minidump.GetStreamFor(thread.ThreadContext);
117 E : EXPECT_TRUE(thread_context.IsValid());
118 :
119 E : CONTEXT ctx = {};
120 E : EXPECT_TRUE(thread_context.ReadAndAdvanceElement(&ctx));
121 E : }
122 E : }
123 :
124 E : TEST_F(FileMinidumpTest, GetMemoryList) {
125 E : FileMinidump minidump;
126 E : ASSERT_TRUE(minidump.Open(testing::TestMinidumps::GetNotepad32Dump()));
127 :
128 E : auto memory = minidump.GetMemoryList();
129 E : EXPECT_TRUE(memory.IsValid());
130 E : EXPECT_NE(0U, memory.header().NumberOfMemoryRanges);
131 :
132 : // TODO(siggi): what to do here?
133 E : size_t memory_count = 0;
134 E : size_t memory_size = 0;
135 E : for (const auto& element : memory) {
136 E : ++memory_count;
137 E : memory_size += element.Memory.DataSize;
138 E : }
139 :
140 E : ASSERT_EQ(memory.header().NumberOfMemoryRanges, memory_count);
141 E : ASSERT_LT(0u, memory_size);
142 E : }
143 :
144 E : TEST_F(FileMinidumpTest, GetModuleList) {
145 E : FileMinidump minidump;
146 E : ASSERT_TRUE(minidump.Open(testing::TestMinidumps::GetNotepad32Dump()));
147 :
148 E : auto modules = minidump.GetModuleList();
149 E : EXPECT_TRUE(modules.IsValid());
150 E : EXPECT_NE(0U, modules.header().NumberOfModules);
151 :
152 : // TODO(siggi): what to do here?
153 E : size_t module_count = 0;
154 E : size_t module_size = 0;
155 E : for (const auto& element : modules) {
156 E : ++module_count;
157 E : module_size += element.SizeOfImage;
158 E : }
159 :
160 E : ASSERT_EQ(modules.header().NumberOfModules, module_count);
161 E : ASSERT_LT(0u, module_size);
162 E : }
163 :
164 E : TEST_F(FileMinidumpTest, GetThreadList) {
165 E : FileMinidump minidump;
166 E : ASSERT_TRUE(minidump.Open(testing::TestMinidumps::GetNotepad32Dump()));
167 :
168 E : auto threads = minidump.GetThreadList();
169 E : EXPECT_TRUE(threads.IsValid());
170 E : EXPECT_NE(0U, threads.header().NumberOfThreads);
171 :
172 E : std::set<uint32_t> thread_id_set;
173 E : for (const auto& element : threads) {
174 E : ASSERT_TRUE(thread_id_set.insert(element.ThreadId).second);
175 E : }
176 :
177 E : ASSERT_LT(0u, thread_id_set.size());
178 E : }
179 :
180 : #if 0
181 : // TODO(siggi): This is apparently itanium-specific :/.
182 : TEST_F(FileMinidumpTest, GetThreadExList) {
183 : FileMinidump minidump;
184 : ASSERT_TRUE(minidump.Open(testing::TestMinidumps::GetNotepad64Dump()));
185 :
186 : auto threads = minidump.GetThreadExList();
187 : EXPECT_TRUE(threads.IsValid());
188 : EXPECT_NE(0U, threads.header().NumberOfThreads);
189 :
190 : std::set<uint32_t> thread_id_set;
191 : for (const auto& element : threads) {
192 : ASSERT_TRUE(thread_id_set.insert(element.ThreadId).second);
193 : }
194 :
195 : ASSERT_LT(0u, thread_id_set.size());
196 : }
197 : #endif
198 :
199 E : TEST(BufferMinidumpTest, InitFailsForInvalidFile) {
200 : // Opening an empty buffer should fail.
201 : {
202 E : uint8_t data = 0;
203 E : BufferMinidump minidump;
204 E : ASSERT_FALSE(minidump.Initialize(&data, 0));
205 E : }
206 :
207 : // Create a file with a header, but an invalid signature.
208 : {
209 E : MINIDUMP_HEADER hdr = {0};
210 :
211 E : ScopedMinidumpBuffer buf;
212 E : buf.Append(hdr);
213 :
214 E : BufferMinidump minidump;
215 E : ASSERT_FALSE(minidump.Initialize(buf.data(), buf.len()));
216 E : }
217 :
218 : // Create a file with a valid signature, but a zero-length directory.
219 : {
220 E : MINIDUMP_HEADER hdr = {0};
221 E : hdr.Signature = MINIDUMP_SIGNATURE;
222 :
223 E : ScopedMinidumpBuffer buf;
224 E : buf.Append(hdr);
225 :
226 E : BufferMinidump minidump;
227 E : ASSERT_FALSE(minidump.Initialize(buf.data(), buf.len()));
228 E : }
229 :
230 : // Create a file with a valid header, but a missing directory.
231 : {
232 E : MINIDUMP_HEADER hdr = {0};
233 E : hdr.Signature = MINIDUMP_SIGNATURE;
234 E : hdr.NumberOfStreams = 10;
235 E : hdr.StreamDirectoryRva = sizeof(hdr);
236 :
237 E : ScopedMinidumpBuffer buf;
238 E : buf.Append(hdr);
239 :
240 E : BufferMinidump minidump;
241 E : ASSERT_FALSE(minidump.Initialize(buf.data(), buf.len()));
242 E : }
243 E : }
244 :
245 E : TEST(BufferMinidumpTest, StreamTest) {
246 : // Create a buffer with some data to test the streams.
247 E : ScopedMinidumpBuffer buf;
248 :
249 : {
250 E : MINIDUMP_HEADER hdr = {0};
251 E : hdr.Signature = MINIDUMP_SIGNATURE;
252 E : hdr.NumberOfStreams = 1;
253 E : hdr.StreamDirectoryRva = sizeof(hdr);
254 :
255 E : buf.Append(hdr);
256 :
257 E : for (uint32_t i = 0; i < 100; ++i)
258 E : buf.Append(i);
259 : }
260 :
261 E : BufferMinidump minidump;
262 E : ASSERT_TRUE(minidump.Initialize(buf.data(), buf.len()));
263 :
264 : // Make a short, arbitrary location.
265 E : MINIDUMP_LOCATION_DESCRIPTOR loc = { 7, sizeof(MINIDUMP_HEADER) };
266 E : Minidump::Stream test = minidump.GetStreamFor(loc);
267 :
268 E : EXPECT_EQ(7U, test.remaining_length());
269 :
270 : // Read the first integer.
271 E : const uint32_t kSentinel = 0xCAFEBABE;
272 E : uint32_t tmp = kSentinel;
273 E : ASSERT_TRUE(test.ReadAndAdvanceElement(&tmp));
274 E : EXPECT_EQ(0U, tmp);
275 E : EXPECT_EQ(3U, test.remaining_length());
276 :
277 : // Reading another integer should fail, as the stream doesn't cover it.
278 E : tmp = kSentinel;
279 E : ASSERT_FALSE(test.ReadAndAdvanceElement(&tmp));
280 : // The failing read must not modify the input.
281 E : EXPECT_EQ(kSentinel, tmp);
282 :
283 : // Try the same thing with byte reads.
284 E : uint8_t bytes[10] = {};
285 E : ASSERT_FALSE(test.ReadBytes(4, &bytes));
286 :
287 : // A three-byte read should succeed.
288 E : ASSERT_TRUE(test.ReadAndAdvanceBytes(3, &bytes));
289 E : EXPECT_EQ(0U, test.remaining_length());
290 :
291 : // Little-endian byte order assumed.
292 E : EXPECT_EQ(1U, bytes[0]);
293 E : EXPECT_EQ(0U, bytes[1]);
294 E : EXPECT_EQ(0U, bytes[2]);
295 :
296 : // No moar data.
297 E : EXPECT_FALSE(test.ReadBytes(1, &bytes));
298 :
299 : // Reset the stream to test reading via a string.
300 E : test = minidump.GetStreamFor(loc);
301 E : std::string data;
302 E : ASSERT_TRUE(test.ReadAndAdvanceBytes(1, &data));
303 E : EXPECT_EQ(6U, test.remaining_length());
304 E : EXPECT_EQ(1U, data.size());
305 E : EXPECT_EQ(0, data[0]);
306 E : }
307 :
308 E : TEST(BufferMinidumpTest, ReadAndAdvanceString) {
309 E : wchar_t kSomeString[] = L"some string";
310 :
311 : // Create a minimal buffer to test reading a string.
312 E : ScopedMinidumpBuffer buf;
313 : {
314 : // Valid header.
315 E : MINIDUMP_HEADER hdr = {0};
316 E : hdr.Signature = MINIDUMP_SIGNATURE;
317 E : hdr.NumberOfStreams = 1;
318 E : hdr.StreamDirectoryRva = sizeof(hdr);
319 E : buf.Append(hdr);
320 :
321 : // Dummy directory.
322 E : MINIDUMP_DIRECTORY directory = {0};
323 E : buf.Append(directory);
324 :
325 : // A string. Note that although a null terminating character is written, it
326 : // is not counted in the size written to the file.
327 E : ULONG32 size_bytes = sizeof(kSomeString) - sizeof(wchar_t);
328 E : buf.Append(size_bytes);
329 E : buf.Append(kSomeString, sizeof(kSomeString));
330 : }
331 :
332 E : BufferMinidump minidump;
333 E : ASSERT_TRUE(minidump.Initialize(buf.data(), buf.len()));
334 :
335 : MINIDUMP_LOCATION_DESCRIPTOR loc = {
336 E : static_cast<ULONG32>(-1),
337 E : sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY)};
338 E : Minidump::Stream test = minidump.GetStreamFor(loc);
339 E : std::wstring recovered;
340 E : ASSERT_TRUE(test.ReadAndAdvanceString(&recovered));
341 E : ASSERT_EQ(kSomeString, recovered);
342 E : }
343 :
344 : } // namespace minidump
|