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 <string>
19 :
20 : #include "base/files/file_util.h"
21 : #include "base/files/scoped_temp_dir.h"
22 : #include "base/strings/utf_string_conversions.h"
23 : #include "gtest/gtest.h"
24 : #include "syzygy/core/unittest_util.h"
25 : #include "syzygy/minidump/unittest_util.h"
26 :
27 : namespace minidump {
28 :
29 : class MinidumpTest : public testing::Test {
30 : public:
31 E : void SetUp() override {
32 E : ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
33 E : dump_file_ = temp_dir_.path().Append(L"minidump.dmp");
34 E : }
35 :
36 E : const base::FilePath& dump_file() const { return dump_file_; }
37 :
38 : private:
39 : base::FilePath dump_file_;
40 : base::ScopedTempDir temp_dir_;
41 : };
42 :
43 E : TEST_F(MinidumpTest, OpenSuccedsForValidFile) {
44 E : Minidump minidump;
45 :
46 E : ASSERT_TRUE(minidump.Open(testing::TestMinidumps::GetNotepad32Dump()));
47 E : ASSERT_LE(1U, minidump.directory().size());
48 E : }
49 :
50 E : TEST_F(MinidumpTest, OpenFailsForInvalidFile) {
51 E : Minidump minidump;
52 :
53 : // Try opening a non-existing file.
54 E : ASSERT_FALSE(minidump.Open(dump_file()));
55 :
56 : // Create an empty file, opening it should fail.
57 : {
58 E : base::ScopedFILE tmp(base::OpenFile(dump_file(), "wb"));
59 E : }
60 E : ASSERT_FALSE(minidump.Open(dump_file()));
61 :
62 : // Create a file with a header, but an invalid signature.
63 : {
64 E : base::ScopedFILE tmp(base::OpenFile(dump_file(), "wb"));
65 :
66 E : MINIDUMP_HEADER hdr = {0};
67 E : ASSERT_EQ(sizeof(hdr), fwrite(&hdr, sizeof(char), sizeof(hdr), tmp.get()));
68 E : }
69 E : ASSERT_FALSE(minidump.Open(dump_file()));
70 :
71 : // Create a file with a valid signature, but a zero-length directory.
72 : {
73 E : base::ScopedFILE tmp(base::OpenFile(dump_file(), "wb"));
74 :
75 E : MINIDUMP_HEADER hdr = {0};
76 E : hdr.Signature = MINIDUMP_SIGNATURE;
77 E : ASSERT_EQ(sizeof(hdr), fwrite(&hdr, sizeof(char), sizeof(hdr), tmp.get()));
78 E : }
79 E : ASSERT_FALSE(minidump.Open(dump_file()));
80 :
81 : // Create a file with a valid header, but a missing directory.
82 : {
83 E : base::ScopedFILE tmp(base::OpenFile(dump_file(), "wb"));
84 :
85 E : MINIDUMP_HEADER hdr = {0};
86 E : hdr.Signature = MINIDUMP_SIGNATURE;
87 E : hdr.NumberOfStreams = 10;
88 E : hdr.StreamDirectoryRva = sizeof(hdr);
89 E : ASSERT_EQ(sizeof(hdr), fwrite(&hdr, sizeof(char), sizeof(hdr), tmp.get()));
90 E : }
91 E : ASSERT_FALSE(minidump.Open(dump_file()));
92 E : }
93 :
94 E : TEST_F(MinidumpTest, StreamTest) {
95 : // Create a file with some data to test the streams.
96 : {
97 E : base::ScopedFILE tmp(base::OpenFile(dump_file(), "wb"));
98 :
99 E : MINIDUMP_HEADER hdr = {0};
100 E : hdr.Signature = MINIDUMP_SIGNATURE;
101 E : hdr.NumberOfStreams = 1;
102 E : hdr.StreamDirectoryRva = sizeof(hdr);
103 E : ASSERT_EQ(sizeof(hdr), fwrite(&hdr, sizeof(char), sizeof(hdr), tmp.get()));
104 :
105 E : for (uint32_t i = 0; i < 100; ++i)
106 E : ASSERT_EQ(sizeof(i), fwrite(&i, sizeof(char), sizeof(i), tmp.get()));
107 E : }
108 :
109 E : Minidump minidump;
110 E : ASSERT_TRUE(minidump.Open(dump_file()));
111 :
112 : // Make a short, arbitrary location.
113 E : MINIDUMP_LOCATION_DESCRIPTOR loc = { 7, sizeof(MINIDUMP_HEADER) };
114 E : Minidump::Stream test = minidump.GetStreamFor(loc);
115 :
116 E : EXPECT_EQ(7U, test.GetRemainingBytes());
117 :
118 : // Read the first integer.
119 E : const uint32_t kSentinel = 0xCAFEBABE;
120 E : uint32_t tmp = kSentinel;
121 E : ASSERT_TRUE(test.ReadElement(&tmp));
122 E : EXPECT_EQ(0U, tmp);
123 E : EXPECT_EQ(3U, test.GetRemainingBytes());
124 :
125 : // Reading another integer should fail, as the stream doesn't cover it.
126 E : tmp = kSentinel;
127 E : ASSERT_FALSE(test.ReadElement(&tmp));
128 : // The failing read must not modify the input.
129 E : EXPECT_EQ(kSentinel, tmp);
130 :
131 : // Try the same thing with byte reads.
132 E : uint8_t bytes[10] = {};
133 E : ASSERT_FALSE(test.ReadBytes(4, &bytes));
134 :
135 : // A three-byte read should succeed.
136 E : ASSERT_TRUE(test.ReadBytes(3, &bytes));
137 E : EXPECT_EQ(0U, test.GetRemainingBytes());
138 :
139 : // Little-endian byte order assumed.
140 E : EXPECT_EQ(1U, bytes[0]);
141 E : EXPECT_EQ(0U, bytes[1]);
142 E : EXPECT_EQ(0U, bytes[2]);
143 :
144 : // No moar data.
145 E : EXPECT_FALSE(test.ReadBytes(1, &bytes));
146 :
147 : // Reset the stream to test reading via a string.
148 E : test = minidump.GetStreamFor(loc);
149 E : std::string data;
150 E : ASSERT_TRUE(test.ReadBytes(1, &data));
151 E : EXPECT_EQ(6U, test.GetRemainingBytes());
152 E : EXPECT_EQ(1U, data.size());
153 E : EXPECT_EQ(0, data[0]);
154 E : }
155 :
156 E : TEST_F(MinidumpTest, FindNextStream) {
157 E : Minidump minidump;
158 :
159 E : ASSERT_TRUE(minidump.Open(testing::TestMinidumps::GetNotepad32Dump()));
160 :
161 : Minidump::Stream sys_info =
162 E : minidump.FindNextStream(nullptr, SystemInfoStream);
163 E : ASSERT_TRUE(sys_info.IsValid());
164 :
165 E : MINIDUMP_SYSTEM_INFO info = {};
166 E : EXPECT_TRUE(sys_info.ReadElement(&info));
167 :
168 : Minidump::Stream invalid =
169 E : minidump.FindNextStream(&sys_info, SystemInfoStream);
170 E : EXPECT_FALSE(invalid.IsValid());
171 E : }
172 :
173 E : TEST_F(MinidumpTest, ReadThreadInfo) {
174 E : Minidump minidump;
175 :
176 E : ASSERT_TRUE(minidump.Open(testing::TestMinidumps::GetNotepad32Dump()));
177 :
178 : Minidump::Stream thread_list =
179 E : minidump.FindNextStream(nullptr, ThreadListStream);
180 E : ASSERT_TRUE(thread_list.IsValid());
181 :
182 E : ULONG32 num_threads = 0;
183 E : ASSERT_TRUE(thread_list.ReadElement(&num_threads));
184 :
185 E : for (size_t i = 0; i < num_threads; ++i) {
186 E : MINIDUMP_THREAD thread = {};
187 E : ASSERT_TRUE(thread_list.ReadElement(&thread));
188 :
189 E : Minidump::Stream thread_memory = minidump.GetStreamFor(thread.Stack.Memory);
190 E : EXPECT_TRUE(thread_memory.IsValid());
191 :
192 : Minidump::Stream thread_context =
193 E : minidump.GetStreamFor(thread.ThreadContext);
194 E : EXPECT_TRUE(thread_context.IsValid());
195 :
196 E : CONTEXT ctx = {};
197 E : EXPECT_TRUE(thread_context.ReadElement(&ctx));
198 E : }
199 E : }
200 :
201 E : TEST_F(MinidumpTest, ReadString) {
202 E : wchar_t kSomeString[] = L"some string";
203 :
204 : // Create a minimal file to test reading a string.
205 : {
206 E : base::ScopedFILE tmp(base::OpenFile(dump_file(), "wb"));
207 :
208 : // Valid header.
209 E : MINIDUMP_HEADER hdr = {0};
210 E : hdr.Signature = MINIDUMP_SIGNATURE;
211 E : hdr.NumberOfStreams = 1;
212 E : hdr.StreamDirectoryRva = sizeof(hdr);
213 E : ASSERT_EQ(sizeof(hdr), fwrite(&hdr, sizeof(char), sizeof(hdr), tmp.get()));
214 :
215 : // Dummy directory.
216 E : MINIDUMP_DIRECTORY directory = {0};
217 : ASSERT_EQ(sizeof(directory),
218 E : fwrite(&directory, sizeof(char), sizeof(directory), tmp.get()));
219 :
220 : // A string. Note that although a null terminating character is written, it
221 : // is not counted in the size written to the file.
222 E : ULONG32 size_bytes = sizeof(kSomeString) - sizeof(wchar_t);
223 : ASSERT_EQ(sizeof(ULONG32),
224 E : fwrite(&size_bytes, sizeof(char), sizeof(ULONG32), tmp.get()));
225 : ASSERT_EQ(sizeof(kSomeString), fwrite(&kSomeString, sizeof(char),
226 E : sizeof(kSomeString), tmp.get()));
227 E : }
228 :
229 E : Minidump minidump;
230 E : ASSERT_TRUE(minidump.Open(dump_file()));
231 :
232 : MINIDUMP_LOCATION_DESCRIPTOR loc = {
233 : static_cast<ULONG32>(-1),
234 E : sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY)};
235 E : Minidump::Stream test = minidump.GetStreamFor(loc);
236 E : std::wstring recovered;
237 E : ASSERT_TRUE(test.ReadString(&recovered));
238 E : ASSERT_EQ(kSomeString, recovered);
239 E : }
240 :
241 : } // namespace minidump
|