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/asan_runtime.h"
16 :
17 : #include <algorithm>
18 : #include <vector>
19 :
20 : #include "base/bind.h"
21 : #include "base/command_line.h"
22 : #include "base/environment.h"
23 : #include "base/logging.h"
24 : #include "base/strings/stringprintf.h"
25 : #include "base/strings/sys_string_conversions.h"
26 : #include "base/strings/utf_string_conversions.h"
27 : #include "base/win/pe_image.h"
28 : #include "base/win/wrapped_window_proc.h"
29 : #include "syzygy/agent/asan/asan_logger.h"
30 : #include "syzygy/agent/asan/block.h"
31 : #include "syzygy/agent/asan/heap_checker.h"
32 : #include "syzygy/agent/asan/page_protection_helpers.h"
33 : #include "syzygy/agent/asan/shadow.h"
34 : #include "syzygy/agent/asan/stack_capture_cache.h"
35 : #include "syzygy/agent/asan/windows_heap_adapter.h"
36 : #include "syzygy/crashdata/crashdata.h"
37 : #include "syzygy/trace/client/client_utils.h"
38 : #include "syzygy/trace/protocol/call_trace_defs.h"
39 :
40 : namespace agent {
41 : namespace asan {
42 :
43 : namespace {
44 :
45 : using agent::asan::AsanLogger;
46 : using agent::asan::StackCaptureCache;
47 : using agent::asan::WindowsHeapAdapter;
48 : using base::win::WinProcExceptionFilter;
49 :
50 : // Signatures of the various Breakpad functions for setting custom crash
51 : // key-value pairs.
52 : // Post r194002.
53 : typedef void (__cdecl * SetCrashKeyValuePairPtr)(const char*, const char*);
54 : // Post r217590.
55 : typedef void (__cdecl * SetCrashKeyValueImplPtr)(const wchar_t*,
56 : const wchar_t*);
57 :
58 : // Signature of an enhanced crash reporting function.
59 : typedef void(__cdecl* ReportCrashWithProtobufPtr)(EXCEPTION_POINTERS*,
60 : const char*,
61 : size_t);
62 :
63 : // Collects the various Breakpad-related exported functions.
64 : struct BreakpadFunctions {
65 : // The Breakpad crash reporting entry point.
66 : WinProcExceptionFilter crash_for_exception_ptr;
67 :
68 : // The optional enhanced crash reporting entry point.
69 : ReportCrashWithProtobufPtr report_crash_with_protobuf_ptr;
70 :
71 : // Various flavours of the custom key-value setting function. The version
72 : // exported depends on the version of Chrome. It is possible for both of these
73 : // to be NULL even if crash_for_exception_ptr is not NULL.
74 : SetCrashKeyValuePairPtr set_crash_key_value_pair_ptr;
75 : SetCrashKeyValueImplPtr set_crash_key_value_impl_ptr;
76 : };
77 :
78 : // The static breakpad functions. All runtimes share these. This is under
79 : // AsanRuntime::lock_.
80 : BreakpadFunctions breakpad_functions = {};
81 :
82 : // A custom exception code we use to indicate that the exception originated
83 : // from Asan, and shouldn't be processed again by our unhandled exception
84 : // handler. This value has been created according to the rules here:
85 : // http://msdn.microsoft.com/en-us/library/windows/hardware/ff543026(v=vs.85).aspx
86 : // See winerror.h for more details.
87 : static const DWORD kAsanFacility = 0x68B; // No more than 11 bits.
88 : static const DWORD kAsanStatus = 0x5AD0; // No more than 16 bits.
89 : static const DWORD kAsanException =
90 : (3 << 30) | // Severity = error.
91 : (1 << 29) | // Customer defined code (not defined by MS).
92 : (kAsanFacility << 16) | // Facility code.
93 : kAsanStatus; // Status code.
94 : COMPILE_ASSERT((kAsanFacility >> 11) == 0, too_many_facility_bits);
95 : COMPILE_ASSERT((kAsanStatus >> 16) == 0, too_many_status_bits);
96 : COMPILE_ASSERT((kAsanException & (3 << 27)) == 0,
97 : bits_27_and_28_must_be_clear);
98 :
99 : // Raises an exception, first wrapping it an Asan specific exception. This
100 : // indicates to our unhandled exception handler that it doesn't need to
101 : // process the exception.
102 : void RaiseFilteredException(
103 i : DWORD code, DWORD flags, DWORD num_args, const ULONG_PTR* args) {
104 : // Retain the original arguments and craft a new exception.
105 : const ULONG_PTR arguments[4] = {
106 i : code, flags, num_args, reinterpret_cast<const ULONG_PTR>(args) };
107 i : ::RaiseException(kAsanException, 0, ARRAYSIZE(arguments), arguments);
108 i : }
109 :
110 : // The default error handler. It is expected that this will be bound in a
111 : // callback in the Asan runtime.
112 : // @param context The context when the error has been reported.
113 : // @param error_info The information about this error.
114 E : void DefaultErrorHandler(AsanErrorInfo* error_info) {
115 E : DCHECK_NE(reinterpret_cast<AsanErrorInfo*>(NULL), error_info);
116 :
117 : ULONG_PTR arguments[] = {
118 : reinterpret_cast<ULONG_PTR>(&error_info->context),
119 : reinterpret_cast<ULONG_PTR>(error_info)
120 E : };
121 :
122 E : ::DebugBreak();
123 :
124 : // This raises an error in such a way that the Asan unhandled exception
125 : // handler will not process it.
126 : RaiseFilteredException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
127 : 0,
128 : ARRAYSIZE(arguments),
129 E : arguments);
130 E : }
131 :
132 : // Returns the breakpad crash reporting functions if breakpad is enabled for
133 : // the current executable.
134 : //
135 : // If we're running in the context of a breakpad enabled binary we can
136 : // report errors directly via that breakpad entry-point. This allows us
137 : // to report the exact context of the error without including the Asan RTL
138 : // in crash context, depending on where and when we capture the context.
139 : //
140 : // @param breakpad_functions The Breakpad functions structure to be populated.
141 : // @returns true if we found breakpad functions, false otherwise.
142 E : bool GetBreakpadFunctions(BreakpadFunctions* breakpad_functions) {
143 E : DCHECK_NE(reinterpret_cast<BreakpadFunctions*>(NULL), breakpad_functions);
144 :
145 : // Clear the structure.
146 E : ::memset(breakpad_functions, 0, sizeof(*breakpad_functions));
147 :
148 : // The named entry-point exposed to report a crash.
149 : static const char kCrashHandlerSymbol[] = "CrashForException";
150 :
151 : // The optional enhanced entry-point exposed to report a crash.
152 : static const char kReportCrashWithProtobufSymbol[] =
153 : "ReportCrashWithProtobuf";
154 :
155 : // The named entry-point exposed to annotate a crash with a key/value pair.
156 : static const char kSetCrashKeyValuePairSymbol[] = "SetCrashKeyValuePair";
157 : static const char kSetCrashKeyValueImplSymbol[] = "SetCrashKeyValueImpl";
158 :
159 : // Get a handle to the current executable image.
160 E : HMODULE exe_hmodule = ::GetModuleHandle(NULL);
161 :
162 : // Lookup the crash handler symbol.
163 : breakpad_functions->crash_for_exception_ptr =
164 : reinterpret_cast<WinProcExceptionFilter>(
165 E : ::GetProcAddress(exe_hmodule, kCrashHandlerSymbol));
166 :
167 : // Lookup the optional enhanced crash handler symbol.
168 : breakpad_functions->report_crash_with_protobuf_ptr =
169 : reinterpret_cast<ReportCrashWithProtobufPtr>(
170 E : ::GetProcAddress(exe_hmodule, kReportCrashWithProtobufSymbol));
171 :
172 : if (breakpad_functions->crash_for_exception_ptr == NULL &&
173 E : breakpad_functions->report_crash_with_protobuf_ptr == NULL) {
174 E : return false;
175 : }
176 :
177 : // Lookup the crash annotation symbol.
178 : breakpad_functions->set_crash_key_value_pair_ptr =
179 : reinterpret_cast<SetCrashKeyValuePairPtr>(
180 E : ::GetProcAddress(exe_hmodule, kSetCrashKeyValuePairSymbol));
181 : breakpad_functions->set_crash_key_value_impl_ptr =
182 : reinterpret_cast<SetCrashKeyValueImplPtr>(
183 E : ::GetProcAddress(exe_hmodule, kSetCrashKeyValueImplSymbol));
184 :
185 E : return true;
186 E : }
187 :
188 : // Sets a crash key using the given breakpad function.
189 : void SetCrashKeyValuePair(const BreakpadFunctions& breakpad_functions,
190 : const char* key,
191 E : const char* value) {
192 E : if (breakpad_functions.set_crash_key_value_pair_ptr != NULL) {
193 i : breakpad_functions.set_crash_key_value_pair_ptr(key, value);
194 i : return;
195 : }
196 :
197 E : if (breakpad_functions.set_crash_key_value_impl_ptr != NULL) {
198 i : std::wstring wkey = base::UTF8ToWide(key);
199 i : std::wstring wvalue = base::UTF8ToWide(value);
200 : breakpad_functions.set_crash_key_value_impl_ptr(wkey.c_str(),
201 i : wvalue.c_str());
202 i : return;
203 : }
204 :
205 : return;
206 E : }
207 :
208 : // Writes the appropriate crash keys for the given error.
209 : void SetCrashKeys(const BreakpadFunctions& breakpad_functions,
210 E : AsanErrorInfo* error_info) {
211 : DCHECK(breakpad_functions.crash_for_exception_ptr != NULL ||
212 E : breakpad_functions.report_crash_with_protobuf_ptr != NULL);
213 E : DCHECK(error_info != NULL);
214 :
215 : SetCrashKeyValuePair(breakpad_functions,
216 : "asan-error-type",
217 E : ErrorInfoAccessTypeToStr(error_info->error_type));
218 :
219 E : if (error_info->shadow_info[0] != '\0') {
220 : SetCrashKeyValuePair(breakpad_functions,
221 : "asan-error-message",
222 E : error_info->shadow_info);
223 : }
224 E : }
225 :
226 : // Initializes an exception record for an Asan crash.
227 : void InitializeExceptionRecord(const AsanErrorInfo* error_info,
228 : EXCEPTION_RECORD* record,
229 E : EXCEPTION_POINTERS* pointers) {
230 E : DCHECK_NE(static_cast<AsanErrorInfo*>(nullptr), error_info);
231 E : DCHECK_NE(static_cast<EXCEPTION_RECORD*>(nullptr), record);
232 E : DCHECK_NE(static_cast<EXCEPTION_POINTERS*>(nullptr), pointers);
233 :
234 E : ::memset(record, 0, sizeof(EXCEPTION_RECORD));
235 E : record->ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
236 : record->ExceptionAddress = reinterpret_cast<PVOID>(
237 E : error_info->context.Eip);
238 E : record->NumberParameters = 2;
239 : record->ExceptionInformation[0] = reinterpret_cast<ULONG_PTR>(
240 E : &error_info->context);
241 E : record->ExceptionInformation[1] = reinterpret_cast<ULONG_PTR>(error_info);
242 :
243 E : pointers->ExceptionRecord = record;
244 E : pointers->ContextRecord = const_cast<CONTEXT*>(&error_info->context);
245 E : }
246 :
247 : // Creates a serialized protobuf representing crash data.
248 E : bool PopulateProtobuf(const AsanErrorInfo& error_info, std::string* protobuf) {
249 E : DCHECK_NE(static_cast<std::string*>(nullptr), protobuf);
250 E : crashdata::Value value;
251 E : PopulateErrorInfo(error_info, &value);
252 E : if (!value.SerializeToString(protobuf))
253 i : return false;
254 E : return true;
255 E : }
256 :
257 : // The breakpad error handler. It is expected that this will be bound in a
258 : // callback in the Asan runtime.
259 : // @param breakpad_functions A struct containing pointers to the various
260 : // Breakpad reporting functions.
261 : // @param error_info The information about this error.
262 : void BreakpadErrorHandler(const BreakpadFunctions& breakpad_functions,
263 E : AsanErrorInfo* error_info) {
264 : DCHECK(breakpad_functions.crash_for_exception_ptr != NULL ||
265 E : breakpad_functions.report_crash_with_protobuf_ptr != NULL);
266 E : DCHECK(error_info != NULL);
267 :
268 E : SetCrashKeys(breakpad_functions, error_info);
269 :
270 E : EXCEPTION_RECORD exception = {};
271 E : EXCEPTION_POINTERS pointers = {};
272 E : InitializeExceptionRecord(error_info, &exception, &pointers);
273 :
274 E : if (breakpad_functions.report_crash_with_protobuf_ptr) {
275 E : std::string protobuf;
276 E : PopulateProtobuf(*error_info, &protobuf);
277 : breakpad_functions.report_crash_with_protobuf_ptr(
278 E : &pointers, protobuf.data(), protobuf.length());
279 E : } else {
280 E : breakpad_functions.crash_for_exception_ptr(&pointers);
281 : }
282 i : NOTREACHED();
283 i : }
284 :
285 : // A helper function to find if an intrusive list contains a given entry.
286 : // @param list The list in which we want to look for the entry.
287 : // @param item The entry we want to look for.
288 : // @returns true if the list contains this entry, false otherwise.
289 : bool HeapListContainsEntry(const LIST_ENTRY* list, const LIST_ENTRY* item) {
290 : LIST_ENTRY* current = list->Flink;
291 : while (current != NULL) {
292 : LIST_ENTRY* next_item = NULL;
293 : if (current->Flink != list) {
294 : next_item = current->Flink;
295 : }
296 :
297 : if (current == item) {
298 : return true;
299 : }
300 :
301 : current = next_item;
302 : }
303 : return false;
304 : }
305 :
306 : // Check if the current process is large address aware.
307 : // @returns true if it is, false otherwise.
308 E : bool CurrentProcessIsLargeAddressAware() {
309 E : const base::win::PEImage image(::GetModuleHandle(NULL));
310 :
311 : bool process_is_large_address_aware =
312 : (image.GetNTHeaders()->FileHeader.Characteristics &
313 E : IMAGE_FILE_LARGE_ADDRESS_AWARE) != 0;
314 :
315 E : return process_is_large_address_aware;
316 E : }
317 :
318 : // A helper function to send a command to Windbg. Windbg should first receive
319 : // the ".ocommand ASAN" command to treat those messages as commands.
320 E : void AsanDbgCmd(const wchar_t* fmt, ...) {
321 E : if (!base::debug::BeingDebugged())
322 E : return;
323 : // The string should start with "ASAN" to be interpreted by the debugger as a
324 : // command.
325 i : std::wstring command_wstring = L"ASAN ";
326 : va_list args;
327 i : va_start(args, fmt);
328 :
329 : // Append the actual command to the wstring.
330 i : base::StringAppendV(&command_wstring, fmt, args);
331 :
332 : // Append "; g" to make sure that the debugger continues its execution after
333 : // executing this command. This is needed because when the .ocommand function
334 : // is used under Windbg the debugger will break on OutputDebugString.
335 i : command_wstring.append(L"; g");
336 :
337 i : OutputDebugString(command_wstring.c_str());
338 E : }
339 :
340 : // A helper function to print a message to Windbg's console.
341 E : void AsanDbgMessage(const wchar_t* fmt, ...) {
342 E : if (!base::debug::BeingDebugged())
343 E : return;
344 : // Prepend the message with the .echo command so it'll be printed into the
345 : // debugger's console.
346 i : std::wstring message_wstring = L".echo ";
347 : va_list args;
348 i : va_start(args, fmt);
349 :
350 : // Append the actual message to the wstring.
351 i : base::StringAppendV(&message_wstring, fmt, args);
352 :
353 : // Treat the message as a command to print it.
354 i : AsanDbgCmd(message_wstring.c_str());
355 E : }
356 :
357 : // Switch to the caller's context and print its stack trace in Windbg.
358 : void AsanDbgPrintContext(const CONTEXT& context) {
359 : if (!base::debug::BeingDebugged())
360 : return;
361 : AsanDbgMessage(L"Caller's context (%p) and stack trace:", &context);
362 : AsanDbgCmd(L".cxr %p; kv", reinterpret_cast<uint32>(&context));
363 : }
364 :
365 : // Returns the maximum allocation size that can be made safely. This leaves
366 : // space for child function frames, ideally enough for Breakpad to do its
367 : // work.
368 E : size_t MaxSafeAllocaSize() {
369 : // We leave 5KB of stack space for Breakpad and other crash reporting
370 : // machinery.
371 E : const size_t kReservedStack = 5 * 1024;
372 :
373 : // Find the base of the stack.
374 E : MEMORY_BASIC_INFORMATION mbi = {};
375 E : void* stack = &mbi;
376 E : if (VirtualQuery(stack, &mbi, sizeof(mbi)) == 0)
377 i : return 0;
378 : size_t max_size = reinterpret_cast<uint8*>(stack) -
379 E : reinterpret_cast<uint8*>(mbi.AllocationBase);
380 E : max_size -= std::min(max_size, kReservedStack);
381 E : return max_size;
382 E : }
383 :
384 : // Performs a dynamic stack allocation of at most |size| bytes. Sets the actual
385 : // size of the allocation and the pointer to it by modifying |size| and |result|
386 : // directly.
387 : #define SAFE_ALLOCA(size, result) { \
388 : size_t max_size = MaxSafeAllocaSize(); \
389 : size = std::min(size, max_size); \
390 : result = _alloca(size); \
391 : if (result == NULL) \
392 : size = 0; \
393 : }
394 :
395 : // Runs the heap checker if enabled. If heap corruption is found serializes
396 : // the results to the stack and modifies the |error_info| structure.
397 : #define CHECK_HEAP_CORRUPTION(runtime, error_info) \
398 : (error_info)->heap_is_corrupt = false; \
399 : if (!((runtime)->params_.check_heap_on_failure)) { \
400 : runtime_->logger_->Write( \
401 : "SyzyASAN: Heap checker disabled, ignoring exception."); \
402 : } else { \
403 : runtime_->logger_->Write( \
404 : "SyzyASAN: Heap checker enabled, processing exception."); \
405 : AutoHeapManagerLock lock(runtime_->heap_manager_.get()); \
406 : HeapChecker heap_checker; \
407 : HeapChecker::CorruptRangesVector corrupt_ranges; \
408 : heap_checker.IsHeapCorrupt(&corrupt_ranges); \
409 : size_t size = (runtime)->CalculateCorruptHeapInfoSize(corrupt_ranges); \
410 : void* buffer = NULL; \
411 : if (size > 0) { \
412 : SAFE_ALLOCA(size, buffer); \
413 : (runtime)->WriteCorruptHeapInfo( \
414 : corrupt_ranges, size, buffer, error_info); \
415 : } \
416 : }
417 :
418 : } // namespace
419 :
420 E : base::Lock AsanRuntime::lock_;
421 : AsanRuntime* AsanRuntime::runtime_ = NULL;
422 : LPTOP_LEVEL_EXCEPTION_FILTER AsanRuntime::previous_uef_ = NULL;
423 : bool AsanRuntime::uef_installed_ = false;
424 :
425 : AsanRuntime::AsanRuntime()
426 E : : logger_(), stack_cache_(), asan_error_callback_(), heap_manager_() {
427 E : ::common::SetDefaultAsanParameters(¶ms_);
428 E : starting_ticks_ = ::GetTickCount();
429 E : }
430 :
431 E : AsanRuntime::~AsanRuntime() {
432 E : }
433 :
434 E : void AsanRuntime::SetUp(const std::wstring& flags_command_line) {
435 E : base::AutoLock auto_lock(lock_);
436 E : DCHECK(!runtime_);
437 E : runtime_ = this;
438 :
439 : // Ensure that the current process is not large address aware. It shouldn't be
440 : // because the shadow memory assume that the process will only be able to use
441 : // 2GB of address space.
442 E : CHECK(!CurrentProcessIsLargeAddressAware());
443 :
444 : // Initialize the command-line structures. This is needed so that
445 : // SetUpLogger() can include the command-line in the message announcing
446 : // this process. Note: this is mostly for debugging purposes.
447 E : CommandLine::Init(0, NULL);
448 :
449 E : Shadow::SetUp();
450 :
451 : // Setup the "global" state.
452 E : common::StackCapture::Init();
453 E : StackCaptureCache::Init();
454 E : SetUpLogger();
455 E : SetUpStackCache();
456 E : SetUpHeapManager();
457 E : WindowsHeapAdapter::SetUp(heap_manager_.get());
458 :
459 : // Parse and propagate any flags set via the environment variable. This logs
460 : // failure for us.
461 E : if (!::common::ParseAsanParameters(flags_command_line, ¶ms_))
462 i : return;
463 :
464 : // Propagates the flags values to the different modules.
465 E : PropagateParams();
466 :
467 : // Register the error reporting callback to use if/when an Asan error is
468 : // detected. If we're able to resolve a breakpad error reporting function
469 : // then use that; otherwise, fall back to the default error handler.
470 : if (!params_.disable_breakpad_reporting &&
471 E : GetBreakpadFunctions(&breakpad_functions)) {
472 E : logger_->Write("SyzyASAN: Using Breakpad for error reporting.");
473 E : SetErrorCallBack(base::Bind(&BreakpadErrorHandler, breakpad_functions));
474 E : } else {
475 E : logger_->Write("SyzyASAN: Using default error reporting handler.");
476 E : SetErrorCallBack(base::Bind(&DefaultErrorHandler));
477 : }
478 :
479 : // Install the unhandled exception handler. This is only installed once
480 : // across all runtime instances in a process so we check that it hasn't
481 : // already been installed.
482 E : if (!uef_installed_) {
483 E : uef_installed_ = true;
484 E : previous_uef_ = ::SetUnhandledExceptionFilter(&UnhandledExceptionFilter);
485 : }
486 :
487 : // Finally, initialize the heap manager. This comes after parsing all
488 : // parameters as some decisions can only be made once.
489 E : heap_manager_->Init();
490 E : }
491 :
492 E : void AsanRuntime::TearDown() {
493 E : base::AutoLock auto_lock(lock_);
494 :
495 E : WindowsHeapAdapter::TearDown();
496 E : TearDownHeapManager();
497 E : TearDownStackCache();
498 E : TearDownLogger();
499 E : DCHECK(asan_error_callback_.is_null() == FALSE);
500 E : asan_error_callback_.Reset();
501 E : Shadow::TearDown();
502 :
503 : // Unregister ourselves as the singleton runtime for UEF.
504 E : runtime_ = NULL;
505 :
506 : // In principle, we should also check that all the heaps have been destroyed
507 : // but this is not guaranteed to be the case in Chrome, so the heap list may
508 : // not be empty here.
509 E : }
510 :
511 E : void AsanRuntime::OnErrorImpl(AsanErrorInfo* error_info) {
512 E : DCHECK_NE(reinterpret_cast<AsanErrorInfo*>(NULL), error_info);
513 :
514 E : LogAsanErrorInfo(error_info);
515 :
516 E : if (params_.minidump_on_failure) {
517 E : DCHECK(logger_.get() != NULL);
518 E : logger_->SaveMiniDump(&error_info->context, error_info);
519 : }
520 :
521 E : if (params_.exit_on_failure) {
522 E : DCHECK(logger_.get() != NULL);
523 E : logger_->Stop();
524 E : exit(EXIT_FAILURE);
525 : }
526 E : }
527 :
528 E : void AsanRuntime::OnError(AsanErrorInfo* error_info) {
529 E : DCHECK_NE(reinterpret_cast<AsanErrorInfo*>(NULL), error_info);
530 :
531 : // Grab the global page protection lock to prevent page protection settings
532 : // from being modified while processing the error.
533 E : ::common::AutoRecursiveLock lock(block_protect_lock);
534 :
535 : // Unfortunately this is a giant macro, but it needs to be as it performs
536 : // stack allocations.
537 E : CHECK_HEAP_CORRUPTION(this, error_info);
538 :
539 E : OnErrorImpl(error_info);
540 :
541 : // Call the callback to handle this error.
542 E : DCHECK(!asan_error_callback_.is_null());
543 E : asan_error_callback_.Run(error_info);
544 E : }
545 :
546 E : void AsanRuntime::SetErrorCallBack(const AsanOnErrorCallBack& callback) {
547 E : asan_error_callback_ = callback;
548 E : }
549 :
550 E : void AsanRuntime::SetUpLogger() {
551 : // Setup variables we're going to use.
552 E : scoped_ptr<base::Environment> env(base::Environment::Create());
553 E : scoped_ptr<AsanLogger> client(new AsanLogger);
554 E : CHECK(env.get() != NULL);
555 E : CHECK(client.get() != NULL);
556 :
557 : // Initialize the client.
558 : client->set_instance_id(
559 E : base::UTF8ToWide(trace::client::GetInstanceIdForThisModule()));
560 E : client->Init();
561 :
562 : // Register the client singleton instance.
563 E : logger_.reset(client.release());
564 E : }
565 :
566 E : void AsanRuntime::TearDownLogger() {
567 E : logger_.reset();
568 E : }
569 :
570 E : void AsanRuntime::SetUpStackCache() {
571 E : DCHECK(stack_cache_.get() == NULL);
572 E : DCHECK(logger_.get() != NULL);
573 E : stack_cache_.reset(new StackCaptureCache(logger_.get()));
574 E : }
575 :
576 E : void AsanRuntime::TearDownStackCache() {
577 E : DCHECK(stack_cache_.get() != NULL);
578 E : stack_cache_->LogStatistics();
579 E : stack_cache_.reset();
580 E : }
581 :
582 E : void AsanRuntime::SetUpHeapManager() {
583 : DCHECK_EQ(static_cast<heap_managers::BlockHeapManager*>(NULL),
584 E : heap_manager_.get());
585 E : DCHECK_NE(static_cast<StackCaptureCache*>(NULL), stack_cache_.get());
586 E : heap_manager_.reset(new heap_managers::BlockHeapManager(stack_cache_.get()));
587 :
588 : // Configure the heap manager to notify us on heap corruption.
589 : heap_manager_->SetHeapErrorCallback(base::Bind(&AsanRuntime::OnError,
590 E : base::Unretained(this)));
591 E : }
592 :
593 E : void AsanRuntime::TearDownHeapManager() {
594 : DCHECK_NE(static_cast<heap_managers::BlockHeapManager*>(NULL),
595 E : heap_manager_.get());
596 : // Tear down the heap manager before we destroy it and lose our pointer
597 : // to it. This is necessary because the heap manager can raise errors
598 : // while tearing down the heap, which will in turn call back into the
599 : // block heap manager via the runtime.
600 E : heap_manager_->TearDownHeapManager();
601 E : heap_manager_.reset();
602 E : }
603 :
604 E : bool AsanRuntime::GetAsanFlagsEnvVar(std::wstring* env_var_wstr) {
605 E : scoped_ptr<base::Environment> env(base::Environment::Create());
606 E : if (env.get() == NULL) {
607 i : LOG(ERROR) << "base::Environment::Create returned NULL.";
608 i : return false;
609 : }
610 :
611 : // If this fails, the environment variable simply does not exist.
612 E : std::string env_var_str;
613 E : if (!env->GetVar(::common::kSyzyAsanOptionsEnvVar, &env_var_str)) {
614 E : return true;
615 : }
616 :
617 E : *env_var_wstr = base::SysUTF8ToWide(env_var_str);
618 :
619 E : return true;
620 E : }
621 :
622 E : void AsanRuntime::PropagateParams() {
623 : // This function has to be kept in sync with the AsanParameters struct. These
624 : // checks will ensure that this is the case.
625 : COMPILE_ASSERT(sizeof(::common::AsanParameters) == 56,
626 : must_update_propagate_params);
627 : COMPILE_ASSERT(::common::kAsanParametersVersion == 8,
628 : must_update_parameters_version);
629 :
630 : // Push the configured parameter values to the appropriate endpoints.
631 E : heap_manager_->set_parameters(params_);
632 : StackCaptureCache::set_compression_reporting_period(
633 E : params_.reporting_period);
634 : common::StackCapture::set_bottom_frames_to_skip(
635 E : params_.bottom_frames_to_skip);
636 E : stack_cache_->set_max_num_frames(params_.max_num_frames);
637 : // ignored_stack_ids is used locally by AsanRuntime.
638 E : logger_->set_log_as_text(params_.log_as_text);
639 : // exit_on_failure is used locally by AsanRuntime.
640 E : logger_->set_minidump_on_failure(params_.minidump_on_failure);
641 E : }
642 :
643 : size_t AsanRuntime::CalculateCorruptHeapInfoSize(
644 E : const HeapChecker::CorruptRangesVector& corrupt_ranges) {
645 : size_t n = corrupt_ranges.size() *
646 E : (sizeof(AsanCorruptBlockRange) + sizeof(AsanBlockInfo));
647 E : return n;
648 E : }
649 :
650 : void AsanRuntime::WriteCorruptHeapInfo(
651 : const HeapChecker::CorruptRangesVector& corrupt_ranges,
652 : size_t buffer_size,
653 : void* buffer,
654 E : AsanErrorInfo* error_info) {
655 : DCHECK((buffer_size == 0 && buffer == NULL) ||
656 E : (buffer_size != 0 && buffer != NULL));
657 E : DCHECK_NE(reinterpret_cast<AsanErrorInfo*>(NULL), error_info);
658 :
659 E : ::memset(buffer, 0, buffer_size);
660 :
661 E : error_info->heap_is_corrupt = false;
662 E : error_info->corrupt_range_count = 0;
663 E : error_info->corrupt_block_count = 0;
664 E : error_info->corrupt_ranges_reported = 0;
665 E : error_info->corrupt_ranges = NULL;
666 :
667 E : if (corrupt_ranges.empty())
668 i : return;
669 :
670 : // If we have corrupt ranges then set the aggregate fields.
671 E : error_info->heap_is_corrupt = true;
672 E : error_info->corrupt_range_count = corrupt_ranges.size();
673 E : for (size_t i = 0; i < corrupt_ranges.size(); ++i)
674 E : error_info->corrupt_block_count += corrupt_ranges[i].block_count;
675 :
676 : // We report a AsanCorruptBlockRange and at least one AsanBlockInfo per
677 : // corrupt range. Determine how many ranges we can report on.
678 : size_t range_count = buffer_size /
679 E : (sizeof(AsanCorruptBlockRange) + sizeof(AsanBlockInfo));
680 E : range_count = std::min(range_count, corrupt_ranges.size());
681 :
682 : // Allocate space for the corrupt range metadata.
683 E : uint8* cursor = reinterpret_cast<uint8*>(buffer);
684 E : uint8* buffer_end = cursor + buffer_size;
685 : error_info->corrupt_ranges = reinterpret_cast<AsanCorruptBlockRange*>(
686 E : cursor);
687 E : cursor += range_count * sizeof(AsanCorruptBlockRange);
688 E : error_info->corrupt_range_count = corrupt_ranges.size();
689 E : error_info->corrupt_ranges_reported = range_count;
690 :
691 : // Allocate space for the corrupt block metadata.
692 E : size_t block_count = (buffer_end - cursor) / sizeof(AsanBlockInfo);
693 E : AsanBlockInfo* block_infos = reinterpret_cast<AsanBlockInfo*>(cursor);
694 E : cursor += block_count * sizeof(AsanBlockInfo);
695 :
696 : // Write as many corrupt block ranges as we have room for. This is
697 : // effectively random as it is by order of address.
698 E : for (size_t i = 0; i < range_count; ++i) {
699 : // Copy the information about the corrupt range.
700 E : error_info->corrupt_ranges[i] = corrupt_ranges[i];
701 :
702 : // Allocate space for the first block of this range on the stack.
703 : // TODO(sebmarchand): Report more blocks if necessary.
704 E : AsanBlockInfo* asan_block_info = block_infos;
705 E : error_info->corrupt_ranges[i].block_info = block_infos;
706 E : error_info->corrupt_ranges[i].block_info_count = 1;
707 E : ++block_infos;
708 :
709 : // Use a shadow walker to find the first corrupt block in this range and
710 : // copy its metadata.
711 : ShadowWalker shadow_walker(
712 : false,
713 : reinterpret_cast<const uint8*>(corrupt_ranges[i].address),
714 : reinterpret_cast<const uint8*>(corrupt_ranges[i].address) +
715 E : corrupt_ranges[i].length);
716 E : BlockInfo block_info = {};
717 E : CHECK(shadow_walker.Next(&block_info));
718 : // The heap checker removes block protections as it goes, so this block
719 : // should be readable. However, remove page protections just to be sure.
720 : // They are left turned off so that the minidump generation can introspect
721 : // the block.
722 E : BlockProtectNone(block_info);
723 E : ErrorInfoGetAsanBlockInfo(block_info, stack_cache_.get(), asan_block_info);
724 E : DCHECK_EQ(kDataIsCorrupt, asan_block_info->analysis.block_state);
725 E : }
726 :
727 : return;
728 E : }
729 :
730 E : void AsanRuntime::LogAsanErrorInfo(AsanErrorInfo* error_info) {
731 E : DCHECK_NE(reinterpret_cast<AsanErrorInfo*>(NULL), error_info);
732 :
733 E : const char* bug_descr = ErrorInfoAccessTypeToStr(error_info->error_type);
734 E : if (logger_->log_as_text()) {
735 : std::string output(base::StringPrintf(
736 : "SyzyASAN error: %s on address 0x%08X (stack_id=0x%08X)\n",
737 E : bug_descr, error_info->location, error_info->crash_stack_id));
738 E : if (error_info->access_mode != agent::asan::ASAN_UNKNOWN_ACCESS) {
739 E : const char* access_mode_str = NULL;
740 E : if (error_info->access_mode == agent::asan::ASAN_READ_ACCESS)
741 E : access_mode_str = "READ";
742 E : else
743 E : access_mode_str = "WRITE";
744 : base::StringAppendF(&output,
745 : "%s of size %d at 0x%08X\n",
746 : access_mode_str,
747 : error_info->access_size,
748 E : error_info->location);
749 : }
750 :
751 : // Log the failure and stack.
752 E : logger_->WriteWithContext(output, error_info->context);
753 :
754 E : logger_->Write(error_info->shadow_info);
755 E : if (error_info->block_info.free_stack_size != 0U) {
756 : logger_->WriteWithStackTrace("freed here:\n",
757 : error_info->block_info.free_stack,
758 E : error_info->block_info.free_stack_size);
759 : }
760 E : if (error_info->block_info.alloc_stack_size != NULL) {
761 : logger_->WriteWithStackTrace("previously allocated here:\n",
762 : error_info->block_info.alloc_stack,
763 E : error_info->block_info.alloc_stack_size);
764 : }
765 E : if (error_info->error_type >= USE_AFTER_FREE) {
766 E : std::string shadow_text;
767 E : Shadow::AppendShadowMemoryText(error_info->location, &shadow_text);
768 E : logger_->Write(shadow_text);
769 E : }
770 E : }
771 :
772 : // Print the base of the Windbg help message.
773 : AsanDbgMessage(L"An Asan error has been found (%ls), here are the details:",
774 E : base::SysUTF8ToWide(bug_descr).c_str());
775 :
776 : // Print the Windbg information to display the allocation stack if present.
777 E : if (error_info->block_info.alloc_stack_size != NULL) {
778 E : AsanDbgMessage(L"Allocation stack trace:");
779 : AsanDbgCmd(L"dps %p l%d",
780 : error_info->block_info.alloc_stack,
781 E : error_info->block_info.alloc_stack_size);
782 : }
783 :
784 : // Print the Windbg information to display the free stack if present.
785 E : if (error_info->block_info.free_stack_size != NULL) {
786 E : AsanDbgMessage(L"Free stack trace:");
787 : AsanDbgCmd(L"dps %p l%d",
788 : error_info->block_info.free_stack,
789 E : error_info->block_info.free_stack_size);
790 : }
791 E : }
792 :
793 E : void AsanRuntime::GetBadAccessInformation(AsanErrorInfo* error_info) {
794 E : base::AutoLock lock(lock_);
795 :
796 : // Checks if this is an access to an internal structure or if it's an access
797 : // in the upper region of the memory (over the 2 GB limit).
798 : if ((reinterpret_cast<size_t>(error_info->location) & (1 << 31)) != 0 ||
799 : Shadow::GetShadowMarkerForAddress(error_info->location)
800 E : == kAsanMemoryMarker) {
801 E : error_info->error_type = WILD_ACCESS;
802 E : } else if (Shadow::GetShadowMarkerForAddress(error_info->location) ==
803 E : kInvalidAddressMarker) {
804 i : error_info->error_type = INVALID_ADDRESS;
805 i : } else {
806 E : ErrorInfoGetBadAccessInformation(stack_cache_.get(), error_info);
807 : }
808 E : }
809 :
810 E : bool AsanRuntime::allocation_filter_flag() {
811 E : return heap_manager_->allocation_filter_flag();
812 E : }
813 :
814 E : void AsanRuntime::set_allocation_filter_flag(bool value) {
815 E : heap_manager_->set_allocation_filter_flag(value);
816 E : }
817 :
818 E : void AsanRuntime::AddThreadId(uint32 thread_id) {
819 E : DCHECK_NE(0u, thread_id);
820 E : base::AutoLock lock(thread_ids_lock_);
821 E : thread_ids_.insert(thread_id);
822 E : }
823 :
824 E : bool AsanRuntime::ThreadIdIsValid(uint32 thread_id) {
825 E : base::AutoLock lock(thread_ids_lock_);
826 E : return thread_ids_.count(thread_id) > 0;
827 E : }
828 :
829 E : bool AsanRuntime::HeapIdIsValid(HeapManagerInterface::HeapId heap_id) {
830 : // Consider dying heaps in this query, as they are still valid from the
831 : // point of view of an error report.
832 E : return heap_manager_->IsValidHeapIdUnlocked(heap_id, true);
833 E : }
834 :
835 E : HeapType AsanRuntime::GetHeapType(HeapManagerInterface::HeapId heap_id) {
836 E : return heap_manager_->GetHeapTypeUnlocked(heap_id);
837 E : }
838 :
839 i : int AsanRuntime::CrashForException(EXCEPTION_POINTERS* exception) {
840 i : return ExceptionFilterImpl(false, exception);
841 i : }
842 :
843 : LONG WINAPI AsanRuntime::UnhandledExceptionFilter(
844 E : struct _EXCEPTION_POINTERS* exception) {
845 E : return ExceptionFilterImpl(true, exception);
846 E : }
847 :
848 : LONG AsanRuntime::ExceptionFilterImpl(bool is_unhandled,
849 E : EXCEPTION_POINTERS* exception) {
850 : // This ensures that we don't have multiple colliding crashes being processed
851 : // simultaneously.
852 E : base::AutoLock auto_lock(lock_);
853 :
854 : // Grab the global page protection lock to prevent page protection settings
855 : // from being modified while processing the error.
856 E : ::common::AutoRecursiveLock lock(block_protect_lock);
857 :
858 : // This is needed for unittesting.
859 E : runtime_->logger_->Write("SyzyASAN: Handling an exception.");
860 :
861 : // If we're bound to a runtime then look for heap corruption and
862 : // potentially augment the exception record. This needs to exist in the
863 : // outermost scope of this function as pointers to it may be passed to
864 : // other exception handlers.
865 E : AsanErrorInfo error_info = {};
866 :
867 : // If this is set to true then an Asan error will be emitted.
868 E : bool emit_asan_error = false;
869 :
870 : // If this is an exception that we launched then extract the original
871 : // exception data and continue processing it.
872 E : if (exception->ExceptionRecord->ExceptionCode == kAsanException) {
873 i : ULONG_PTR* args = exception->ExceptionRecord->ExceptionInformation;
874 i : DWORD code = args[0];
875 i : DWORD flags = args[1];
876 i : DWORD nargs = args[2];
877 i : const ULONG_PTR* orig_args = reinterpret_cast<const ULONG_PTR*>(args[3]);
878 :
879 : // Rebuild the exception with the original exception data.
880 i : exception->ExceptionRecord->ExceptionCode = code;
881 i : exception->ExceptionRecord->ExceptionFlags = flags;
882 i : exception->ExceptionRecord->NumberParameters = nargs;
883 i : for (DWORD i = 0; i < nargs; ++i)
884 i : args[i] = orig_args[i];
885 E : } else if (runtime_) {
886 : // Initialize this as if heap corruption is the primary error being
887 : // reported. This will be overridden by the access violation handling
888 : // code below, if necessary.
889 E : error_info.location = exception->ExceptionRecord->ExceptionAddress;
890 E : error_info.context = *exception->ContextRecord;
891 E : error_info.error_type = CORRUPT_HEAP;
892 E : error_info.access_mode = ASAN_UNKNOWN_ACCESS;
893 :
894 : // It is possible that access violations are due to page protections of a
895 : // sufficiently large allocation. In this case the shadow will contain
896 : // block redzone markers at the given address. We take over the exception
897 : // if that is the case.
898 : if (exception->ExceptionRecord->ExceptionCode ==
899 : EXCEPTION_ACCESS_VIOLATION &&
900 : exception->ExceptionRecord->NumberParameters >= 2 &&
901 E : exception->ExceptionRecord->ExceptionInformation[0] <= 1) {
902 : void* address = reinterpret_cast<void*>(
903 E : exception->ExceptionRecord->ExceptionInformation[1]);
904 E : ShadowMarker marker = Shadow::GetShadowMarkerForAddress(address);
905 : if (ShadowMarkerHelper::IsRedzone(marker) &&
906 E : ShadowMarkerHelper::IsActiveBlock(marker)) {
907 E : BlockInfo block_info = {};
908 E : if (Shadow::BlockInfoFromShadow(address, &block_info)) {
909 : // Page protections have to be removed from this block otherwise our
910 : // own inspection will cause further errors.
911 E : ScopedBlockAccess block_access(block_info);
912 :
913 : // Useful for unittesting.
914 : runtime_->logger_->Write("SyzyASAN: Caught an invalid access via "
915 E : "an access violation exception.");
916 :
917 : // Override the invalid access location with the faulting address,
918 : // not the code address.
919 E : error_info.location = address;
920 : // The exact access size isn't reported so simply set it to 1 (an
921 : // obvious lower bound).
922 E : error_info.access_size = 1;
923 : // Determine if this is a read or a write using information in the
924 : // exception record.
925 : error_info.access_mode =
926 : exception->ExceptionRecord->ExceptionInformation[0] == 0 ?
927 E : ASAN_READ_ACCESS : ASAN_WRITE_ACCESS;
928 :
929 : // Fill out the rest of the bad access information.
930 : ErrorInfoGetBadAccessInformation(runtime_->stack_cache(),
931 E : &error_info);
932 E : emit_asan_error = true;
933 E : }
934 : }
935 : }
936 :
937 E : CHECK_HEAP_CORRUPTION(runtime_, &error_info);
938 E : if (error_info.heap_is_corrupt)
939 E : emit_asan_error = true;
940 : }
941 :
942 : // If an Asan error was detected then report it via the logger and take over
943 : // the exception record.
944 E : EXCEPTION_RECORD record = {};
945 E : if (emit_asan_error) {
946 : // Log the error via the usual means.
947 E : runtime_->OnErrorImpl(&error_info);
948 :
949 : // If we have Breakpad integration then set our crash keys.
950 E : if (breakpad_functions.crash_for_exception_ptr != NULL)
951 E : SetCrashKeys(breakpad_functions, &error_info);
952 :
953 : // Remember the old exception record.
954 E : EXCEPTION_RECORD* old_record = exception->ExceptionRecord;
955 :
956 : // Initialize the exception record and chain the original exception to it.
957 E : InitializeExceptionRecord(&error_info, &record, exception);
958 E : record.ExceptionRecord = old_record;
959 : }
960 :
961 E : if (breakpad_functions.report_crash_with_protobuf_ptr) {
962 : // This method is expected to terminate the process.
963 E : std::string protobuf;
964 E : PopulateProtobuf(error_info, &protobuf);
965 : breakpad_functions.report_crash_with_protobuf_ptr(
966 E : exception, protobuf.data(), protobuf.length());
967 E : return EXCEPTION_CONTINUE_SEARCH;
968 : }
969 :
970 E : if (is_unhandled) {
971 : // Pass the buck to the next exception handler. If the process is Breakpad
972 : // enabled this will eventually make its way there.
973 E : if (previous_uef_ != NULL)
974 E : return (*previous_uef_)(exception);
975 : }
976 :
977 : // If we've found an Asan error then pass the buck to Breakpad directly,
978 : // if possible. Otherwise, simply let things take their natural course.
979 i : if (emit_asan_error && breakpad_functions.crash_for_exception_ptr)
980 i : return (*breakpad_functions.crash_for_exception_ptr)(exception);
981 :
982 : // We can't do anything with this, so let the system deal with it.
983 i : return EXCEPTION_CONTINUE_SEARCH;
984 i : }
985 :
986 : } // namespace asan
987 : } // namespace agent
|