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