1 : // Copyright 2012 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/agent/asan/runtime.h"
16 :
17 : #include <algorithm>
18 : #include <utility>
19 : #include <vector>
20 :
21 : #include "base/bind.h"
22 : #include "base/command_line.h"
23 : #include "base/environment.h"
24 : #include "base/file_version_info.h"
25 : #include "base/logging.h"
26 : #include "base/path_service.h"
27 : #include "base/rand_util.h"
28 : #include "base/version.h"
29 : #include "base/strings/string_number_conversions.h"
30 : #include "base/strings/stringprintf.h"
31 : #include "base/strings/sys_string_conversions.h"
32 : #include "base/strings/utf_string_conversions.h"
33 : #include "base/win/pe_image.h"
34 : #include "base/win/wrapped_window_proc.h"
35 : #include "syzygy/agent/asan/block.h"
36 : #include "syzygy/agent/asan/crt_interceptors.h"
37 : #include "syzygy/agent/asan/heap_checker.h"
38 : #include "syzygy/agent/asan/logger.h"
39 : #include "syzygy/agent/asan/memory_interceptors.h"
40 : #include "syzygy/agent/asan/memory_interceptors_patcher.h"
41 : #include "syzygy/agent/asan/page_protection_helpers.h"
42 : #include "syzygy/agent/asan/shadow.h"
43 : #include "syzygy/agent/asan/stack_capture_cache.h"
44 : #include "syzygy/agent/asan/system_interceptors.h"
45 : #include "syzygy/agent/asan/windows_heap_adapter.h"
46 : #include "syzygy/agent/asan/memory_notifiers/shadow_memory_notifier.h"
47 : #include "syzygy/crashdata/crashdata.h"
48 : #include "syzygy/trace/client/client_utils.h"
49 : #include "syzygy/trace/protocol/call_trace_defs.h"
50 :
51 : // Disable all optimizations in this file. This entirely consists of code
52 : // that runs once at startup, or once during error handling. The latter code
53 : // is particularly useful to keep in its highest-fidelity form to help with
54 : // diagnosing edge cases during crash processing.
55 : #pragma optimize("", off)
56 : #pragma auto_inline(off)
57 :
58 : namespace agent {
59 : namespace asan {
60 :
61 : namespace {
62 :
63 : using agent::asan::AsanLogger;
64 : using agent::asan::StackCaptureCache;
65 : using agent::asan::WindowsHeapAdapter;
66 : using base::win::WinProcExceptionFilter;
67 :
68 : // Signatures of the various Breakpad functions for setting custom crash
69 : // key-value pairs.
70 : // Post r194002.
71 : typedef void(__cdecl* SetCrashKeyValuePairPtr)(const char*, const char*);
72 : // Post r217590.
73 : typedef void(__cdecl* SetCrashKeyValueImplPtr)(const wchar_t*, const wchar_t*);
74 :
75 : // Signature of enhanced crash reporting functions.
76 : typedef void(__cdecl* ReportCrashWithProtobufPtr)(EXCEPTION_POINTERS* info,
77 : const char* protobuf,
78 : size_t protobuf_length);
79 : typedef void(__cdecl* ReportCrashWithProtobufAndMemoryRangesPtr)(
80 : EXCEPTION_POINTERS* info,
81 : const char* protobuf,
82 : size_t protobuf_length,
83 : const void* const* base_addresses,
84 : const size_t* lengths);
85 :
86 : // Collects the various Breakpad-related exported functions.
87 : struct BreakpadFunctions {
88 : // The Breakpad crash reporting entry point.
89 : WinProcExceptionFilter crash_for_exception_ptr;
90 :
91 : // The optional enhanced crash reporting entry points.
92 : ReportCrashWithProtobufPtr report_crash_with_protobuf_ptr;
93 : ReportCrashWithProtobufAndMemoryRangesPtr
94 : report_crash_with_protobuf_and_memory_ranges_ptr;
95 :
96 : // Various flavours of the custom key-value setting function. The version
97 : // exported depends on the version of Chrome. It is possible for both of these
98 : // to be NULL even if crash_for_exception_ptr is not NULL.
99 : SetCrashKeyValuePairPtr set_crash_key_value_pair_ptr;
100 : SetCrashKeyValueImplPtr set_crash_key_value_impl_ptr;
101 : };
102 :
103 : // The static breakpad functions. All runtimes share these. This is under
104 : // AsanRuntime::lock_.
105 : BreakpadFunctions breakpad_functions = {};
106 :
107 : // A custom exception code we use to indicate that the exception originated
108 : // from Asan, and shouldn't be processed again by our unhandled exception
109 : // handler. This value has been created according to the rules here:
110 : // http://msdn.microsoft.com/en-us/library/windows/hardware/ff543026(v=vs.85).aspx
111 : // See winerror.h for more details.
112 : static const DWORD kAsanFacility = 0x68B; // No more than 11 bits.
113 : static const DWORD kAsanStatus = 0x5AD0; // No more than 16 bits.
114 : static const DWORD kAsanException =
115 : (3 << 30) | // Severity = error.
116 : (1 << 29) | // Customer defined code (not defined by MS).
117 : (kAsanFacility << 16) | // Facility code.
118 : kAsanStatus; // Status code.
119 : static_assert((kAsanFacility >> 11) == 0, "Too many facility bits.");
120 : static_assert((kAsanStatus >> 16) == 0, "Too many status bits.");
121 : static_assert((kAsanException & (3 << 27)) == 0,
122 : "Bits 27 and 28 should be clear.");
123 :
124 : // Raises an exception, first wrapping it an Asan specific exception. This
125 : // indicates to our unhandled exception handler that it doesn't need to
126 : // process the exception.
127 : void RaiseFilteredException(DWORD code,
128 : DWORD flags,
129 : DWORD num_args,
130 i : const ULONG_PTR* args) {
131 : // Retain the original arguments and craft a new exception.
132 : const ULONG_PTR arguments[4] = {
133 i : code, flags, num_args, reinterpret_cast<const ULONG_PTR>(args)};
134 i : ::RaiseException(kAsanException, 0, ARRAYSIZE(arguments), arguments);
135 i : }
136 :
137 : // The default error handler. It is expected that this will be bound in a
138 : // callback in the Asan runtime.
139 : // @param context The context when the error has been reported.
140 : // @param error_info The information about this error.
141 E : void DefaultErrorHandler(AsanErrorInfo* error_info) {
142 E : DCHECK_NE(reinterpret_cast<AsanErrorInfo*>(NULL), error_info);
143 :
144 : ULONG_PTR arguments[] = {reinterpret_cast<ULONG_PTR>(&error_info->context),
145 E : reinterpret_cast<ULONG_PTR>(error_info)};
146 :
147 E : ::DebugBreak();
148 :
149 : // This raises an error in such a way that the Asan unhandled exception
150 : // handler will not process it.
151 : RaiseFilteredException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0,
152 E : ARRAYSIZE(arguments), arguments);
153 E : }
154 :
155 : // Returns the breakpad crash reporting functions if breakpad is enabled for
156 : // the current executable.
157 : //
158 : // If we're running in the context of a breakpad enabled binary we can
159 : // report errors directly via that breakpad entry-point. This allows us
160 : // to report the exact context of the error without including the Asan RTL
161 : // in crash context, depending on where and when we capture the context.
162 : //
163 : // @param breakpad_functions The Breakpad functions structure to be populated.
164 : // @param enable_kasko Indicates if the Kasko functions should be used.
165 : // @returns true if we found breakpad functions, false otherwise.
166 : bool GetBreakpadFunctions(BreakpadFunctions* breakpad_functions,
167 E : bool enable_kasko) {
168 E : DCHECK_NE(reinterpret_cast<BreakpadFunctions*>(NULL), breakpad_functions);
169 :
170 : // Clear the structure.
171 E : ::memset(breakpad_functions, 0, sizeof(*breakpad_functions));
172 :
173 : // The named entry-point exposed to report a crash.
174 : static const char kCrashHandlerSymbol[] = "CrashForException";
175 :
176 : // The optional enhanced entry-points exposed to report a crash.
177 : static const char kReportCrashWithProtobufSymbol[] =
178 : "ReportCrashWithProtobuf";
179 : static const char kReportCrashWithProtobufAndMemoryRangesSymbol[] =
180 : "ReportCrashWithProtobufAndMemoryRanges";
181 :
182 : // The named entry-point exposed to annotate a crash with a key/value pair.
183 : static const char kSetCrashKeyValuePairSymbol[] = "SetCrashKeyValuePair";
184 : static const char kSetCrashKeyValueImplSymbol[] = "SetCrashKeyValueImpl";
185 :
186 : // Get a handle to the current executable image.
187 E : HMODULE exe_hmodule = ::GetModuleHandle(NULL);
188 :
189 : // Lookup the crash handler symbol.
190 : breakpad_functions->crash_for_exception_ptr =
191 : reinterpret_cast<WinProcExceptionFilter>(
192 E : ::GetProcAddress(exe_hmodule, kCrashHandlerSymbol));
193 :
194 E : if (enable_kasko) {
195 : // Lookup the optional enhanced crash handler symbol.
196 : breakpad_functions->report_crash_with_protobuf_ptr =
197 : reinterpret_cast<ReportCrashWithProtobufPtr>(
198 E : ::GetProcAddress(exe_hmodule, kReportCrashWithProtobufSymbol));
199 : // Lookup the optional enhanced crash handler symbol.
200 : breakpad_functions->report_crash_with_protobuf_and_memory_ranges_ptr =
201 : reinterpret_cast<ReportCrashWithProtobufAndMemoryRangesPtr>(
202 : ::GetProcAddress(exe_hmodule,
203 E : kReportCrashWithProtobufAndMemoryRangesSymbol));
204 : }
205 :
206 : if (breakpad_functions->crash_for_exception_ptr == NULL &&
207 : breakpad_functions->report_crash_with_protobuf_ptr == NULL &&
208 : breakpad_functions->report_crash_with_protobuf_and_memory_ranges_ptr ==
209 E : NULL) {
210 E : return false;
211 : }
212 :
213 : // Lookup the crash annotation symbol.
214 : breakpad_functions->set_crash_key_value_pair_ptr =
215 : reinterpret_cast<SetCrashKeyValuePairPtr>(
216 E : ::GetProcAddress(exe_hmodule, kSetCrashKeyValuePairSymbol));
217 : breakpad_functions->set_crash_key_value_impl_ptr =
218 : reinterpret_cast<SetCrashKeyValueImplPtr>(
219 E : ::GetProcAddress(exe_hmodule, kSetCrashKeyValueImplSymbol));
220 :
221 E : return true;
222 E : }
223 :
224 : // Sets a crash key using the given breakpad function. The breakpad functions
225 : // are passed by value so a stack copy is made.
226 : void SetCrashKeyValuePair(BreakpadFunctions breakpad_functions,
227 : const char* key,
228 E : const char* value) {
229 E : if (breakpad_functions.set_crash_key_value_pair_ptr != NULL) {
230 i : breakpad_functions.set_crash_key_value_pair_ptr(key, value);
231 i : return;
232 : }
233 :
234 E : if (breakpad_functions.set_crash_key_value_impl_ptr != NULL) {
235 i : std::wstring wkey = base::UTF8ToWide(key);
236 i : std::wstring wvalue = base::UTF8ToWide(value);
237 : breakpad_functions.set_crash_key_value_impl_ptr(wkey.c_str(),
238 i : wvalue.c_str());
239 i : return;
240 : }
241 :
242 : return;
243 E : }
244 :
245 : // Writes some early crash keys. These will be present even if SyzyAsan crashes
246 : // and can be used to help triage those bugs.
247 : void SetEarlyCrashKeys(BreakpadFunctions breakpad_functions,
248 E : AsanRuntime* runtime) {
249 E : DCHECK_NE(static_cast<AsanRuntime*>(nullptr), runtime);
250 :
251 : SetCrashKeyValuePair(
252 : breakpad_functions, "asan-random-key",
253 E : base::StringPrintf("%016llx", runtime->random_key()).c_str());
254 :
255 E : if (runtime->params().feature_randomization) {
256 : SetCrashKeyValuePair(
257 : breakpad_functions, "asan-feature-set",
258 i : base::UintToString(runtime->enabled_features()).c_str());
259 : }
260 E : }
261 :
262 : // This sets early crash keys for sufficiently modern versions of Chrome that
263 : // are known to support this.
264 : void SetEarlyCrashKeysForModernChrome(BreakpadFunctions breakpad_functions,
265 E : AsanRuntime* runtime) {
266 : // Modern Chrome versions use SetCrashKeyValueImpl exclusively.
267 E : if (breakpad_functions.set_crash_key_value_impl_ptr == nullptr)
268 E : return;
269 :
270 : // The process needs to be an instance of "chrome.exe".
271 i : base::FilePath path;
272 i : if (!PathService::Get(base::FILE_EXE, &path))
273 i : return;
274 i : if (path.BaseName().value() != L"chrome.exe")
275 i : return;
276 :
277 : scoped_ptr<FileVersionInfo> version_info(
278 i : FileVersionInfo::CreateFileVersionInfo(path));
279 i : if (!version_info.get())
280 i : return;
281 :
282 : // The version string may have the format "0.1.2.3 (baadf00d)". The
283 : // revision hash must be stripped in order to use base::Version.
284 i : std::string v = base::WideToUTF8(version_info->product_version());
285 i : size_t offset = v.find_first_not_of("0123456789.");
286 i : if (offset != v.npos)
287 i : v.resize(offset);
288 :
289 : // Ensure the version is sufficiently new.
290 i : base::Version version(v);
291 i : if (!version.IsValid())
292 i : return;
293 i : if (version.IsOlderThan("36.0.0.0"))
294 i : return;
295 :
296 : // Set a crash key that indicates that early crash keys were successfully
297 : // set and then set the remaining early crash keys themselves.
298 i : SetCrashKeyValuePair(breakpad_functions, "asan-early-keys", "true");
299 i : SetEarlyCrashKeys(breakpad_functions, runtime);
300 E : }
301 :
302 : // Writes the appropriate crash keys for the given error. The breakpad
303 : // functions are passed by value so a stack copy is made.
304 : void SetCrashKeys(BreakpadFunctions breakpad_functions,
305 E : AsanErrorInfo* error_info) {
306 : DCHECK(breakpad_functions.crash_for_exception_ptr != NULL ||
307 : breakpad_functions.report_crash_with_protobuf_ptr != NULL ||
308 : breakpad_functions.report_crash_with_protobuf_and_memory_ranges_ptr !=
309 E : NULL);
310 E : DCHECK(error_info != NULL);
311 :
312 : // Reset the early crash keys, as they may not actually have been set.
313 E : SetEarlyCrashKeys(breakpad_functions, AsanRuntime::runtime());
314 :
315 : SetCrashKeyValuePair(breakpad_functions, "asan-error-type",
316 E : ErrorInfoAccessTypeToStr(error_info->error_type));
317 :
318 E : if (error_info->shadow_info[0] != '\0') {
319 : SetCrashKeyValuePair(breakpad_functions, "asan-error-message",
320 E : error_info->shadow_info);
321 : }
322 :
323 E : if (error_info->asan_parameters.feature_randomization) {
324 : SetCrashKeyValuePair(breakpad_functions, "asan-feature-set",
325 i : base::UintToString(error_info->feature_set).c_str());
326 : }
327 E : }
328 :
329 : // Initializes an exception record for an Asan crash.
330 : void InitializeExceptionRecord(const AsanErrorInfo* error_info,
331 : EXCEPTION_RECORD* record,
332 E : EXCEPTION_POINTERS* pointers) {
333 E : DCHECK_NE(static_cast<AsanErrorInfo*>(nullptr), error_info);
334 E : DCHECK_NE(static_cast<EXCEPTION_RECORD*>(nullptr), record);
335 E : DCHECK_NE(static_cast<EXCEPTION_POINTERS*>(nullptr), pointers);
336 :
337 E : ::memset(record, 0, sizeof(EXCEPTION_RECORD));
338 E : record->ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
339 E : record->ExceptionAddress = reinterpret_cast<PVOID>(error_info->context.Eip);
340 E : record->NumberParameters = 2;
341 : record->ExceptionInformation[0] =
342 E : reinterpret_cast<ULONG_PTR>(&error_info->context);
343 E : record->ExceptionInformation[1] = reinterpret_cast<ULONG_PTR>(error_info);
344 :
345 E : pointers->ExceptionRecord = record;
346 E : pointers->ContextRecord = const_cast<CONTEXT*>(&error_info->context);
347 E : }
348 :
349 : // Creates a serialized protobuf representing crash data. Also populates
350 : // |memory_ranges| with memory contents related to the crash.
351 : bool PopulateProtobufAndMemoryRanges(const AsanErrorInfo& error_info,
352 : std::string* protobuf,
353 E : MemoryRanges* memory_ranges) {
354 E : DCHECK_NE(static_cast<std::string*>(nullptr), protobuf);
355 E : crashdata::Value value;
356 : PopulateErrorInfo(AsanRuntime::runtime()->shadow(), error_info, &value,
357 E : memory_ranges);
358 E : if (!value.SerializeToString(protobuf))
359 i : return false;
360 E : return true;
361 E : }
362 :
363 : // The breakpad error handler. It is expected that this will be bound in a
364 : // callback in the Asan runtime.
365 : // @param breakpad_functions A struct containing pointers to the various
366 : // Breakpad reporting functions.
367 : // @param error_info The information about this error.
368 : // @note @p breakpad_function is passed by value so a stack copy is made.
369 : void BreakpadErrorHandler(BreakpadFunctions breakpad_functions,
370 E : AsanErrorInfo* error_info) {
371 : DCHECK(breakpad_functions.crash_for_exception_ptr != NULL ||
372 : breakpad_functions.report_crash_with_protobuf_ptr != NULL ||
373 : breakpad_functions.report_crash_with_protobuf_and_memory_ranges_ptr !=
374 E : NULL);
375 E : DCHECK(error_info != NULL);
376 :
377 E : SetCrashKeys(breakpad_functions, error_info);
378 :
379 E : EXCEPTION_RECORD exception = {};
380 E : EXCEPTION_POINTERS pointers = {};
381 E : InitializeExceptionRecord(error_info, &exception, &pointers);
382 :
383 E : if (breakpad_functions.report_crash_with_protobuf_and_memory_ranges_ptr) {
384 i : std::string protobuf;
385 i : MemoryRanges memory_ranges;
386 i : PopulateProtobufAndMemoryRanges(*error_info, &protobuf, &memory_ranges);
387 :
388 : // Convert the memory ranges to arrays.
389 i : std::vector<const void*> base_addresses;
390 i : std::vector<size_t> range_lengths;
391 i : for (const auto& val : memory_ranges) {
392 i : base_addresses.push_back(val.first);
393 i : range_lengths.push_back(val.second);
394 i : }
395 : // Null-terminate these two arrays.
396 i : base_addresses.push_back(nullptr);
397 i : range_lengths.push_back(0);
398 :
399 : breakpad_functions.report_crash_with_protobuf_and_memory_ranges_ptr(
400 : &pointers, protobuf.data(), protobuf.length(), base_addresses.data(),
401 i : range_lengths.data());
402 E : } else if (breakpad_functions.report_crash_with_protobuf_ptr) {
403 E : std::string protobuf;
404 E : PopulateProtobufAndMemoryRanges(*error_info, &protobuf, nullptr);
405 : breakpad_functions.report_crash_with_protobuf_ptr(
406 E : &pointers, protobuf.data(), protobuf.length());
407 E : } else {
408 E : breakpad_functions.crash_for_exception_ptr(&pointers);
409 : }
410 i : NOTREACHED();
411 i : }
412 :
413 : // A helper function to find if an intrusive list contains a given entry.
414 : // @param list The list in which we want to look for the entry.
415 : // @param item The entry we want to look for.
416 : // @returns true if the list contains this entry, false otherwise.
417 : bool HeapListContainsEntry(const LIST_ENTRY* list, const LIST_ENTRY* item) {
418 : LIST_ENTRY* current = list->Flink;
419 : while (current != NULL) {
420 : LIST_ENTRY* next_item = NULL;
421 : if (current->Flink != list) {
422 : next_item = current->Flink;
423 : }
424 :
425 : if (current == item) {
426 : return true;
427 : }
428 :
429 : current = next_item;
430 : }
431 : return false;
432 : }
433 :
434 : // A helper function to send a command to Windbg. Windbg should first receive
435 : // the ".ocommand ASAN" command to treat those messages as commands.
436 E : void AsanDbgCmd(const wchar_t* fmt, ...) {
437 E : if (!base::debug::BeingDebugged())
438 E : return;
439 : // The string should start with "ASAN" to be interpreted by the debugger as a
440 : // command.
441 i : std::wstring command_wstring = L"ASAN ";
442 : va_list args;
443 i : va_start(args, fmt);
444 :
445 : // Append the actual command to the wstring.
446 i : base::StringAppendV(&command_wstring, fmt, args);
447 :
448 : // Append "; g" to make sure that the debugger continues its execution after
449 : // executing this command. This is needed because when the .ocommand function
450 : // is used under Windbg the debugger will break on OutputDebugString.
451 i : command_wstring.append(L"; g");
452 :
453 i : OutputDebugString(command_wstring.c_str());
454 E : }
455 :
456 : // A helper function to print a message to Windbg's console.
457 E : void AsanDbgMessage(const wchar_t* fmt, ...) {
458 E : if (!base::debug::BeingDebugged())
459 E : return;
460 : // Prepend the message with the .echo command so it'll be printed into the
461 : // debugger's console.
462 i : std::wstring message_wstring = L".echo ";
463 : va_list args;
464 i : va_start(args, fmt);
465 :
466 : // Append the actual message to the wstring.
467 i : base::StringAppendV(&message_wstring, fmt, args);
468 :
469 : // Treat the message as a command to print it.
470 i : AsanDbgCmd(message_wstring.c_str());
471 E : }
472 :
473 : // Switch to the caller's context and print its stack trace in Windbg.
474 : void AsanDbgPrintContext(const CONTEXT& context) {
475 : if (!base::debug::BeingDebugged())
476 : return;
477 : AsanDbgMessage(L"Caller's context (%p) and stack trace:", &context);
478 : AsanDbgCmd(L".cxr %p; kv", reinterpret_cast<uint32>(&context));
479 : }
480 :
481 : // Returns the maximum allocation size that can be made safely. This leaves
482 : // space for child function frames, ideally enough for Breakpad to do its
483 : // work.
484 E : size_t MaxSafeAllocaSize() {
485 : // We leave 5KB of stack space for Breakpad and other crash reporting
486 : // machinery.
487 E : const size_t kReservedStack = 5 * 1024;
488 :
489 : // Find the base of the stack.
490 E : MEMORY_BASIC_INFORMATION mbi = {};
491 E : void* stack = &mbi;
492 E : if (VirtualQuery(stack, &mbi, sizeof(mbi)) == 0)
493 i : return 0;
494 : size_t max_size = reinterpret_cast<uint8*>(stack) -
495 E : reinterpret_cast<uint8*>(mbi.AllocationBase);
496 E : max_size -= std::min(max_size, kReservedStack);
497 E : return max_size;
498 E : }
499 :
500 : // Performs a dynamic stack allocation of at most |size| bytes. Sets the actual
501 : // size of the allocation and the pointer to it by modifying |size| and |result|
502 : // directly.
503 : #define SAFE_ALLOCA(size, result) \
504 : { \
505 : size_t max_size = MaxSafeAllocaSize(); \
506 : size = std::min(size, max_size); \
507 : result = _alloca(size); \
508 : if (result == NULL) \
509 : size = 0; \
510 : }
511 :
512 : // Runs the heap checker if enabled. If heap corruption is found serializes
513 : // the results to the stack and modifies the |error_info| structure.
514 : #define CHECK_HEAP_CORRUPTION(runtime, error_info) \
515 : (error_info)->heap_is_corrupt = false; \
516 : if (!((runtime)->params_.check_heap_on_failure)) { \
517 : runtime_->logger_->Write( \
518 : "SyzyASAN: Heap checker disabled, ignoring exception."); \
519 : } else { \
520 : runtime_->logger_->Write( \
521 : "SyzyASAN: Heap checker enabled, processing exception."); \
522 : AutoHeapManagerLock lock((runtime)->heap_manager_.get()); \
523 : HeapChecker heap_checker((runtime)->shadow()); \
524 : HeapChecker::CorruptRangesVector corrupt_ranges; \
525 : heap_checker.IsHeapCorrupt(&corrupt_ranges); \
526 : size_t size = (runtime)->CalculateCorruptHeapInfoSize(corrupt_ranges); \
527 : void* buffer = NULL; \
528 : if (size > 0) { \
529 : SAFE_ALLOCA(size, buffer); \
530 : (runtime) \
531 : ->WriteCorruptHeapInfo(corrupt_ranges, size, buffer, error_info); \
532 : } \
533 : }
534 :
535 i : void LaunchMessageBox(const base::StringPiece& message) {
536 : // TODO(chrisha): Consider making this close itself with a timeout to prevent
537 : // hangs on the waterfall.
538 i : ::MessageBoxA(nullptr, message.data(), nullptr, MB_OK | MB_ICONEXCLAMATION);
539 i : }
540 :
541 : } // namespace
542 :
543 E : base::Lock AsanRuntime::lock_;
544 : AsanRuntime* AsanRuntime::runtime_ = NULL;
545 : LPTOP_LEVEL_EXCEPTION_FILTER AsanRuntime::previous_uef_ = NULL;
546 : bool AsanRuntime::uef_installed_ = false;
547 :
548 : AsanRuntime::AsanRuntime()
549 : : logger_(),
550 : stack_cache_(),
551 : asan_error_callback_(),
552 : heap_manager_(),
553 : random_key_(::__rdtsc()),
554 E : enable_kasko_(true) {
555 E : ::common::SetDefaultAsanParameters(¶ms_);
556 E : starting_ticks_ = ::GetTickCount();
557 E : }
558 :
559 E : AsanRuntime::~AsanRuntime() {
560 E : }
561 :
562 E : bool AsanRuntime::SetUp(const std::wstring& flags_command_line) {
563 E : base::AutoLock auto_lock(lock_);
564 E : DCHECK(!runtime_);
565 E : runtime_ = this;
566 :
567 : // Setup the shadow memory first. If this fails the dynamic runtime can
568 : // safely disable the instrumentation.
569 E : if (!SetUpShadow())
570 i : return false;
571 :
572 : // Parse and propagate any flags set via the environment variable. This logs
573 : // failure for us.
574 E : if (!::common::ParseAsanParameters(flags_command_line, ¶ms_))
575 i : return false;
576 :
577 : // Initialize the command-line structures. This is needed so that
578 : // SetUpLogger() can include the command-line in the message announcing
579 : // this process. Note: this is mostly for debugging purposes.
580 E : base::CommandLine::Init(0, NULL);
581 :
582 : // Setup other global state.
583 E : common::StackCapture::Init();
584 E : StackCaptureCache::Init();
585 E : if (!SetUpMemoryNotifier())
586 i : return false;
587 E : if (!SetUpLogger())
588 i : return false;
589 E : if (!SetUpStackCache())
590 i : return false;
591 E : if (!SetUpHeapManager())
592 i : return false;
593 E : WindowsHeapAdapter::SetUp(heap_manager_.get());
594 :
595 E : if (params_.feature_randomization)
596 E : enabled_features_ = GenerateRandomFeatureSet();
597 :
598 : // Propagates the flags values to the different modules.
599 E : PropagateParams();
600 :
601 : // Register the error reporting callback to use if/when an Asan error is
602 : // detected. If we're able to resolve a breakpad error reporting function
603 : // then use that; otherwise, fall back to the default error handler.
604 : if (!params_.disable_breakpad_reporting &&
605 E : GetBreakpadFunctions(&breakpad_functions, enable_kasko_)) {
606 E : logger_->Write("SyzyASAN: Using Breakpad for error reporting.");
607 E : SetErrorCallBack(base::Bind(&BreakpadErrorHandler, breakpad_functions));
608 E : } else {
609 E : logger_->Write("SyzyASAN: Using default error reporting handler.");
610 E : SetErrorCallBack(base::Bind(&DefaultErrorHandler));
611 : }
612 :
613 : // Install the unhandled exception handler. This is only installed once
614 : // across all runtime instances in a process so we check that it hasn't
615 : // already been installed.
616 E : if (!uef_installed_) {
617 E : uef_installed_ = true;
618 E : previous_uef_ = ::SetUnhandledExceptionFilter(&UnhandledExceptionFilter);
619 : }
620 :
621 : // Finally, initialize the heap manager. This comes after parsing all
622 : // parameters as some decisions can only be made once.
623 E : heap_manager_->Init();
624 :
625 : // Set some early crash keys.
626 : // NOTE: This calls back into the executable process to set crash keys. This
627 : // only works for Breakpad/Kasko enabled processes, assuming we haven't
628 : // instrumented the executable. In the case of Chrome this works because we
629 : // have instrumented chrome.dll, which is loaded at runtime by chrome.exe,
630 : // which is already initialized by the time we get here.
631 : // TODO(chrisha): Either do this via an instrumented module entry hook, or
632 : // expose a client API in Kasko.
633 E : SetEarlyCrashKeysForModernChrome(breakpad_functions, this);
634 :
635 E : return true;
636 E : }
637 :
638 E : void AsanRuntime::TearDown() {
639 E : base::AutoLock auto_lock(lock_);
640 :
641 : // The WindowsHeapAdapter will only have been initialized if the heap manager
642 : // was successfully created and initialized.
643 E : if (heap_manager_.get() != nullptr)
644 E : WindowsHeapAdapter::TearDown();
645 E : TearDownHeapManager();
646 E : TearDownStackCache();
647 E : TearDownLogger();
648 E : TearDownMemoryNotifier();
649 E : TearDownShadow();
650 E : asan_error_callback_.Reset();
651 :
652 : // Unregister ourselves as the singleton runtime for UEF.
653 E : runtime_ = NULL;
654 :
655 : // In principle, we should also check that all the heaps have been destroyed
656 : // but this is not guaranteed to be the case in Chrome, so the heap list may
657 : // not be empty here.
658 E : }
659 :
660 E : void AsanRuntime::OnErrorImpl(AsanErrorInfo* error_info) {
661 E : DCHECK_NE(reinterpret_cast<AsanErrorInfo*>(NULL), error_info);
662 :
663 : // Copy the parameters into the crash report.
664 E : error_info->asan_parameters = params_;
665 E : error_info->feature_set = enabled_features_;
666 :
667 E : LogAsanErrorInfo(error_info);
668 :
669 E : if (params_.minidump_on_failure) {
670 E : DCHECK(logger_.get() != NULL);
671 E : std::string protobuf;
672 E : MemoryRanges memory_ranges;
673 E : PopulateProtobufAndMemoryRanges(*error_info, &protobuf, &memory_ranges);
674 :
675 : logger_->SaveMinidumpWithProtobufAndMemoryRanges(
676 E : &error_info->context, error_info, protobuf, memory_ranges);
677 E : }
678 :
679 E : if (params_.exit_on_failure) {
680 E : DCHECK(logger_.get() != NULL);
681 E : logger_->Stop();
682 E : exit(EXIT_FAILURE);
683 : }
684 E : }
685 :
686 E : void AsanRuntime::OnError(AsanErrorInfo* error_info) {
687 E : DCHECK_NE(reinterpret_cast<AsanErrorInfo*>(NULL), error_info);
688 :
689 : // Grab the global page protection lock to prevent page protection settings
690 : // from being modified while processing the error.
691 E : ::common::AutoRecursiveLock lock(block_protect_lock);
692 :
693 : // Unfortunately this is a giant macro, but it needs to be as it performs
694 : // stack allocations.
695 E : CHECK_HEAP_CORRUPTION(this, error_info);
696 :
697 E : OnErrorImpl(error_info);
698 :
699 : // Call the callback to handle this error.
700 E : DCHECK(!asan_error_callback_.is_null());
701 E : asan_error_callback_.Run(error_info);
702 E : }
703 :
704 E : void AsanRuntime::SetErrorCallBack(const AsanOnErrorCallBack& callback) {
705 E : asan_error_callback_ = callback;
706 E : }
707 :
708 E : bool AsanRuntime::SetUpShadow() {
709 : // If a non-trivial static shadow is provided, but it's the wrong size, then
710 : // this runtime is unable to support hotpatching and its being run in the
711 : // wrong memory model.
712 : if (asan_memory_interceptors_shadow_memory_size > 1 &&
713 E : asan_memory_interceptors_shadow_memory_size != Shadow::RequiredLength()) {
714 : static const char kMessage[] =
715 : "Runtime can't support the current memory model. LAA?";
716 i : LaunchMessageBox(kMessage);
717 i : NOTREACHED() << kMessage;
718 : }
719 :
720 : // Use the static shadow memory if possible.
721 E : if (asan_memory_interceptors_shadow_memory_size == Shadow::RequiredLength()) {
722 : shadow_.reset(new Shadow(asan_memory_interceptors_shadow_memory,
723 E : asan_memory_interceptors_shadow_memory_size));
724 E : } else {
725 : // Otherwise dynamically allocate the shadow memory.
726 i : shadow_.reset(new Shadow());
727 :
728 : // If the allocation fails, then return false.
729 i : if (shadow_->shadow() == nullptr)
730 i : return false;
731 :
732 : // Patch the memory interceptors to refer to the newly allocated shadow.
733 : // If this fails simply explode because it is unsafe to continue.
734 : CHECK(PatchMemoryInterceptorShadowReferences(
735 i : asan_memory_interceptors_shadow_memory, shadow_->shadow()));
736 : }
737 :
738 : // Setup the shadow and configure the various interceptors to use it.
739 E : shadow_->SetUp();
740 E : agent::asan::SetCrtInterceptorShadow(shadow_.get());
741 E : agent::asan::SetMemoryInterceptorShadow(shadow_.get());
742 E : agent::asan::SetSystemInterceptorShadow(shadow_.get());
743 :
744 E : return true;
745 E : }
746 :
747 E : void AsanRuntime::TearDownShadow() {
748 : // If this didn't successfully initialize then do nothing.
749 E : if (shadow_->shadow() == nullptr)
750 i : return;
751 :
752 E : shadow_->TearDown();
753 E : agent::asan::SetCrtInterceptorShadow(nullptr);
754 E : agent::asan::SetMemoryInterceptorShadow(nullptr);
755 E : agent::asan::SetSystemInterceptorShadow(nullptr);
756 : // Unpatch the probes if necessary.
757 E : if (shadow_->shadow() != asan_memory_interceptors_shadow_memory) {
758 : CHECK(PatchMemoryInterceptorShadowReferences(
759 i : shadow_->shadow(), asan_memory_interceptors_shadow_memory));
760 : }
761 E : shadow_.reset();
762 E : }
763 :
764 E : bool AsanRuntime::SetUpMemoryNotifier() {
765 E : DCHECK_NE(static_cast<Shadow*>(nullptr), shadow_.get());
766 E : DCHECK_NE(static_cast<uint8_t*>(nullptr), shadow_->shadow());
767 : DCHECK_EQ(static_cast<MemoryNotifierInterface*>(nullptr),
768 E : memory_notifier_.get());
769 : memory_notifiers::ShadowMemoryNotifier* memory_notifier =
770 E : new memory_notifiers::ShadowMemoryNotifier(shadow_.get());
771 E : memory_notifier->NotifyInternalUse(memory_notifier, sizeof(*memory_notifier));
772 E : memory_notifier_.reset(memory_notifier);
773 E : return true;
774 E : }
775 :
776 E : void AsanRuntime::TearDownMemoryNotifier() {
777 E : if (memory_notifier_.get() == nullptr)
778 i : return;
779 :
780 : memory_notifiers::ShadowMemoryNotifier* memory_notifier =
781 : reinterpret_cast<memory_notifiers::ShadowMemoryNotifier*>(
782 E : memory_notifier_.get());
783 : memory_notifier->NotifyReturnedToOS(memory_notifier,
784 E : sizeof(*memory_notifier));
785 E : memory_notifier_.reset(nullptr);
786 E : }
787 :
788 E : bool AsanRuntime::SetUpLogger() {
789 : DCHECK_NE(static_cast<MemoryNotifierInterface*>(nullptr),
790 E : memory_notifier_.get());
791 E : DCHECK_EQ(static_cast<AsanLogger*>(nullptr), logger_.get());
792 :
793 : // Setup variables we're going to use.
794 E : scoped_ptr<base::Environment> env(base::Environment::Create());
795 E : scoped_ptr<AsanLogger> client(new AsanLogger);
796 E : CHECK(env.get() != NULL);
797 E : CHECK(client.get() != NULL);
798 :
799 : // Initialize the client.
800 : client->set_instance_id(
801 E : base::UTF8ToWide(trace::client::GetInstanceIdForThisModule()));
802 E : client->Init();
803 :
804 : // Register the client singleton instance.
805 E : logger_.reset(client.release());
806 E : memory_notifier_->NotifyInternalUse(logger_.get(), sizeof(*logger_.get()));
807 :
808 E : return true;
809 E : }
810 :
811 E : void AsanRuntime::TearDownLogger() {
812 E : if (logger_.get() == nullptr)
813 i : return;
814 :
815 : DCHECK_NE(static_cast<MemoryNotifierInterface*>(nullptr),
816 E : memory_notifier_.get());
817 E : memory_notifier_->NotifyReturnedToOS(logger_.get(), sizeof(*logger_.get()));
818 E : logger_.reset();
819 E : }
820 :
821 E : bool AsanRuntime::SetUpStackCache() {
822 : DCHECK_NE(static_cast<MemoryNotifierInterface*>(nullptr),
823 E : memory_notifier_.get());
824 E : DCHECK_NE(static_cast<AsanLogger*>(nullptr), logger_.get());
825 E : DCHECK_EQ(static_cast<StackCaptureCache*>(nullptr), stack_cache_.get());
826 : stack_cache_.reset(
827 E : new StackCaptureCache(logger_.get(), memory_notifier_.get()));
828 : memory_notifier_->NotifyInternalUse(stack_cache_.get(),
829 E : sizeof(*stack_cache_.get()));
830 :
831 E : return true;
832 E : }
833 :
834 E : void AsanRuntime::TearDownStackCache() {
835 E : if (stack_cache_.get() == nullptr)
836 i : return;
837 :
838 : DCHECK_NE(static_cast<MemoryNotifierInterface*>(nullptr),
839 E : memory_notifier_.get());
840 E : DCHECK_NE(static_cast<AsanLogger*>(nullptr), logger_.get());
841 :
842 E : stack_cache_->LogStatistics();
843 : memory_notifier_->NotifyReturnedToOS(stack_cache_.get(),
844 E : sizeof(*stack_cache_.get()));
845 E : stack_cache_.reset();
846 E : }
847 :
848 E : bool AsanRuntime::SetUpHeapManager() {
849 : DCHECK_NE(static_cast<MemoryNotifierInterface*>(nullptr),
850 E : memory_notifier_.get());
851 E : DCHECK_NE(static_cast<AsanLogger*>(nullptr), logger_.get());
852 E : DCHECK_NE(static_cast<StackCaptureCache*>(nullptr), stack_cache_.get());
853 : DCHECK_EQ(static_cast<heap_managers::BlockHeapManager*>(nullptr),
854 E : heap_manager_.get());
855 :
856 : heap_manager_.reset(new heap_managers::BlockHeapManager(
857 E : shadow(), stack_cache_.get(), memory_notifier_.get()));
858 : memory_notifier_->NotifyInternalUse(heap_manager_.get(),
859 E : sizeof(*heap_manager_.get()));
860 :
861 : // Configure the heap manager to notify us on heap corruption.
862 : heap_manager_->SetHeapErrorCallback(
863 E : base::Bind(&AsanRuntime::OnError, base::Unretained(this)));
864 :
865 E : return true;
866 E : }
867 :
868 E : void AsanRuntime::TearDownHeapManager() {
869 E : if (stack_cache_.get() == nullptr)
870 i : return;
871 :
872 : DCHECK_NE(static_cast<MemoryNotifierInterface*>(nullptr),
873 E : memory_notifier_.get());
874 E : DCHECK_NE(static_cast<AsanLogger*>(nullptr), logger_.get());
875 E : DCHECK_NE(static_cast<StackCaptureCache*>(nullptr), stack_cache_.get());
876 :
877 : // Tear down the heap manager before we destroy it and lose our pointer
878 : // to it. This is necessary because the heap manager can raise errors
879 : // while tearing down the heap, which will in turn call back into the
880 : // block heap manager via the runtime.
881 E : heap_manager_->TearDownHeapManager();
882 : memory_notifier_->NotifyReturnedToOS(heap_manager_.get(),
883 E : sizeof(*heap_manager_.get()));
884 E : heap_manager_.reset();
885 E : }
886 :
887 E : bool AsanRuntime::GetAsanFlagsEnvVar(std::wstring* env_var_wstr) {
888 E : scoped_ptr<base::Environment> env(base::Environment::Create());
889 E : if (env.get() == NULL) {
890 i : LOG(ERROR) << "base::Environment::Create returned NULL.";
891 i : return false;
892 : }
893 :
894 : // If this fails, the environment variable simply does not exist.
895 E : std::string env_var_str;
896 E : if (!env->GetVar(::common::kSyzyAsanOptionsEnvVar, &env_var_str)) {
897 E : return true;
898 : }
899 :
900 E : *env_var_wstr = base::SysUTF8ToWide(env_var_str);
901 :
902 E : return true;
903 E : }
904 :
905 E : void AsanRuntime::PropagateParams() {
906 : // This function has to be kept in sync with the AsanParameters struct. These
907 : // checks will ensure that this is the case.
908 : static_assert(sizeof(::common::AsanParameters) == 60,
909 : "Must propagate parameters.");
910 : static_assert(::common::kAsanParametersVersion == 14,
911 : "Must update parameters version.");
912 :
913 : // Push the configured parameter values to the appropriate endpoints.
914 E : heap_manager_->set_parameters(params_);
915 E : StackCaptureCache::set_compression_reporting_period(params_.reporting_period);
916 : common::StackCapture::set_bottom_frames_to_skip(
917 E : params_.bottom_frames_to_skip);
918 E : stack_cache_->set_max_num_frames(params_.max_num_frames);
919 : // ignored_stack_ids is used locally by AsanRuntime.
920 E : logger_->set_log_as_text(params_.log_as_text);
921 : // exit_on_failure is used locally by AsanRuntime.
922 E : logger_->set_minidump_on_failure(params_.minidump_on_failure);
923 E : }
924 :
925 : size_t AsanRuntime::CalculateCorruptHeapInfoSize(
926 E : const HeapChecker::CorruptRangesVector& corrupt_ranges) {
927 : size_t n = corrupt_ranges.size() *
928 E : (sizeof(AsanCorruptBlockRange) + sizeof(AsanBlockInfo));
929 E : return n;
930 E : }
931 :
932 : void AsanRuntime::WriteCorruptHeapInfo(
933 : const HeapChecker::CorruptRangesVector& corrupt_ranges,
934 : size_t buffer_size,
935 : void* buffer,
936 E : AsanErrorInfo* error_info) {
937 : DCHECK((buffer_size == 0 && buffer == NULL) ||
938 E : (buffer_size != 0 && buffer != NULL));
939 E : DCHECK_NE(reinterpret_cast<AsanErrorInfo*>(NULL), error_info);
940 :
941 E : ::memset(buffer, 0, buffer_size);
942 :
943 E : error_info->heap_is_corrupt = false;
944 E : error_info->corrupt_range_count = 0;
945 E : error_info->corrupt_block_count = 0;
946 E : error_info->corrupt_ranges_reported = 0;
947 E : error_info->corrupt_ranges = NULL;
948 :
949 E : if (corrupt_ranges.empty())
950 i : return;
951 :
952 : // If we have corrupt ranges then set the aggregate fields.
953 E : error_info->heap_is_corrupt = true;
954 E : error_info->corrupt_range_count = corrupt_ranges.size();
955 E : for (size_t i = 0; i < corrupt_ranges.size(); ++i)
956 E : error_info->corrupt_block_count += corrupt_ranges[i].block_count;
957 :
958 : // We report a AsanCorruptBlockRange and at least one AsanBlockInfo per
959 : // corrupt range. Determine how many ranges we can report on.
960 : size_t range_count =
961 E : buffer_size / (sizeof(AsanCorruptBlockRange) + sizeof(AsanBlockInfo));
962 E : range_count = std::min(range_count, corrupt_ranges.size());
963 :
964 : // Allocate space for the corrupt range metadata.
965 E : uint8* cursor = reinterpret_cast<uint8*>(buffer);
966 E : uint8* buffer_end = cursor + buffer_size;
967 E : error_info->corrupt_ranges = reinterpret_cast<AsanCorruptBlockRange*>(cursor);
968 E : cursor += range_count * sizeof(AsanCorruptBlockRange);
969 E : error_info->corrupt_range_count = corrupt_ranges.size();
970 E : error_info->corrupt_ranges_reported = range_count;
971 :
972 : // Allocate space for the corrupt block metadata.
973 E : size_t block_count = (buffer_end - cursor) / sizeof(AsanBlockInfo);
974 E : AsanBlockInfo* block_infos = reinterpret_cast<AsanBlockInfo*>(cursor);
975 E : cursor += block_count * sizeof(AsanBlockInfo);
976 :
977 : // Write as many corrupt block ranges as we have room for. This is
978 : // effectively random as it is by order of address.
979 E : for (size_t i = 0; i < range_count; ++i) {
980 : // Copy the information about the corrupt range.
981 E : error_info->corrupt_ranges[i] = corrupt_ranges[i];
982 :
983 : // Allocate space for the first block of this range on the stack.
984 : // TODO(sebmarchand): Report more blocks if necessary.
985 E : AsanBlockInfo* asan_block_info = block_infos;
986 E : error_info->corrupt_ranges[i].block_info = block_infos;
987 E : error_info->corrupt_ranges[i].block_info_count = 1;
988 E : ++block_infos;
989 :
990 : // Use a shadow walker to find the first corrupt block in this range and
991 : // copy its metadata.
992 : ShadowWalker shadow_walker(
993 : shadow(), false,
994 : reinterpret_cast<const uint8*>(corrupt_ranges[i].address),
995 : reinterpret_cast<const uint8*>(corrupt_ranges[i].address) +
996 E : corrupt_ranges[i].length);
997 E : BlockInfo block_info = {};
998 E : CHECK(shadow_walker.Next(&block_info));
999 : // The heap checker removes block protections as it goes, so this block
1000 : // should be readable. However, remove page protections just to be sure.
1001 : // They are left turned off so that the minidump generation can introspect
1002 : // the block.
1003 E : BlockProtectNone(block_info, shadow());
1004 : ErrorInfoGetAsanBlockInfo(shadow(), block_info, stack_cache_.get(),
1005 E : asan_block_info);
1006 E : DCHECK_EQ(kDataIsCorrupt, asan_block_info->analysis.block_state);
1007 E : }
1008 :
1009 : return;
1010 E : }
1011 :
1012 E : void AsanRuntime::LogAsanErrorInfo(AsanErrorInfo* error_info) {
1013 E : DCHECK_NE(reinterpret_cast<AsanErrorInfo*>(NULL), error_info);
1014 :
1015 E : const char* bug_descr = ErrorInfoAccessTypeToStr(error_info->error_type);
1016 E : if (logger_->log_as_text()) {
1017 : std::string output(base::StringPrintf(
1018 : "SyzyASAN error: %s on address 0x%08X (stack_id=0x%08X)\n", bug_descr,
1019 E : error_info->location, error_info->crash_stack_id));
1020 E : if (error_info->access_mode != agent::asan::ASAN_UNKNOWN_ACCESS) {
1021 E : const char* access_mode_str = NULL;
1022 E : if (error_info->access_mode == agent::asan::ASAN_READ_ACCESS)
1023 E : access_mode_str = "READ";
1024 E : else
1025 E : access_mode_str = "WRITE";
1026 : base::StringAppendF(&output, "%s of size %d at 0x%08X\n", access_mode_str,
1027 E : error_info->access_size, error_info->location);
1028 : }
1029 :
1030 : // Log the failure and stack.
1031 E : logger_->WriteWithContext(output, error_info->context);
1032 :
1033 E : logger_->Write(error_info->shadow_info);
1034 E : if (error_info->block_info.free_stack_size != 0U) {
1035 : logger_->WriteWithStackTrace("freed here:\n",
1036 : error_info->block_info.free_stack,
1037 E : error_info->block_info.free_stack_size);
1038 : }
1039 E : if (error_info->block_info.alloc_stack_size != NULL) {
1040 : logger_->WriteWithStackTrace("previously allocated here:\n",
1041 : error_info->block_info.alloc_stack,
1042 E : error_info->block_info.alloc_stack_size);
1043 : }
1044 E : if (error_info->error_type >= USE_AFTER_FREE) {
1045 E : std::string shadow_text;
1046 E : shadow()->AppendShadowMemoryText(error_info->location, &shadow_text);
1047 E : logger_->Write(shadow_text);
1048 E : }
1049 E : }
1050 :
1051 : // Print the base of the Windbg help message.
1052 : AsanDbgMessage(L"An Asan error has been found (%ls), here are the details:",
1053 E : base::SysUTF8ToWide(bug_descr).c_str());
1054 :
1055 : // Print the Windbg information to display the allocation stack if present.
1056 E : if (error_info->block_info.alloc_stack_size != NULL) {
1057 E : AsanDbgMessage(L"Allocation stack trace:");
1058 : AsanDbgCmd(L"dps %p l%d", error_info->block_info.alloc_stack,
1059 E : error_info->block_info.alloc_stack_size);
1060 : }
1061 :
1062 : // Print the Windbg information to display the free stack if present.
1063 E : if (error_info->block_info.free_stack_size != NULL) {
1064 E : AsanDbgMessage(L"Free stack trace:");
1065 : AsanDbgCmd(L"dps %p l%d", error_info->block_info.free_stack,
1066 E : error_info->block_info.free_stack_size);
1067 : }
1068 E : }
1069 :
1070 E : AsanFeatureSet AsanRuntime::GenerateRandomFeatureSet() {
1071 : AsanFeatureSet enabled_features =
1072 E : static_cast<AsanFeatureSet>(base::RandGenerator(ASAN_FEATURE_MAX));
1073 E : DCHECK_LT(enabled_features, ASAN_FEATURE_MAX);
1074 : enabled_features = static_cast<AsanFeatureSet>(
1075 E : static_cast<size_t>(enabled_features) & kAsanDisabledFeatureMask);
1076 :
1077 : heap_manager_->enable_page_protections_ =
1078 E : (enabled_features & ASAN_FEATURE_ENABLE_PAGE_PROTECTIONS) != 0;
1079 : params_.enable_large_block_heap =
1080 E : (enabled_features & ASAN_FEATURE_ENABLE_LARGE_BLOCK_HEAP) != 0;
1081 E : enable_kasko_ = (enabled_features & ASAN_FEATURE_ENABLE_KASKO) != 0;
1082 E : return enabled_features;
1083 E : }
1084 :
1085 E : void AsanRuntime::GetBadAccessInformation(AsanErrorInfo* error_info) {
1086 E : base::AutoLock lock(lock_);
1087 :
1088 : // Checks if this is an access to an internal structure or if it's an access
1089 : // in the upper region of the memory (over the 2 GB limit).
1090 : if ((reinterpret_cast<size_t>(error_info->location) & (1 << 31)) != 0 ||
1091 : shadow()->GetShadowMarkerForAddress(error_info->location) ==
1092 E : kAsanMemoryMarker) {
1093 E : error_info->error_type = WILD_ACCESS;
1094 E : } else if (shadow()->GetShadowMarkerForAddress(error_info->location) ==
1095 E : kInvalidAddressMarker) {
1096 E : error_info->error_type = INVALID_ADDRESS;
1097 E : } else {
1098 E : ErrorInfoGetBadAccessInformation(shadow(), stack_cache_.get(), error_info);
1099 : }
1100 E : }
1101 :
1102 E : bool AsanRuntime::allocation_filter_flag() {
1103 E : return heap_manager_->allocation_filter_flag();
1104 E : }
1105 :
1106 E : void AsanRuntime::set_allocation_filter_flag(bool value) {
1107 E : heap_manager_->set_allocation_filter_flag(value);
1108 E : }
1109 :
1110 E : void AsanRuntime::AddThreadId(uint32 thread_id) {
1111 E : DCHECK_NE(0u, thread_id);
1112 E : base::AutoLock lock(thread_ids_lock_);
1113 E : thread_ids_.insert(thread_id);
1114 E : }
1115 :
1116 E : bool AsanRuntime::ThreadIdIsValid(uint32 thread_id) {
1117 E : base::AutoLock lock(thread_ids_lock_);
1118 E : return thread_ids_.count(thread_id) > 0;
1119 E : }
1120 :
1121 E : bool AsanRuntime::HeapIdIsValid(HeapManagerInterface::HeapId heap_id) {
1122 : // Consider dying heaps in this query, as they are still valid from the
1123 : // point of view of an error report.
1124 E : return heap_manager_->IsValidHeapIdUnlocked(heap_id, true);
1125 E : }
1126 :
1127 E : HeapType AsanRuntime::GetHeapType(HeapManagerInterface::HeapId heap_id) {
1128 E : return heap_manager_->GetHeapTypeUnlocked(heap_id);
1129 E : }
1130 :
1131 i : int AsanRuntime::CrashForException(EXCEPTION_POINTERS* exception) {
1132 i : return ExceptionFilterImpl(false, exception);
1133 i : }
1134 :
1135 : LONG WINAPI
1136 E : AsanRuntime::UnhandledExceptionFilter(struct _EXCEPTION_POINTERS* exception) {
1137 E : return ExceptionFilterImpl(true, exception);
1138 E : }
1139 :
1140 : // static
1141 : LONG AsanRuntime::ExceptionFilterImpl(bool is_unhandled,
1142 E : EXCEPTION_POINTERS* exception) {
1143 : // This ensures that we don't have multiple colliding crashes being processed
1144 : // simultaneously.
1145 E : base::AutoLock auto_lock(lock_);
1146 :
1147 : // Grab the global page protection lock to prevent page protection settings
1148 : // from being modified while processing the error.
1149 E : ::common::AutoRecursiveLock lock(block_protect_lock);
1150 :
1151 : // This is needed for unittesting.
1152 E : runtime_->logger_->Write("SyzyASAN: Handling an exception.");
1153 :
1154 : // If we're bound to a runtime then look for heap corruption and
1155 : // potentially augment the exception record. This needs to exist in the
1156 : // outermost scope of this function as pointers to it may be passed to
1157 : // other exception handlers.
1158 E : AsanErrorInfo error_info = {};
1159 :
1160 : // If this is set to true then an Asan error will be emitted.
1161 E : bool emit_asan_error = false;
1162 : // Will be set to true if a near-nullptr access is detected.
1163 E : bool near_nullptr_access = false;
1164 :
1165 E : Shadow* shadow = runtime_->shadow();
1166 : // If this is an exception that we launched then extract the original
1167 : // exception data and continue processing it.
1168 E : if (exception->ExceptionRecord->ExceptionCode == kAsanException) {
1169 i : ULONG_PTR* args = exception->ExceptionRecord->ExceptionInformation;
1170 i : DWORD code = args[0];
1171 i : DWORD flags = args[1];
1172 i : DWORD nargs = args[2];
1173 i : const ULONG_PTR* orig_args = reinterpret_cast<const ULONG_PTR*>(args[3]);
1174 :
1175 : // Rebuild the exception with the original exception data.
1176 i : exception->ExceptionRecord->ExceptionCode = code;
1177 i : exception->ExceptionRecord->ExceptionFlags = flags;
1178 i : exception->ExceptionRecord->NumberParameters = nargs;
1179 i : for (DWORD i = 0; i < nargs; ++i)
1180 i : args[i] = orig_args[i];
1181 E : } else if (runtime_) {
1182 : // Initialize this as if heap corruption is the primary error being
1183 : // reported. This will be overridden by the access violation handling
1184 : // code below, if necessary.
1185 E : error_info.location = exception->ExceptionRecord->ExceptionAddress;
1186 E : error_info.context = *exception->ContextRecord;
1187 E : error_info.error_type = CORRUPT_HEAP;
1188 E : error_info.access_mode = ASAN_UNKNOWN_ACCESS;
1189 :
1190 : // It is possible that access violations are due to page protections of a
1191 : // sufficiently large allocation. In this case the shadow will contain
1192 : // block redzone markers at the given address. We take over the exception
1193 : // if that is the case.
1194 : if (exception->ExceptionRecord->ExceptionCode ==
1195 : EXCEPTION_ACCESS_VIOLATION &&
1196 : exception->ExceptionRecord->NumberParameters >= 2 &&
1197 E : exception->ExceptionRecord->ExceptionInformation[0] <= 1) {
1198 : void* address = reinterpret_cast<void*>(
1199 E : exception->ExceptionRecord->ExceptionInformation[1]);
1200 :
1201 : // The first 64k of user memory is unmapped in Windows, we treat those as
1202 : // near-nullptr accesses.
1203 : near_nullptr_access =
1204 E : address < reinterpret_cast<void*>(Shadow::kAddressLowerBound);
1205 :
1206 E : ShadowMarker marker = shadow->GetShadowMarkerForAddress(address);
1207 : if ((!near_nullptr_access ||
1208 : runtime_->params().report_invalid_accesses) &&
1209 : ShadowMarkerHelper::IsRedzone(marker) &&
1210 E : ShadowMarkerHelper::IsActiveBlock(marker)) {
1211 E : BlockInfo block_info = {};
1212 E : if (shadow->BlockInfoFromShadow(address, &block_info)) {
1213 : // Page protections have to be removed from this block otherwise our
1214 : // own inspection will cause further errors.
1215 E : BlockProtectNone(block_info, runtime_->shadow());
1216 :
1217 : // Useful for unittesting.
1218 : runtime_->logger_->Write(
1219 : "SyzyASAN: Caught an invalid access via "
1220 E : "an access violation exception.");
1221 :
1222 : // Override the invalid access location with the faulting address,
1223 : // not the code address.
1224 E : error_info.location = address;
1225 : // The exact access size isn't reported so simply set it to 1 (an
1226 : // obvious lower bound).
1227 E : error_info.access_size = 1;
1228 : // Determine if this is a read or a write using information in the
1229 : // exception record.
1230 : error_info.access_mode =
1231 : exception->ExceptionRecord->ExceptionInformation[0] == 0
1232 : ? ASAN_READ_ACCESS
1233 E : : ASAN_WRITE_ACCESS;
1234 :
1235 : // Fill out the rest of the bad access information.
1236 : ErrorInfoGetBadAccessInformation(shadow, runtime_->stack_cache(),
1237 E : &error_info);
1238 E : emit_asan_error = true;
1239 : }
1240 : }
1241 : }
1242 :
1243 E : CHECK_HEAP_CORRUPTION(runtime_, &error_info);
1244 E : if (error_info.heap_is_corrupt)
1245 E : emit_asan_error = true;
1246 : }
1247 :
1248 : // If an Asan error was detected then report it via the logger and take over
1249 : // the exception record.
1250 E : EXCEPTION_RECORD record = {};
1251 E : if (emit_asan_error) {
1252 E : if (near_nullptr_access) {
1253 : runtime_->logger_->Write(
1254 E : "SyzyASAN: Caught a near-nullptr access with heap corruption.");
1255 : }
1256 :
1257 : // Log the error via the usual means.
1258 E : runtime_->OnErrorImpl(&error_info);
1259 :
1260 : // If we have Breakpad integration then set our crash keys.
1261 E : if (breakpad_functions.crash_for_exception_ptr != NULL)
1262 E : SetCrashKeys(breakpad_functions, &error_info);
1263 :
1264 : // Remember the old exception record.
1265 E : EXCEPTION_RECORD* old_record = exception->ExceptionRecord;
1266 :
1267 : // Initialize the exception record and chain the original exception to it.
1268 E : InitializeExceptionRecord(&error_info, &record, exception);
1269 E : record.ExceptionRecord = old_record;
1270 E : } else if (near_nullptr_access &&
1271 E : !runtime_->params().report_invalid_accesses) {
1272 : // For unit testing. Record that we ignored a near-nullptr access.
1273 : runtime_->logger_->Write(
1274 E : "SyzyASAN: Ignoring a near-nullptr access without heap corruption.");
1275 : }
1276 :
1277 E : if (emit_asan_error && breakpad_functions.report_crash_with_protobuf_ptr) {
1278 : // This method is expected to terminate the process.
1279 E : std::string protobuf;
1280 E : PopulateProtobufAndMemoryRanges(error_info, &protobuf, nullptr);
1281 : breakpad_functions.report_crash_with_protobuf_ptr(
1282 E : exception, protobuf.data(), protobuf.length());
1283 E : return EXCEPTION_CONTINUE_SEARCH;
1284 : }
1285 :
1286 E : if (is_unhandled) {
1287 : // Pass the buck to the next exception handler. If the process is Breakpad
1288 : // enabled this will eventually make its way there.
1289 E : if (previous_uef_ != NULL)
1290 E : return (*previous_uef_)(exception);
1291 : }
1292 :
1293 : // If we've found an Asan error then pass the buck to Breakpad directly,
1294 : // if possible. Otherwise, simply let things take their natural course.
1295 i : if (emit_asan_error && breakpad_functions.crash_for_exception_ptr)
1296 i : return (*breakpad_functions.crash_for_exception_ptr)(exception);
1297 :
1298 : // We can't do anything with this, so let the system deal with it.
1299 i : return EXCEPTION_CONTINUE_SEARCH;
1300 i : }
1301 :
1302 : } // namespace asan
1303 : } // namespace agent
|