1 : // Copyright 2014 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/refinery/unittest_util.h"
16 :
17 : #include <windows.h> // NOLINT
18 : #include <dbghelp.h>
19 :
20 : #include <cstring>
21 : #include <vector>
22 :
23 : #include "base/base_switches.h"
24 : #include "base/command_line.h"
25 : #include "base/logging.h"
26 : #include "base/files/file_util.h"
27 : #include "base/files/scoped_temp_dir.h"
28 : #include "base/numerics/safe_math.h"
29 : #include "base/strings/string_number_conversions.h"
30 : #include "base/strings/utf_string_conversions.h"
31 : #include "base/test/multiprocess_test.h"
32 : #include "base/test/test_timeouts.h"
33 : #include "gtest/gtest.h"
34 : #include "syzygy/common/com_utils.h"
35 : #include "testing/multiprocess_func_list.h"
36 :
37 m : namespace testing {
38 :
39 m : namespace {
40 :
41 m : void PopulateContext(std::string* context_bytes, int base_offset) {
42 m : DCHECK(context_bytes != nullptr);
43 :
44 m : context_bytes->resize(sizeof(CONTEXT));
45 m : CONTEXT* ctx = reinterpret_cast<CONTEXT*>(&context_bytes->at(0));
46 m : ctx->ContextFlags = CONTEXT_SEGMENTS | CONTEXT_INTEGER | CONTEXT_CONTROL;
47 m : ctx->SegGs = base_offset + 1;
48 m : ctx->SegFs = base_offset + 2;
49 m : ctx->SegEs = base_offset + 3;
50 m : ctx->SegDs = base_offset + 4;
51 m : ctx->Edi = base_offset + 11;
52 m : ctx->Esi = base_offset + 12;
53 m : ctx->Ebx = base_offset + 13;
54 m : ctx->Edx = base_offset + 14;
55 m : ctx->Ecx = base_offset + 15;
56 m : ctx->Eax = base_offset + 16;
57 m : ctx->Ebp = base_offset + 21;
58 m : ctx->Eip = base_offset + 22;
59 m : ctx->SegCs = base_offset + 23;
60 m : ctx->EFlags = base_offset + 24;
61 m : ctx->Esp = base_offset + 25;
62 m : ctx->SegSs = base_offset + 26;
63 m : }
64 :
65 m : using MemorySpecification = MinidumpSpecification::MemorySpecification;
66 m : using ThreadSpecification = MinidumpSpecification::ThreadSpecification;
67 m : using ExceptionSpecification = MinidumpSpecification::ExceptionSpecification;
68 m : using ModuleSpecification = MinidumpSpecification::ModuleSpecification;
69 :
70 : // TODO(manzagop): ensure on destruction or finalizing that the allocations are
71 : // actually reflected in the file. At this point, allocating without writing
72 : // would leave the file short.
73 m : class MinidumpSerializer {
74 m : public:
75 m : typedef RVA Position;
76 :
77 m : MinidumpSerializer();
78 :
79 : // @pre @p dir must be a valid directory.
80 m : bool Initialize(const base::ScopedTempDir& dir);
81 m : bool SerializeThreads(const std::vector<ThreadSpecification>& threads);
82 m : bool SerializeMemory(const std::vector<MemorySpecification>& regions);
83 m : bool SerializeModules(const std::vector<ModuleSpecification>& modules);
84 m : bool SerializeExceptions(
85 m : const std::vector<ExceptionSpecification>& exceptions);
86 m : bool Finalize();
87 :
88 m : const base::FilePath& path() const { return path_; }
89 :
90 m : private:
91 m : static const Position kHeaderPos = 0U;
92 :
93 m : void SerializeDirectory();
94 m : void SerializeHeader();
95 :
96 m : Position Allocate(size_t size_bytes);
97 :
98 : // Allocate new space and write to it.
99 m : template <class DataType>
100 m : Position Append(const DataType& data);
101 : // @pre @p data must not be empty.
102 m : template <class DataType>
103 m : Position AppendVec(const std::vector<DataType>& data);
104 : // @pre @p elements must not be empty.
105 m : template <class DataType>
106 m : Position AppendListStream(MINIDUMP_STREAM_TYPE type,
107 m : const std::vector<DataType>& elements);
108 m : Position AppendBytes(base::StringPiece data);
109 m : Position AppendMinidumpString(base::StringPiece utf8);
110 :
111 : // Write to already allocated space.
112 m : template <class DataType>
113 m : void Write(Position pos, const DataType& data);
114 m : void WriteBytes(Position pos, size_t size_bytes, const void* data);
115 :
116 m : bool IncrementCursor(size_t size_bytes);
117 :
118 : // Gets the position of an address range which is fully contained in a
119 : // serialized range.
120 : // @pre is_serialize_memory_invoked_ must be true.
121 : // @param range the range for which to get the rva.
122 : // @param pos the returned position.
123 : // returns true on success, false otherwise.
124 m : bool GetPos(const refinery::AddressRange& range, Position* pos) const;
125 :
126 m : void AddDirectoryEntry(MINIDUMP_STREAM_TYPE type, Position pos, size_t size);
127 :
128 m : bool succeeded() const { return !failed_; }
129 :
130 m : bool failed_;
131 m : bool is_serialize_memory_invoked_;
132 :
133 m : std::vector<MINIDUMP_DIRECTORY> directory_;
134 :
135 m : Position cursor_; // The next allocatable position.
136 m : base::FilePath path_;
137 m : base::ScopedFILE file_;
138 :
139 m : std::map<refinery::AddressRange, Position> memory_positions_;
140 m : };
141 :
142 m : MinidumpSerializer::MinidumpSerializer()
143 m : : failed_(true), is_serialize_memory_invoked_(false), cursor_(0U) {
144 m : }
145 :
146 m : bool MinidumpSerializer::Initialize(const base::ScopedTempDir& dir) {
147 m : DCHECK(dir.IsValid());
148 :
149 m : failed_ = false;
150 m : is_serialize_memory_invoked_ = false;
151 m : directory_.clear();
152 :
153 : // Create the backing file.
154 m : cursor_ = 0U;
155 m : if (!CreateTemporaryFileInDir(dir.path(), &path_)) {
156 m : failed_ = true;
157 m : return false;
158 m : }
159 :
160 m : file_.reset(base::OpenFile(path_, "wb"));
161 m : if (file_.get() == nullptr)
162 m : failed_ = true;
163 :
164 : // Allocate the header.
165 m : Position pos = Allocate(sizeof(MINIDUMP_HEADER));
166 m : DCHECK_EQ(kHeaderPos, pos);
167 :
168 m : return succeeded();
169 m : }
170 :
171 m : bool MinidumpSerializer::SerializeThreads(
172 m : const std::vector<ThreadSpecification>& specs) {
173 m : if (specs.empty())
174 m : return succeeded();
175 :
176 m : std::vector<MINIDUMP_THREAD> threads;
177 m : threads.resize(specs.size());
178 :
179 m : for (int i = 0; i < specs.size(); ++i) {
180 m : const ThreadSpecification& spec = specs[i];
181 :
182 : // Write the context.
183 m : DCHECK_EQ(sizeof(CONTEXT), spec.context_data.size());
184 m : Position pos = AppendBytes(spec.context_data);
185 :
186 : // Copy thread to vector for more efficient serialization, then set Rvas.
187 m : DCHECK_EQ(sizeof(MINIDUMP_THREAD), spec.thread_data.size());
188 m : memcpy(&threads.at(i), spec.thread_data.c_str(), sizeof(MINIDUMP_THREAD));
189 :
190 m : MINIDUMP_THREAD& thread = threads[i];
191 m : refinery::AddressRange stack_range(thread.Stack.StartOfMemoryRange,
192 m : thread.Stack.Memory.DataSize);
193 m : if (!GetPos(stack_range, &thread.Stack.Memory.Rva))
194 m : failed_ = true;
195 m : thread.ThreadContext.Rva = pos;
196 m : }
197 :
198 m : AppendListStream(ThreadListStream, threads);
199 m : return succeeded();
200 m : }
201 :
202 m : bool MinidumpSerializer::SerializeMemory(
203 m : const std::vector<MemorySpecification>& regions) {
204 : // Signal that memory serialization has occured, and regions now have
205 : // associated positions in the minidump.
206 m : is_serialize_memory_invoked_ = true;
207 :
208 m : if (regions.empty())
209 m : return succeeded();
210 :
211 : // Write bytes data and create the memory descriptors.
212 m : std::vector<MINIDUMP_MEMORY_DESCRIPTOR> memory_descriptors;
213 m : memory_descriptors.resize(regions.size());
214 m : size_t idx = 0;
215 m : for (const auto& region : regions) {
216 m : refinery::AddressRange range(region.address, region.buffer.size());
217 m : DCHECK(range.IsValid());
218 :
219 m : Position pos = AppendBytes(region.buffer);
220 m : auto inserted = memory_positions_.insert(std::make_pair(range, pos));
221 m : DCHECK_EQ(true, inserted.second);
222 :
223 m : memory_descriptors[idx].StartOfMemoryRange = range.start();
224 m : memory_descriptors[idx].Memory.DataSize = range.size();
225 m : memory_descriptors[idx].Memory.Rva = pos;
226 :
227 m : ++idx;
228 m : }
229 :
230 : // Write descriptors and create directory entry.
231 m : AppendListStream(MemoryListStream, memory_descriptors);
232 :
233 m : return succeeded();
234 m : }
235 :
236 m : bool MinidumpSerializer::SerializeModules(
237 m : const std::vector<ModuleSpecification>& module_specs) {
238 m : if (module_specs.empty())
239 m : return succeeded();
240 :
241 m : std::vector<MINIDUMP_MODULE> modules;
242 m : modules.resize(module_specs.size());
243 :
244 m : for (int i = 0; i < module_specs.size(); ++i) {
245 m : modules[i].BaseOfImage = module_specs[i].addr;
246 m : modules[i].SizeOfImage = module_specs[i].size;
247 m : modules[i].CheckSum = module_specs[i].checksum;
248 m : modules[i].TimeDateStamp = module_specs[i].timestamp;
249 m : modules[i].ModuleNameRva = AppendMinidumpString(module_specs[i].name);
250 m : }
251 :
252 m : AppendListStream(ModuleListStream, modules);
253 :
254 m : return succeeded();
255 m : }
256 :
257 m : bool MinidumpSerializer::SerializeExceptions(
258 m : const std::vector<ExceptionSpecification>& exception_specs) {
259 m : if (exception_specs.empty())
260 m : return succeeded();
261 :
262 m : for (const ExceptionSpecification& spec : exception_specs) {
263 : // Write the context.
264 m : DCHECK_EQ(sizeof(CONTEXT), spec.context_data.size());
265 m : Position pos = AppendBytes(spec.context_data);
266 :
267 : // Write the MINIDUMP_EXCEPTION_STREAM.
268 m : ULONG32 thread_id = spec.thread_id;
269 m : Position rva = Append(thread_id);
270 m : ULONG32 dummy_alignment = 0U;
271 m : Append(dummy_alignment);
272 :
273 m : MINIDUMP_EXCEPTION exception = {0};
274 m : exception.ExceptionCode = spec.exception_code;
275 m : exception.ExceptionFlags = spec.exception_flags;
276 m : exception.ExceptionRecord = spec.exception_record;
277 m : exception.ExceptionAddress = spec.exception_address;
278 m : exception.NumberParameters = spec.exception_information.size();
279 m : DCHECK(exception.NumberParameters <= EXCEPTION_MAXIMUM_PARAMETERS);
280 m : for (size_t i = 0; i < spec.exception_information.size(); ++i) {
281 m : exception.ExceptionInformation[i] = spec.exception_information[i];
282 m : }
283 m : Append(exception);
284 :
285 m : MINIDUMP_LOCATION_DESCRIPTOR context_loc = {};
286 m : context_loc.DataSize = sizeof(CONTEXT);
287 m : context_loc.Rva = pos;
288 m : Append(context_loc);
289 :
290 m : AddDirectoryEntry(ExceptionStream, rva, sizeof(MINIDUMP_EXCEPTION_STREAM));
291 m : }
292 :
293 m : return succeeded();
294 m : }
295 :
296 m : bool MinidumpSerializer::Finalize() {
297 : // Serialize the directory.
298 m : Position pos = AppendVec(directory_);
299 :
300 : // Serialize the header.
301 m : MINIDUMP_HEADER hdr;
302 m : hdr.Signature = MINIDUMP_SIGNATURE;
303 m : hdr.NumberOfStreams = directory_.size();
304 m : hdr.StreamDirectoryRva = pos;
305 m : Write(kHeaderPos, hdr);
306 :
307 m : return succeeded();
308 m : }
309 :
310 m : MinidumpSerializer::Position MinidumpSerializer::Allocate(size_t size_bytes) {
311 m : Position pos = cursor_;
312 m : if (!IncrementCursor(size_bytes))
313 m : failed_ = true;
314 m : return pos;
315 m : }
316 :
317 m : template <class DataType>
318 m : MinidumpSerializer::Position MinidumpSerializer::Append(const DataType& data) {
319 m : Position pos = Allocate(sizeof(data));
320 m : Write(pos, data);
321 m : return pos;
322 m : }
323 :
324 m : template <class DataType>
325 m : MinidumpSerializer::Position MinidumpSerializer::AppendVec(
326 m : const std::vector<DataType>& data) {
327 m : DCHECK(!data.empty());
328 :
329 m : size_t size_bytes = sizeof(DataType) * data.size();
330 m : Position pos = Allocate(size_bytes);
331 m : WriteBytes(pos, size_bytes, &data.at(0));
332 m : return pos;
333 m : }
334 :
335 m : template <class DataType>
336 m : MinidumpSerializer::Position MinidumpSerializer::AppendListStream(
337 m : MINIDUMP_STREAM_TYPE type,
338 m : const std::vector<DataType>& elements) {
339 m : DCHECK(!elements.empty());
340 :
341 : // Append the stream
342 m : ULONG32 num_elements = elements.size();
343 m : Position pos = Append(num_elements);
344 m : AppendVec(elements);
345 :
346 : // Create its directory entry.
347 m : size_t size_bytes = sizeof(ULONG32) + elements.size() * sizeof(DataType);
348 m : AddDirectoryEntry(type, pos, size_bytes);
349 :
350 m : return pos;
351 m : }
352 :
353 m : MinidumpSerializer::Position MinidumpSerializer::AppendBytes(
354 m : base::StringPiece data) {
355 m : Position pos = Allocate(data.length());
356 m : WriteBytes(pos, data.length(), data.data());
357 m : return pos;
358 m : }
359 :
360 m : MinidumpSerializer::Position MinidumpSerializer::AppendMinidumpString(
361 m : base::StringPiece utf8) {
362 m : std::wstring wide = base::UTF8ToWide(utf8);
363 m : ULONG32 size_bytes = wide.length() * sizeof(std::wstring::value_type);
364 :
365 m : Position pos = Append(size_bytes);
366 : // Note: write the null termination character.
367 m : size_bytes += sizeof(std::wstring::value_type);
368 m : Position string_pos = Allocate(size_bytes);
369 m : WriteBytes(string_pos, size_bytes, wide.c_str());
370 m : return pos;
371 m : }
372 :
373 m : template <class DataType>
374 m : void MinidumpSerializer::Write(Position pos, const DataType& data) {
375 m : WriteBytes(pos, sizeof(data), &data);
376 m : }
377 :
378 m : void MinidumpSerializer::WriteBytes(Position pos,
379 m : size_t size_bytes,
380 m : const void* data) {
381 m : if (failed_)
382 m : return;
383 :
384 : // Validate the write does not go past the cursor.
385 m : base::CheckedNumeric<Position> pos_end = pos;
386 m : pos_end += size_bytes;
387 m : DCHECK(pos_end.IsValid());
388 m : DCHECK(pos_end.ValueOrDie() <= cursor_);
389 :
390 m : DCHECK(file_.get() != nullptr);
391 :
392 : // Seek and write.
393 m : if (fseek(file_.get(), pos, SEEK_SET) != 0) {
394 m : failed_ = true;
395 m : return;
396 m : }
397 m : if (fwrite(data, sizeof(char), size_bytes, file_.get()) != size_bytes) {
398 m : failed_ = true;
399 m : return;
400 m : }
401 m : }
402 :
403 m : bool MinidumpSerializer::IncrementCursor(size_t size_bytes) {
404 m : base::CheckedNumeric<Position> cur = cursor_;
405 m : cur += size_bytes;
406 m : if (!cur.IsValid())
407 m : return false;
408 :
409 m : cursor_ += size_bytes;
410 m : return true;
411 m : }
412 :
413 m : bool MinidumpSerializer::GetPos(const refinery::AddressRange& range,
414 m : Position* pos) const {
415 m : DCHECK(range.IsValid());
416 m : DCHECK(pos != nullptr);
417 m : DCHECK(is_serialize_memory_invoked_);
418 :
419 m : auto it = memory_positions_.upper_bound(range);
420 m : if (it == memory_positions_.begin())
421 m : return false;
422 :
423 : // Note: given that memory ranges do not overlap, only the immediate
424 : // predecessor is a candidate match.
425 m : --it;
426 m : if (it->first.Contains(range)) {
427 m : *pos = it->second;
428 m : return true;
429 m : }
430 :
431 m : return false;
432 m : }
433 :
434 m : void MinidumpSerializer::AddDirectoryEntry(MINIDUMP_STREAM_TYPE type,
435 m : Position pos,
436 m : size_t size_bytes) {
437 m : MINIDUMP_DIRECTORY directory = {0};
438 m : directory.StreamType = type;
439 m : directory.Location.Rva = pos;
440 m : directory.Location.DataSize = size_bytes;
441 m : directory_.push_back(directory);
442 m : }
443 :
444 m : } // namespace
445 :
446 m : const uint32_t ScopedMinidump::kMinidumpWithStacks =
447 m : MiniDumpWithProcessThreadData | // Get PEB and TEB.
448 m : MiniDumpWithUnloadedModules; // Get unloaded modules when available.
449 :
450 m : const uint32_t ScopedMinidump::kMinidumpWithData =
451 m : kMinidumpWithStacks |
452 m : MiniDumpWithIndirectlyReferencedMemory; // Get referenced memory.
453 :
454 m : MinidumpSpecification::MinidumpSpecification() : allow_memory_overlap_(false) {
455 m : }
456 :
457 m : MinidumpSpecification::MinidumpSpecification(AllowMemoryOverlap dummy)
458 m : : allow_memory_overlap_(true) {
459 m : }
460 :
461 m : bool MinidumpSpecification::AddThread(const ThreadSpecification& spec) {
462 m : DCHECK_EQ(sizeof(MINIDUMP_THREAD), spec.thread_data.size());
463 m : DCHECK_EQ(sizeof(CONTEXT), spec.context_data.size());
464 m : DCHECK_GT(spec.thread_data.size(), 0U);
465 m : DCHECK_GT(spec.context_data.size(), 0U);
466 :
467 m : threads_.push_back(spec);
468 :
469 m : return true;
470 m : }
471 :
472 m : bool MinidumpSpecification::AddMemoryRegion(const MemorySpecification& spec) {
473 m : refinery::Address address = spec.address;
474 m : refinery::Size size_bytes = spec.buffer.size();
475 :
476 : // Ensure range validity.
477 m : refinery::AddressRange range(address, size_bytes);
478 m : if (!range.IsValid())
479 m : return false;
480 :
481 m : if (!allow_memory_overlap_) {
482 : // Overlap is not allowed in this instance - check with successor and
483 : // predecessor.
484 m : auto it = region_sizes_.upper_bound(address);
485 :
486 m : if (it != region_sizes_.end()) {
487 m : refinery::AddressRange post_range(it->first, it->second);
488 m : DCHECK(post_range.IsValid());
489 m : if (range.Intersects(post_range))
490 m : return false;
491 m : }
492 :
493 m : if (it != region_sizes_.begin()) {
494 m : --it;
495 m : refinery::AddressRange pre_range(it->first, it->second);
496 m : DCHECK(pre_range.IsValid());
497 m : if (range.Intersects(pre_range))
498 m : return false;
499 m : }
500 :
501 : // Add the specification.
502 m : auto inserted = region_sizes_.insert(std::make_pair(address, size_bytes));
503 m : if (!inserted.second)
504 m : return false;
505 m : }
506 :
507 m : memory_regions_.push_back(spec);
508 :
509 m : return true;
510 m : }
511 :
512 m : bool MinidumpSpecification::AddModule(const ModuleSpecification& module) {
513 m : modules_.push_back(module);
514 m : return true;
515 m : }
516 :
517 m : bool MinidumpSpecification::AddException(
518 m : const ExceptionSpecification& exception) {
519 m : exceptions_.push_back(exception);
520 m : return true;
521 m : }
522 :
523 m : bool MinidumpSpecification::Serialize(const base::ScopedTempDir& dir,
524 m : base::FilePath* path) const {
525 m : MinidumpSerializer serializer;
526 m : bool success = serializer.Initialize(dir) &&
527 m : serializer.SerializeMemory(memory_regions_) &&
528 m : serializer.SerializeThreads(threads_) &&
529 m : serializer.SerializeModules(modules_) &&
530 m : serializer.SerializeExceptions(exceptions_) &&
531 m : serializer.Finalize();
532 m : *path = serializer.path();
533 m : return success;
534 m : }
535 :
536 m : MinidumpSpecification::MemorySpecification::MemorySpecification()
537 m : : address(0ULL) {
538 m : }
539 :
540 m : MinidumpSpecification::MemorySpecification::MemorySpecification(
541 m : refinery::Address addr,
542 m : base::StringPiece data)
543 m : : address(addr) {
544 m : data.CopyToString(&buffer);
545 m : }
546 :
547 m : MinidumpSpecification::ThreadSpecification::ThreadSpecification(
548 m : size_t thread_id,
549 m : refinery::Address stack_address,
550 m : refinery::Size stack_size) {
551 : // Generate the MINIDUMP_THREAD.
552 m : thread_data.resize(sizeof(MINIDUMP_THREAD));
553 m : MINIDUMP_THREAD* thread =
554 m : reinterpret_cast<MINIDUMP_THREAD*>(&thread_data.at(0));
555 m : thread->ThreadId = thread_id;
556 m : thread->SuspendCount = 2;
557 m : thread->PriorityClass = 3;
558 m : thread->Priority = 4;
559 m : thread->Teb = 5U;
560 m : thread->Stack.StartOfMemoryRange = stack_address;
561 m : thread->Stack.Memory.DataSize = stack_size;
562 m : thread->ThreadContext.DataSize = sizeof(CONTEXT);
563 : // Note: thread.Stack.Memory.Rva and thread.ThreadContext.Rva are set during
564 : // serialization.
565 :
566 m : PopulateContext(&context_data, 0);
567 m : }
568 :
569 m : void MinidumpSpecification::ThreadSpecification::SetTebAddress(
570 m : refinery::Address addr) {
571 m : DCHECK(thread_data.size() == sizeof(MINIDUMP_THREAD));
572 m : MINIDUMP_THREAD* thread =
573 m : reinterpret_cast<MINIDUMP_THREAD*>(&thread_data.at(0));
574 m : thread->Teb = addr;
575 m : }
576 :
577 m : void MinidumpSpecification::ThreadSpecification::FillStackMemorySpecification(
578 m : MinidumpSpecification::MemorySpecification* spec) const {
579 m : DCHECK(spec);
580 m : DCHECK_EQ(sizeof(MINIDUMP_THREAD), thread_data.size());
581 m : const MINIDUMP_THREAD* thread =
582 m : reinterpret_cast<const MINIDUMP_THREAD*>(&thread_data.at(0));
583 :
584 : // The stack is a range of 'S' padded at either end with a single 'P'.
585 m : DCHECK_GT(thread->Stack.StartOfMemoryRange, 0U);
586 m : const ULONG32 kStackMaxSize = static_cast<ULONG32>(-1) - 1;
587 m : DCHECK(thread->Stack.Memory.DataSize < kStackMaxSize);
588 m : spec->address = thread->Stack.StartOfMemoryRange - 1;
589 m : spec->buffer.resize(thread->Stack.Memory.DataSize + 2, 'S');
590 m : spec->buffer[0] = 'P';
591 m : spec->buffer[thread->Stack.Memory.DataSize + 1] = 'P';
592 m : }
593 :
594 m : MinidumpSpecification::ExceptionSpecification::ExceptionSpecification(
595 m : uint32_t thread_identifier) {
596 m : thread_id = thread_identifier;
597 m : exception_code = EXCEPTION_ACCESS_VIOLATION;
598 m : exception_flags = EXCEPTION_NONCONTINUABLE;
599 m : exception_record = 0ULL;
600 m : exception_address = 1111ULL;
601 m : exception_information.push_back(1);
602 m : exception_information.push_back(2222ULL);
603 :
604 m : PopulateContext(&context_data, 100);
605 m : }
606 :
607 m : MinidumpSpecification::ModuleSpecification::ModuleSpecification() {
608 m : addr = 12345ULL;
609 m : size = 75U;
610 m : checksum = 23U;
611 m : timestamp = 42U;
612 m : name = "someModule";
613 m : }
614 :
615 m : namespace {
616 :
617 : // Minidump.
618 m : const wchar_t kMinidumpFileName[] = L"minidump.dmp";
619 m : const char kSwitchExceptionPtrs[] = "exception-ptrs";
620 m : const char kSwitchPid[] = "dump-pid";
621 m : const char kSwitchMinidumpPath[] = "dump-path";
622 m : const char kSwitchTid[] = "exception-thread-id";
623 m : const char kSwitchMinidumpType[] = "minidump-type";
624 :
625 m : __declspec(noinline) DWORD GetEip() {
626 m : return reinterpret_cast<DWORD>(_ReturnAddress());
627 m : }
628 :
629 m : } // namespace
630 :
631 m : MULTIPROCESS_TEST_MAIN(MinidumpDumperProcess) {
632 : // Retrieve information from the command line.
633 m : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
634 m : if (!cmd_line->HasSwitch(kSwitchPid) || !cmd_line->HasSwitch(kSwitchTid) ||
635 m : !cmd_line->HasSwitch(kSwitchExceptionPtrs) ||
636 m : !cmd_line->HasSwitch(kSwitchMinidumpPath)) {
637 m : return 1;
638 m : }
639 :
640 m : std::string pid_string = cmd_line->GetSwitchValueASCII(kSwitchPid);
641 m : unsigned pid_uint = 0U;
642 m : if (!base::StringToUint(pid_string, &pid_uint))
643 m : return 1;
644 m : base::ProcessId pid = static_cast<base::ProcessId>(pid_uint);
645 :
646 m : std::string thread_id_string = cmd_line->GetSwitchValueASCII(kSwitchTid);
647 m : unsigned thread_id = 0U;
648 m : if (!base::StringToUint(thread_id_string, &thread_id))
649 m : return 1;
650 :
651 m : std::string exception_ptrs_string =
652 m : cmd_line->GetSwitchValueASCII(kSwitchExceptionPtrs);
653 m : unsigned exception_ptrs = 0ULL;
654 m : if (!base::StringToUint(exception_ptrs_string, &exception_ptrs))
655 m : return 1;
656 m : std::string minidump_type_string =
657 m : cmd_line->GetSwitchValueASCII(kSwitchMinidumpType);
658 m : uint32_t minidump_type = 0U;
659 m : if (!base::StringToUint(minidump_type_string, &minidump_type))
660 m : return 1;
661 :
662 m : base::FilePath minidump_path =
663 m : cmd_line->GetSwitchValuePath(kSwitchMinidumpPath);
664 :
665 : // Get handles to dumpee and dump file.
666 m : base::Process dumpee_process = base::Process::OpenWithAccess(
667 m : pid, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ);
668 m : if (!dumpee_process.IsValid()) {
669 m : LOG(ERROR) << "Failed to open process: " << ::common::LogWe() << ".";
670 m : return 1;
671 m : }
672 :
673 m : base::File minidump_file(minidump_path,
674 m : base::File::FLAG_CREATE | base::File::FLAG_WRITE);
675 m : if (!minidump_file.IsValid()) {
676 m : LOG(ERROR) << "Failed to create minidump file: " << minidump_path.value();
677 m : return 1;
678 m : }
679 :
680 : // Build the dump related information.
681 m : MINIDUMP_EXCEPTION_INFORMATION exception_information = {};
682 m : exception_information.ThreadId = static_cast<DWORD>(thread_id);
683 m : exception_information.ExceptionPointers =
684 m : reinterpret_cast<PEXCEPTION_POINTERS>(exception_ptrs);
685 m : exception_information.ClientPointers = true;
686 :
687 m : MINIDUMP_USER_STREAM_INFORMATION* user_info = nullptr;
688 m : MINIDUMP_CALLBACK_INFORMATION* callback_info = nullptr;
689 :
690 : // Take the minidump.
691 m : if (::MiniDumpWriteDump(
692 m : dumpee_process.Handle(), pid, minidump_file.GetPlatformFile(),
693 m : static_cast<MINIDUMP_TYPE>(minidump_type), &exception_information,
694 m : user_info, callback_info) == FALSE) {
695 m : LOG(ERROR) << "MiniDumpWriteDump failed: " << ::common::LogWe() << ".";
696 m : return 1;
697 m : }
698 :
699 m : return 0;
700 m : }
701 :
702 m : bool ScopedMinidump::GenerateMinidump(uint32_t minidump_type) {
703 : // Determine minidump path.
704 m : if (!temp_dir_.CreateUniqueTempDir())
705 m : return false;
706 :
707 m : minidump_path_ = temp_dir_.path().Append(kMinidumpFileName);
708 :
709 : // Grab a context. RtlCaptureContext sets the instruction pointer, stack
710 : // pointer and base pointer to values from this function's callee (similar
711 : // to _ReturnAddress). Override them so they actually match the context.
712 : // TODO(manzagop): package this to a utility function.
713 m : CONTEXT context = {};
714 m : ::RtlCaptureContext(&context);
715 m : __asm {
716 m : mov context.Ebp, ebp
717 m : mov context.Esp, esp
718 m : }
719 m : context.Eip = GetEip();
720 :
721 : // Build the exception information.
722 m : EXCEPTION_RECORD exception = {};
723 m : exception.ExceptionCode = 0xCAFEBABE; // Note: a random error code.
724 m : exception.ExceptionAddress = reinterpret_cast<PVOID>(context.Eip);
725 :
726 m : EXCEPTION_POINTERS exception_pointers = {&exception, &context};
727 :
728 : // Build the dumper's command line.
729 m : base::CommandLine dumper_command_line(
730 m : base::GetMultiProcessTestChildBaseCommandLine());
731 m : dumper_command_line.AppendSwitchASCII(switches::kTestChildProcess,
732 m : "MinidumpDumperProcess");
733 m : base::Process current_process = base::Process::Current();
734 m : dumper_command_line.AppendSwitchASCII(
735 m : kSwitchPid, base::UintToString(current_process.Pid()));
736 m : dumper_command_line.AppendSwitchASCII(
737 m : kSwitchTid, base::UintToString(::GetCurrentThreadId()));
738 m : unsigned exception_pointers_uint =
739 m : reinterpret_cast<unsigned>(&exception_pointers);
740 m : dumper_command_line.AppendSwitchASCII(
741 m : kSwitchExceptionPtrs, base::UintToString(exception_pointers_uint));
742 m : dumper_command_line.AppendSwitchASCII(kSwitchMinidumpType,
743 m : base::UintToString(minidump_type));
744 m : dumper_command_line.AppendSwitchPath(kSwitchMinidumpPath, minidump_path());
745 :
746 : // Launch the dumper.
747 m : base::Process dumper_process =
748 m : base::LaunchProcess(dumper_command_line, base::LaunchOptions());
749 m : int exit_code = 0;
750 m : bool success = dumper_process.WaitForExitWithTimeout(
751 m : TestTimeouts::action_timeout(), &exit_code);
752 m : if (!success) {
753 m : dumper_process.Terminate(0, true);
754 m : return false;
755 m : }
756 :
757 m : return exit_code == 0;
758 m : }
759 :
760 m : ScopedHeap::ScopedHeap() : heap_(nullptr) {
761 m : }
762 :
763 m : ScopedHeap::~ScopedHeap() {
764 m : if (heap_ != nullptr) {
765 m : EXPECT_TRUE(::HeapDestroy(heap_));
766 m : heap_ = nullptr;
767 m : }
768 m : }
769 :
770 m : bool ScopedHeap::Create() {
771 m : CHECK(heap_ == nullptr);
772 m : heap_ = ::HeapCreate(0, 0, 0);
773 m : return heap_ != nullptr;
774 m : }
775 :
776 m : void* ScopedHeap::Allocate(size_t block_size) {
777 m : CHECK(heap_ != nullptr);
778 :
779 m : return ::HeapAlloc(heap_, 0, block_size);
780 m : }
781 :
782 m : bool ScopedHeap::Free(void* block) {
783 m : CHECK(heap_ != nullptr);
784 :
785 m : return ::HeapFree(heap_, 0, block);
786 m : }
787 :
788 m : bool ScopedHeap::IsLFHBlock(const void* block) {
789 m : const uint32_t* ptr = reinterpret_cast<const uint32_t*>(block);
790 m : __try {
791 : // Search back a bounded distance for the LFH bin signature.
792 m : for (size_t i = 0; i < 32; ++i) {
793 m : const uint32_t kLFHBinSignature = 0xF0E0D0C0;
794 m : if (*ptr-- == kLFHBinSignature)
795 m : return true;
796 m : }
797 m : } __except (EXCEPTION_EXECUTE_HANDLER) { // NOLINT
798 : // Note that git cl format yields the above, which is then contrary to
799 : // our presubmit checks.
800 : // On exception, we conclude this isn't an LFH block.
801 m : }
802 :
803 m : return false;
804 m : }
805 :
806 m : refinery::Address ToAddress(const void* ptr) {
807 m : return static_cast<refinery::Address>(reinterpret_cast<uintptr_t>(ptr));
808 m : }
809 :
810 m : bool IsAppVerifierActive() {
811 : // TODO(siggi): This seems like a solid proxy for verifier present.
812 m : return ::GetModuleHandle(L"verifier.dll") != nullptr;
813 m : }
814 :
815 m : } // namespace testing
|