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 "base/bind.h"
18 : #include "base/command_line.h"
19 : #include "base/environment.h"
20 : #include "base/logging.h"
21 : #include "base/string_number_conversions.h"
22 : #include "base/string_util.h"
23 : #include "base/stringprintf.h"
24 : #include "base/utf_string_conversions.h"
25 : #include "base/strings/string_tokenizer.h"
26 : #include "base/strings/sys_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/asan_shadow.h"
31 : #include "syzygy/agent/asan/stack_capture_cache.h"
32 : #include "syzygy/trace/client/client_utils.h"
33 : #include "syzygy/trace/protocol/call_trace_defs.h"
34 :
35 : namespace agent {
36 : namespace asan {
37 :
38 : namespace {
39 :
40 : using agent::asan::AsanLogger;
41 : using agent::asan::HeapProxy;
42 : using agent::asan::StackCaptureCache;
43 : using base::win::WinProcExceptionFilter;
44 :
45 : // Signatures of the various Breakpad functions for setting custom crash
46 : // key-value pairs.
47 : // Post r194002.
48 : typedef void (__cdecl * SetCrashKeyValuePairPtr)(const char*, const char*);
49 : // Post r217590.
50 : typedef void (__cdecl * SetCrashKeyValueImplPtr)(const wchar_t*,
51 : const wchar_t*);
52 :
53 : // Collects the various Breakpad-related exported functions.
54 : struct BreakpadFunctions {
55 : // The Breakpad crash reporting entry point.
56 : WinProcExceptionFilter crash_for_exception_ptr;
57 :
58 : // Various flavours of the custom key-value setting function. The version
59 : // exported depends on the version of Chrome. It is possible for both of these
60 : // to be NULL even if crash_for_exception_ptr is not NULL.
61 : SetCrashKeyValuePairPtr set_crash_key_value_pair_ptr;
62 : SetCrashKeyValueImplPtr set_crash_key_value_impl_ptr;
63 : };
64 :
65 : // The default error handler. It is expected that this will be bound in a
66 : // callback in the ASAN runtime.
67 : // @param context The context when the error has been reported.
68 : // @param error_info The information about this error.
69 i : void DefaultErrorHandler(AsanErrorInfo* error_info) {
70 i : DCHECK(error_info != NULL);
71 :
72 : ULONG_PTR arguments[] = {
73 i : reinterpret_cast<ULONG_PTR>(&error_info->context),
74 : reinterpret_cast<ULONG_PTR>(error_info)
75 i : };
76 :
77 i : ::DebugBreak();
78 : ::RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
79 : 0,
80 : ARRAYSIZE(arguments),
81 i : &arguments[0]);
82 i : }
83 :
84 : // Returns the breakpad crash reporting functions if breakpad is enabled for
85 : // the current executable.
86 : //
87 : // If we're running in the context of a breakpad enabled binary we can
88 : // report errors directly via that breakpad entry-point. This allows us
89 : // to report the exact context of the error without including the ASan RTL
90 : // in crash context, depending on where and when we capture the context.
91 : //
92 : // @param breakpad_functions The Breakpad functions structure to be populated.
93 : // @returns true if we found breakpad functions, false otherwise.
94 E : bool GetBreakpadFunctions(BreakpadFunctions* breakpad_functions) {
95 E : DCHECK(breakpad_functions != NULL);
96 :
97 : // Clear the structure.
98 E : ::memset(breakpad_functions, 0, sizeof(*breakpad_functions));
99 :
100 : // The named entry-point exposed to report a crash.
101 : static const char kCrashHandlerSymbol[] = "CrashForException";
102 :
103 : // The named entry-point exposed to annotate a crash with a key/value pair.
104 : static const char kSetCrashKeyValuePairSymbol[] = "SetCrashKeyValuePair";
105 : static const char kSetCrashKeyValueImplSymbol[] = "SetCrashKeyValueImpl";
106 :
107 : // Get a handle to the current executable image.
108 E : HMODULE exe_hmodule = ::GetModuleHandle(NULL);
109 :
110 : // Lookup the crash handler symbol.
111 : breakpad_functions->crash_for_exception_ptr =
112 : reinterpret_cast<WinProcExceptionFilter>(
113 E : ::GetProcAddress(exe_hmodule, kCrashHandlerSymbol));
114 E : if (breakpad_functions->crash_for_exception_ptr == NULL)
115 E : return false;
116 :
117 : // Lookup the crash annotation symbol.
118 : breakpad_functions->set_crash_key_value_pair_ptr =
119 : reinterpret_cast<SetCrashKeyValuePairPtr>(
120 i : ::GetProcAddress(exe_hmodule, kSetCrashKeyValuePairSymbol));
121 : breakpad_functions->set_crash_key_value_impl_ptr =
122 : reinterpret_cast<SetCrashKeyValueImplPtr>(
123 i : ::GetProcAddress(exe_hmodule, kSetCrashKeyValueImplSymbol));
124 :
125 i : return true;
126 E : }
127 :
128 : // Sets a crash key using the given breakpad function.
129 : void SetCrashKeyValuePair(const BreakpadFunctions& breakpad_functions,
130 : const char* key,
131 E : const char* value) {
132 E : if (breakpad_functions.set_crash_key_value_pair_ptr != NULL) {
133 i : breakpad_functions.set_crash_key_value_pair_ptr(key, value);
134 i : return;
135 : }
136 :
137 E : if (breakpad_functions.set_crash_key_value_impl_ptr != NULL) {
138 i : std::wstring wkey = UTF8ToWide(key);
139 i : std::wstring wvalue = UTF8ToWide(value);
140 : breakpad_functions.set_crash_key_value_impl_ptr(wkey.c_str(),
141 i : wvalue.c_str());
142 i : return;
143 : }
144 :
145 : return;
146 E : }
147 :
148 : // The breakpad error handler. It is expected that this will be bound in a
149 : // callback in the ASAN runtime.
150 : // @param breakpad_functions A struct containing pointers to the various
151 : // Breakpad reporting functions.
152 : // @param error_info The information about this error.
153 : void BreakpadErrorHandler(const BreakpadFunctions& breakpad_functions,
154 i : AsanErrorInfo* error_info) {
155 i : DCHECK(breakpad_functions.crash_for_exception_ptr != NULL);
156 i : DCHECK(error_info != NULL);
157 :
158 : SetCrashKeyValuePair(breakpad_functions,
159 : "asan-error-type",
160 i : HeapProxy::AccessTypeToStr(error_info->error_type));
161 :
162 i : if (error_info->shadow_info[0] != '\0') {
163 : SetCrashKeyValuePair(breakpad_functions,
164 : "asan-error-message",
165 i : error_info->shadow_info);
166 : }
167 :
168 i : EXCEPTION_RECORD exception = {};
169 i : exception.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
170 : exception.ExceptionAddress = reinterpret_cast<PVOID>(
171 i : error_info->context.Eip);
172 i : exception.NumberParameters = 2;
173 : exception.ExceptionInformation[0] = reinterpret_cast<ULONG_PTR>(
174 i : &error_info->context);
175 i : exception.ExceptionInformation[1] = reinterpret_cast<ULONG_PTR>(error_info);
176 :
177 i : EXCEPTION_POINTERS pointers = { &exception, &error_info->context };
178 i : breakpad_functions.crash_for_exception_ptr(&pointers);
179 i : NOTREACHED();
180 i : }
181 :
182 : // Trinary return values used to indicate if a flag was updated or not.
183 : enum FlagResult {
184 : kFlagNotPresent,
185 : kFlagSet,
186 : kFlagError
187 : };
188 :
189 : // Try to update the value of a size_t variable from a command-line.
190 : // @param cmd_line The command line who might contain a given parameter.
191 : // @param param_name The parameter that we want to read.
192 : // @param value Will receive the value of the parameter if it's present.
193 : // @returns kFlagNotPresent if the flag was not present and left at its default;
194 : // kFlagSet if the flag was present, valid and modified; or
195 : // kFlagError if the flag was present but invalid.
196 : FlagResult UpdateSizetFromCommandLine(const CommandLine& cmd_line,
197 : const std::string& param_name,
198 E : size_t* value) {
199 E : DCHECK(value != NULL);
200 E : if (!cmd_line.HasSwitch(param_name))
201 E : return kFlagNotPresent;
202 E : std::string value_str = cmd_line.GetSwitchValueASCII(param_name);
203 E : size_t new_value = 0;
204 E : if (!base::StringToSizeT(value_str, &new_value))
205 i : return kFlagError;
206 E : *value = new_value;
207 :
208 E : return kFlagSet;
209 E : }
210 :
211 : // Try to update the value of an array of ignored stack ids from a command-line.
212 : // We expect the values to be in hexadecimal format and separated by a
213 : // semi-colon.
214 : // @param cmd_line The command line to parse.
215 : // @param param_name The parameter that we want to read.
216 : // @param values Will receive the set of parsed values.
217 : // @returns true on success, false otherwise.
218 : bool ReadIgnoredStackIdsFromCommandLine(const CommandLine& cmd_line,
219 : const std::string& param_name,
220 E : AsanRuntime::StackIdSet* values) {
221 E : DCHECK(values != NULL);
222 E : if (!cmd_line.HasSwitch(param_name))
223 E : return true;
224 E : std::string value_str = cmd_line.GetSwitchValueASCII(param_name);
225 E : base::StringTokenizer string_tokenizer(value_str, ";");
226 E : while (string_tokenizer.GetNext()) {
227 E : int64 new_value = 0;
228 E : if (!base::HexStringToInt64(string_tokenizer.token(), &new_value))
229 i : return false;
230 E : values->insert(static_cast<StackCapture::StackId>(new_value));
231 E : }
232 E : return true;
233 E : }
234 :
235 : // A helper function to find if an intrusive list contains a given entry.
236 : // @param list The list in which we want to look for the entry.
237 : // @param item The entry we want to look for.
238 : // @returns true if the list contains this entry, false otherwise.
239 E : bool HeapListContainsEntry(const LIST_ENTRY* list, const LIST_ENTRY* item) {
240 E : LIST_ENTRY* current = list->Flink;
241 E : while (current != NULL) {
242 E : LIST_ENTRY* next_item = NULL;
243 E : if (current->Flink != list) {
244 E : next_item = current->Flink;
245 : }
246 :
247 E : if (current == item) {
248 E : return true;
249 : }
250 :
251 E : current = next_item;
252 E : }
253 i : return false;
254 E : }
255 :
256 : // Check if the current process is large address aware.
257 : // @returns true if it is, false otherwise.
258 E : bool CurrentProcessIsLargeAddressAware() {
259 E : const base::win::PEImage image(::GetModuleHandle(NULL));
260 :
261 : bool process_is_large_address_aware =
262 : (image.GetNTHeaders()->FileHeader.Characteristics &
263 E : IMAGE_FILE_LARGE_ADDRESS_AWARE) != 0;
264 :
265 E : return process_is_large_address_aware;
266 E : }
267 :
268 : // A helper function to send a command to Windbg. Windbg should first receive
269 : // the ".ocommand ASAN" command to treat those messages as commands.
270 E : void ASANDbgCmd(const wchar_t* fmt, ...) {
271 E : if (!base::debug::BeingDebugged())
272 E : return;
273 : // The string should start with "ASAN" to be interpreted by the debugger as a
274 : // command.
275 i : std::wstring command_wstring = L"ASAN ";
276 : va_list args;
277 i : va_start(args, fmt);
278 :
279 : // Append the actual command to the wstring.
280 i : base::StringAppendV(&command_wstring, fmt, args);
281 :
282 : // Append "; g" to make sure that the debugger continues its execution after
283 : // executing this command. This is needed because when the .ocommand function
284 : // is used under Windbg the debugger will break on OutputDebugString.
285 i : command_wstring.append(L"; g");
286 :
287 i : OutputDebugString(command_wstring.c_str());
288 E : }
289 :
290 : // A helper function to print a message to Windbg's console.
291 E : void ASANDbgMessage(const wchar_t* fmt, ...) {
292 E : if (!base::debug::BeingDebugged())
293 E : return;
294 : // Prepend the message with the .echo command so it'll be printed into the
295 : // debugger's console.
296 i : std::wstring message_wstring = L".echo ";
297 : va_list args;
298 i : va_start(args, fmt);
299 :
300 : // Append the actual message to the wstring.
301 i : base::StringAppendV(&message_wstring, fmt, args);
302 :
303 : // Treat the message as a command to print it.
304 i : ASANDbgCmd(message_wstring.c_str());
305 E : }
306 :
307 : // Switch to the caller's context and print its stack trace in Windbg.
308 : void ASANDbgPrintContext(const CONTEXT& context) {
309 : if (!base::debug::BeingDebugged())
310 : return;
311 : ASANDbgMessage(L"Caller's context (%p) and stack trace:", &context);
312 : ASANDbgCmd(L".cxr %p; kv", reinterpret_cast<uint32>(&context));
313 : }
314 :
315 : // Experiment groups.
316 : const size_t kExperimentQuarantineSizes[] = {
317 : 8 * 1024 * 1024,
318 : 16 * 1024 * 1024, // This is our current default.
319 : 32 * 1024 * 1024,
320 : 64 * 1024 * 1024 };
321 : // Average allocation size is 140 bytes, so each of these has an estimated
322 : // memory process overhead. The header/footer already account for 36 bytes.
323 : const size_t kExperimentTrailerPaddingSizes[] = {
324 : 0, // 36 byte red zone (25.7% overhead). This is our current default.
325 : 12, // 48 byte red zone (34.3% overhead).
326 : 28, // 64 byte red zone (45.7% overhead).
327 : 92 // 128 byte red zone (91.4% overhead).
328 : };
329 :
330 : // Gets the value of a coin toss, which is used for putting us into experimental
331 : // groups. We get this value by checking for a SYZYGY_ASAN_COIN_TOSS environment
332 : // variable. If the variable does not exist or is malformed, we consider that
333 : // the client is opted out of experiments. Otherwise, they are opted in and the
334 : // coin toss value (an unsigned 64 bit integer expressed in hex) is returned.
335 : // @param value Will be populated with the coin toss value on success, 0
336 : // otherwise.
337 : // @returns true if the client is opted in, false otherwise.
338 E : bool GetSyzygyAsanCoinToss(uint64* value) {
339 E : DCHECK_NE(reinterpret_cast<uint64*>(NULL), value);
340 :
341 E : *value = 0;
342 :
343 E : scoped_ptr<base::Environment> env(base::Environment::Create());
344 E : if (env.get() == NULL)
345 i : return false;
346 :
347 E : std::string s;
348 E : if (!env->GetVar(AsanRuntime::kSyzygyAsanCoinTossEnvVar, &s))
349 E : return false;
350 :
351 E : if (!base::HexStringToUInt64(s, value))
352 E : return false;
353 :
354 E : return true;
355 E : }
356 :
357 : } // namespace
358 :
359 : const char AsanRuntime::kSyzygyAsanCoinTossEnvVar[] = "SYZYGY_ASAN_COIN_TOSS";
360 : const char AsanRuntime::kSyzygyAsanOptionsEnvVar[] = "SYZYGY_ASAN_OPTIONS";
361 :
362 : const char AsanRuntime::kBottomFramesToSkip[] = "bottom_frames_to_skip";
363 : const char AsanRuntime::kCompressionReportingPeriod[] =
364 : "compression_reporting_period";
365 : const char AsanRuntime::kExitOnFailure[] = "exit_on_failure";
366 : const char AsanRuntime::kIgnoredStackIds[] = "ignored_stack_ids";
367 : const char AsanRuntime::kMaxNumberOfFrames[] = "max_num_frames";
368 : const char AsanRuntime::kMiniDumpOnFailure[] = "minidump_on_failure";
369 : const char AsanRuntime::kNoLogAsText[] = "no_log_as_text";
370 : const char AsanRuntime::kQuarantineSize[] = "quarantine_size";
371 : const wchar_t AsanRuntime::kSyzyAsanDll[] = L"syzyasan_rtl.dll";
372 : const char AsanRuntime::kTrailerPaddingSize[] = "trailer_padding_size";
373 :
374 : AsanRuntime::AsanRuntime()
375 : : logger_(NULL), stack_cache_(NULL), asan_error_callback_(), flags_(),
376 E : heap_proxy_dlist_lock_(), heap_proxy_dlist_() {
377 E : }
378 :
379 E : AsanRuntime::~AsanRuntime() {
380 E : }
381 :
382 E : void AsanRuntime::SetUp(const std::wstring& flags_command_line) {
383 : // Ensure that the current process is not large address aware. It shouldn't be
384 : // because the shadow memory assume that the process will only be able to use
385 : // 2GB of address space.
386 E : CHECK(!CurrentProcessIsLargeAddressAware());
387 :
388 : // Initialize the command-line structures. This is needed so that
389 : // SetUpLogger() can include the command-line in the message announcing
390 : // this process. Note: this is mostly for debugging purposes.
391 E : CommandLine::Init(0, NULL);
392 :
393 E : Shadow::SetUp();
394 :
395 E : InitializeListHead(&heap_proxy_dlist_);
396 :
397 : // Setup the "global" state.
398 E : StackCapture::Init();
399 E : StackCaptureCache::Init();
400 E : SetUpLogger();
401 E : SetUpStackCache();
402 E : HeapProxy::Init(stack_cache_.get());
403 E : if (!ParseFlagsFromString(flags_command_line)) {
404 i : LOG(ERROR) << "Unable to parse the flags from the input string (\""
405 : << flags_command_line.c_str() << "\").";
406 : }
407 :
408 : // Propagates the flags values to the different modules.
409 E : PropagateFlagsValues();
410 :
411 : // Register the error reporting callback to use if/when an ASAN error is
412 : // detected. If we're able to resolve a breakpad error reporting function
413 : // then use that; otherwise, fall back to the default error handler.
414 E : BreakpadFunctions breakpad_functions = {};
415 E : if (GetBreakpadFunctions(&breakpad_functions)) {
416 i : LOG(INFO) << "SyzyASAN: Using Breakpad for error reporting.";
417 i : SetErrorCallBack(base::Bind(&BreakpadErrorHandler, breakpad_functions));
418 i : } else {
419 E : LOG(INFO) << "SyzyASAN: Using default error reporting handler.";
420 E : SetErrorCallBack(base::Bind(&DefaultErrorHandler));
421 : }
422 :
423 : // Reporting of the experiment group. This is also reported via Finch/UMA, but
424 : // we duplicate it to the crash keys for ease of filtering.
425 E : if (flags_.opted_in) {
426 : SetCrashKeyValuePair(
427 : breakpad_functions,
428 : "asan-experiment-quarantine-size",
429 E : base::UintToString(flags_.quarantine_size).c_str());
430 : SetCrashKeyValuePair(
431 : breakpad_functions,
432 : "asan-experiment-trailer-padding-size",
433 E : base::UintToString(flags_.trailer_padding_size).c_str());
434 : }
435 E : }
436 :
437 E : void AsanRuntime::TearDown() {
438 E : TearDownStackCache();
439 E : TearDownLogger();
440 E : DCHECK(asan_error_callback_.is_null() == FALSE);
441 E : asan_error_callback_.Reset();
442 E : Shadow::TearDown();
443 : // In principle, we should also check that all the heaps have been destroyed
444 : // but this is not guaranteed to be the case in Chrome, so the heap list may
445 : // not be empty here.
446 E : }
447 :
448 E : void AsanRuntime::OnError(AsanErrorInfo* error_info) {
449 E : DCHECK(error_info != NULL);
450 :
451 : const char* bug_descr =
452 E : HeapProxy::AccessTypeToStr(error_info->error_type);
453 E : if (logger_->log_as_text()) {
454 : std::string output(base::StringPrintf(
455 : "SyzyASAN error: %s on address 0x%08X (stack_id=0x%08X)\n",
456 E : bug_descr, error_info->location, error_info->crash_stack_id));
457 E : if (error_info->access_mode != HeapProxy::ASAN_UNKNOWN_ACCESS) {
458 E : const char* access_mode_str = NULL;
459 E : if (error_info->access_mode == HeapProxy::ASAN_READ_ACCESS)
460 E : access_mode_str = "READ";
461 E : else
462 E : access_mode_str = "WRITE";
463 : base::StringAppendF(&output,
464 : "%s of size %d at 0x%08X\n",
465 : access_mode_str,
466 E : error_info->access_size);
467 : }
468 :
469 : // Log the failure and stack.
470 E : logger_->WriteWithContext(output, error_info->context);
471 :
472 E : logger_->Write(error_info->shadow_info);
473 E : if (error_info->free_stack_size != 0U) {
474 : logger_->WriteWithStackTrace("freed here:\n",
475 : error_info->free_stack,
476 E : error_info->free_stack_size);
477 : }
478 E : if (error_info->alloc_stack_size != NULL) {
479 : logger_->WriteWithStackTrace("previously allocated here:\n",
480 : error_info->alloc_stack,
481 E : error_info->alloc_stack_size);
482 : }
483 E : if (error_info->error_type >= HeapProxy::USE_AFTER_FREE) {
484 E : std::string shadow_text;
485 E : Shadow::AppendShadowMemoryText(error_info->location, &shadow_text);
486 E : logger_->Write(shadow_text);
487 E : }
488 E : }
489 :
490 : // Print the base of the Windbg help message.
491 : ASANDbgMessage(L"An Asan error has been found (%ls), here are the details:",
492 E : base::SysUTF8ToWide(bug_descr).c_str());
493 :
494 : // Print the Windbg information to display the allocation stack if present.
495 E : if (error_info->alloc_stack_size != NULL) {
496 E : ASANDbgMessage(L"Allocation stack trace:");
497 : ASANDbgCmd(L"dps %p l%d",
498 : error_info->alloc_stack,
499 E : error_info->alloc_stack_size);
500 : }
501 :
502 : // Print the Windbg information to display the free stack if present.
503 E : if (error_info->free_stack_size != NULL) {
504 E : ASANDbgMessage(L"Free stack trace:");
505 : ASANDbgCmd(L"dps %p l%d",
506 : error_info->free_stack,
507 E : error_info->free_stack_size);
508 : }
509 :
510 E : if (flags_.minidump_on_failure) {
511 i : DCHECK(logger_.get() != NULL);
512 i : logger_->SaveMiniDump(&error_info->context, error_info);
513 : }
514 :
515 E : if (flags_.exit_on_failure) {
516 E : DCHECK(logger_.get() != NULL);
517 E : logger_->Stop();
518 E : exit(EXIT_FAILURE);
519 : }
520 :
521 : // Call the callback to handle this error.
522 E : DCHECK(!asan_error_callback_.is_null());
523 E : asan_error_callback_.Run(error_info);
524 E : }
525 :
526 E : void AsanRuntime::SetErrorCallBack(const AsanOnErrorCallBack& callback) {
527 E : asan_error_callback_ = callback;
528 E : }
529 :
530 E : void AsanRuntime::SetUpLogger() {
531 : // Setup variables we're going to use.
532 E : scoped_ptr<base::Environment> env(base::Environment::Create());
533 E : scoped_ptr<AsanLogger> client(new AsanLogger);
534 E : CHECK(env.get() != NULL);
535 E : CHECK(client.get() != NULL);
536 :
537 : // Initialize the client.
538 : client->set_instance_id(
539 E : UTF8ToWide(trace::client::GetInstanceIdForThisModule()));
540 E : client->Init();
541 :
542 : // Register the client singleton instance.
543 E : logger_.reset(client.release());
544 E : }
545 :
546 E : void AsanRuntime::TearDownLogger() {
547 E : logger_.reset();
548 E : }
549 :
550 E : void AsanRuntime::SetUpStackCache() {
551 E : DCHECK(stack_cache_.get() == NULL);
552 E : DCHECK(logger_.get() != NULL);
553 E : stack_cache_.reset(new StackCaptureCache(logger_.get()));
554 E : }
555 :
556 E : void AsanRuntime::TearDownStackCache() {
557 E : DCHECK(stack_cache_.get() != NULL);
558 E : stack_cache_->LogStatistics();
559 E : stack_cache_.reset();
560 E : }
561 :
562 E : bool AsanRuntime::ParseFlagsFromString(std::wstring str) {
563 : // Prepends the flags with the agent name. We need to do this because the
564 : // command-line constructor expect the process name to be the first value of
565 : // the command-line string.
566 : // Start by inserting a space at the beginning of the flags to separate the
567 : // flags from the agent name.
568 E : str.insert(0, L" ");
569 : // Insert the agent name.
570 E : str.insert(0, kSyzyAsanDll);
571 :
572 E : CommandLine cmd_line = CommandLine::FromString(str);
573 :
574 : // Get our experiment status.
575 E : flags_.opted_in = GetSyzygyAsanCoinToss(&flags_.coin_toss);
576 E : uint64 coin_toss = flags_.coin_toss;
577 :
578 : // Parse the quarantine size flag.
579 E : flags_.quarantine_size = HeapProxy::default_quarantine_max_size();
580 : FlagResult flag_result = UpdateSizetFromCommandLine(
581 E : cmd_line, kQuarantineSize, &flags_.quarantine_size);
582 E : if (flag_result == kFlagError) {
583 i : LOG(ERROR) << "Unable to read " << kQuarantineSize << " from the argument "
584 : << "list.";
585 i : return false;
586 E : } else if (flag_result == kFlagNotPresent && flags_.opted_in) {
587 E : size_t n = arraysize(kExperimentQuarantineSizes);
588 E : flags_.quarantine_size = kExperimentQuarantineSizes[coin_toss % n];
589 E : coin_toss /= n;
590 E : LOG(INFO) << "Using experiment quarantine size of "
591 : << flags_.quarantine_size << ".";
592 : }
593 :
594 : // Parse the trailer padding size flag.
595 E : flags_.trailer_padding_size = 0;
596 : flag_result = UpdateSizetFromCommandLine(
597 E : cmd_line, kTrailerPaddingSize, &flags_.trailer_padding_size);
598 E : if (flag_result == kFlagError) {
599 i : LOG(ERROR) << "Unable to read " << kTrailerPaddingSize << " from the "
600 : << "argument list.";
601 i : return false;
602 E : } else if (flag_result == kFlagNotPresent && flags_.opted_in) {
603 E : size_t n = arraysize(kExperimentTrailerPaddingSizes);
604 E : flags_.trailer_padding_size = kExperimentTrailerPaddingSizes[coin_toss % n];
605 E : coin_toss /= n;
606 E : LOG(INFO) << "Using experiment trailer padding size of "
607 : << flags_.trailer_padding_size << ".";
608 : }
609 :
610 : // Parse the reporting period flag.
611 : flags_.reporting_period =
612 E : StackCaptureCache::GetDefaultCompressionReportingPeriod();
613 : if (UpdateSizetFromCommandLine(cmd_line, kCompressionReportingPeriod,
614 E : &flags_.reporting_period) == kFlagError) {
615 i : LOG(ERROR) << "Unable to read " << kCompressionReportingPeriod
616 : << " from the argument list.";
617 i : return false;
618 : }
619 :
620 : // Parse the bottom frames to skip flag.
621 E : flags_.bottom_frames_to_skip = StackCapture::bottom_frames_to_skip();
622 : if (UpdateSizetFromCommandLine(cmd_line, kBottomFramesToSkip,
623 E : &flags_.bottom_frames_to_skip) == kFlagError) {
624 i : LOG(ERROR) << "Unable to read " << kBottomFramesToSkip << " from the "
625 : << "argument list.";
626 i : return false;
627 : }
628 :
629 : // Parse the max number of frames flag.
630 E : flags_.max_num_frames = stack_cache_->max_num_frames();
631 : if (UpdateSizetFromCommandLine(cmd_line, kMaxNumberOfFrames,
632 E : &flags_.max_num_frames) == kFlagError) {
633 i : LOG(ERROR) << "Unable to read " << kMaxNumberOfFrames << " from the "
634 : << "argument list.";
635 i : return false;
636 : }
637 :
638 : // Parse the ignored stack ids.
639 : if (!ReadIgnoredStackIdsFromCommandLine(cmd_line, kIgnoredStackIds,
640 E : &flags_.ignored_stack_ids)) {
641 i : LOG(ERROR) << "Unable to read " << kIgnoredStackIds << " from the "
642 : << "argument list.";
643 i : return false;
644 : }
645 :
646 : // Parse the other (boolean) flags.
647 E : flags_.exit_on_failure = cmd_line.HasSwitch(kExitOnFailure);
648 E : flags_.minidump_on_failure = cmd_line.HasSwitch(kMiniDumpOnFailure);
649 E : flags_.log_as_text = !cmd_line.HasSwitch(kNoLogAsText);
650 :
651 E : return true;
652 E : }
653 :
654 E : bool AsanRuntime::GetAsanFlagsEnvVar(std::wstring* env_var_wstr) {
655 E : scoped_ptr<base::Environment> env(base::Environment::Create());
656 E : if (env.get() == NULL) {
657 i : LOG(ERROR) << "base::Environment::Create returned NULL.";
658 i : return false;
659 : }
660 :
661 : // If this fails, the environment variable simply does not exist.
662 E : std::string env_var_str;
663 E : if (!env->GetVar(kSyzygyAsanOptionsEnvVar, &env_var_str)) {
664 E : return true;
665 : }
666 :
667 i : *env_var_wstr = base::SysUTF8ToWide(env_var_str);
668 :
669 i : return true;
670 E : }
671 :
672 E : void AsanRuntime::PropagateFlagsValues() const {
673 : // TODO(sebmarchand): Look into edit-free ways to expose new flags to the
674 : // different modules.
675 E : HeapProxy::set_trailer_padding_size(flags_.trailer_padding_size);
676 E : HeapProxy::set_default_quarantine_max_size(flags_.quarantine_size);
677 E : StackCapture::set_bottom_frames_to_skip(flags_.bottom_frames_to_skip);
678 E : StackCaptureCache::set_compression_reporting_period(flags_.reporting_period);
679 E : stack_cache_->set_max_num_frames(flags_.max_num_frames);
680 E : logger_->set_log_as_text(flags_.log_as_text);
681 E : logger_->set_minidump_on_failure(flags_.minidump_on_failure);
682 E : }
683 :
684 E : void AsanRuntime::set_flags(const AsanFlags* flags) {
685 E : DCHECK(flags != NULL);
686 E : flags_ = *flags;
687 E : }
688 :
689 E : void AsanRuntime::AddHeap(HeapProxy* heap) {
690 E : base::AutoLock lock(heap_proxy_dlist_lock_);
691 E : InsertTailList(&heap_proxy_dlist_, HeapProxy::ToListEntry(heap));
692 E : }
693 :
694 E : void AsanRuntime::RemoveHeap(HeapProxy* heap) {
695 E : base::AutoLock lock(heap_proxy_dlist_lock_);
696 : DCHECK(HeapListContainsEntry(&heap_proxy_dlist_,
697 E : HeapProxy::ToListEntry(heap)));
698 E : RemoveEntryList(HeapProxy::ToListEntry(heap));
699 E : }
700 :
701 E : void AsanRuntime::GetBadAccessInformation(AsanErrorInfo* error_info) {
702 E : base::AutoLock lock(heap_proxy_dlist_lock_);
703 :
704 : // Checks if this is an access to an internal structure or if it's an access
705 : // in the upper region of the memory (over the 2 GB limit).
706 : if ((reinterpret_cast<size_t>(error_info->location) & (1 << 31)) != 0 ||
707 : Shadow::GetShadowMarkerForAddress(error_info->location)
708 E : == Shadow::kAsanMemoryByte) {
709 E : error_info->error_type = HeapProxy::WILD_ACCESS;
710 E : } else if (Shadow::GetShadowMarkerForAddress(error_info->location) ==
711 E : Shadow::kInvalidAddress) {
712 E : error_info->error_type = HeapProxy::INVALID_ADDRESS;
713 E : } else {
714 : // TODO(sebmarchand): Add some code to check if the heap is corrupted.
715 E : HeapProxy::GetBadAccessInformation(error_info);
716 : }
717 E : }
718 :
719 : } // namespace asan
720 : } // namespace agent
|