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