//===-- sanitizer_flags.cc ------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer/AddressSanitizer runtime. // //===----------------------------------------------------------------------===// #include "sanitizer_flags.h" #include "sanitizer_common.h" #include "sanitizer_libc.h" #include "sanitizer_list.h" namespace __sanitizer { CommonFlags common_flags_dont_use; struct FlagDescription { const char *name; const char *description; FlagDescription *next; }; IntrusiveList<FlagDescription> flag_descriptions; // If set, the tool will install its own SEGV signal handler by default. #ifndef SANITIZER_NEEDS_SEGV # define SANITIZER_NEEDS_SEGV 1 #endif void SetCommonFlagsDefaults(CommonFlags *f) { f->symbolize = true; f->external_symbolizer_path = 0; f->allow_addr2line = false; f->strip_path_prefix = ""; f->fast_unwind_on_fatal = false; f->fast_unwind_on_malloc = true; f->handle_ioctl = false; f->malloc_context_size = 1; f->log_path = "stderr"; f->verbosity = 0; f->detect_leaks = true; f->leak_check_at_exit = true; f->allocator_may_return_null = false; f->print_summary = true; f->check_printf = true; // TODO(glider): tools may want to set different defaults for handle_segv. f->handle_segv = SANITIZER_NEEDS_SEGV; f->allow_user_segv_handler = false; f->use_sigaltstack = true; f->detect_deadlocks = false; f->clear_shadow_mmap_threshold = 64 * 1024; f->color = "auto"; f->legacy_pthread_cond = false; f->intercept_tls_get_addr = false; f->coverage = false; f->coverage_direct = SANITIZER_ANDROID; f->coverage_dir = "."; f->full_address_space = false; } void ParseCommonFlagsFromString(CommonFlags *f, const char *str) { ParseFlag(str, &f->symbolize, "symbolize", "If set, use the online symbolizer from common sanitizer runtime to turn " "virtual addresses to file/line locations."); ParseFlag(str, &f->external_symbolizer_path, "external_symbolizer_path", "Path to external symbolizer. If empty, the tool will search $PATH for " "the symbolizer."); ParseFlag(str, &f->allow_addr2line, "allow_addr2line", "If set, allows online symbolizer to run addr2line binary to symbolize " "stack traces (addr2line will only be used if llvm-symbolizer binary is " "unavailable."); ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix", "Strips this prefix from file paths in error reports."); ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal", "If available, use the fast frame-pointer-based unwinder on fatal " "errors."); ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc", "If available, use the fast frame-pointer-based unwinder on " "malloc/free."); ParseFlag(str, &f->handle_ioctl, "handle_ioctl", "Intercept and handle ioctl requests."); ParseFlag(str, &f->malloc_context_size, "malloc_context_size", "Max number of stack frames kept for each allocation/deallocation."); ParseFlag(str, &f->log_path, "log_path", "Write logs to \"log_path.pid\". The special values are \"stdout\" and " "\"stderr\". The default is \"stderr\"."); ParseFlag(str, &f->verbosity, "verbosity", "Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output)."); ParseFlag(str, &f->detect_leaks, "detect_leaks", "Enable memory leak detection."); ParseFlag(str, &f->leak_check_at_exit, "leak_check_at_exit", "Invoke leak checking in an atexit handler. Has no effect if " "detect_leaks=false, or if __lsan_do_leak_check() is called before the " "handler has a chance to run."); ParseFlag(str, &f->allocator_may_return_null, "allocator_may_return_null", "If false, the allocator will crash instead of returning 0 on " "out-of-memory."); ParseFlag(str, &f->print_summary, "print_summary", "If false, disable printing error summaries in addition to error " "reports."); ParseFlag(str, &f->check_printf, "check_printf", "Check printf arguments."); ParseFlag(str, &f->handle_segv, "handle_segv", "If set, registers the tool's custom SEGV handler (both SIGBUS and " "SIGSEGV on OSX)."); ParseFlag(str, &f->allow_user_segv_handler, "allow_user_segv_handler", "If set, allows user to register a SEGV handler even if the tool " "registers one."); ParseFlag(str, &f->use_sigaltstack, "use_sigaltstack", "If set, uses alternate stack for signal handling."); ParseFlag(str, &f->detect_deadlocks, "detect_deadlocks", "If set, deadlock detection is enabled."); ParseFlag(str, &f->clear_shadow_mmap_threshold, "clear_shadow_mmap_threshold", "Large shadow regions are zero-filled using mmap(NORESERVE) instead of " "memset(). This is the threshold size in bytes."); ParseFlag(str, &f->color, "color", "Colorize reports: (always|never|auto)."); ParseFlag(str, &f->legacy_pthread_cond, "legacy_pthread_cond", "Enables support for dynamic libraries linked with libpthread 2.2.5."); ParseFlag(str, &f->intercept_tls_get_addr, "intercept_tls_get_addr", "Intercept __tls_get_addr."); ParseFlag(str, &f->help, "help", "Print the flag descriptions."); ParseFlag(str, &f->mmap_limit_mb, "mmap_limit_mb", "Limit the amount of mmap-ed memory (excluding shadow) in Mb; " "not a user-facing flag, used mosly for testing the tools"); ParseFlag(str, &f->coverage, "coverage", "If set, coverage information will be dumped at program shutdown (if the " "coverage instrumentation was enabled at compile time)."); ParseFlag(str, &f->coverage_direct, "coverage_direct", "If set, coverage information will be dumped directly to a memory " "mapped file. This way data is not lost even if the process is " "suddenly killed."); ParseFlag(str, &f->coverage_dir, "coverage_dir", "Target directory for coverage dumps. Defaults to the current " "directory."); ParseFlag(str, &f->full_address_space, "full_address_space", "Sanitize complete address space; " "by default kernel area on 32-bit platforms will not be sanitized"); // Do a sanity check for certain flags. if (f->malloc_context_size < 1) f->malloc_context_size = 1; } static bool GetFlagValue(const char *env, const char *name, const char **value, int *value_length) { if (env == 0) return false; const char *pos = 0; for (;;) { pos = internal_strstr(env, name); if (pos == 0) return false; const char *name_end = pos + internal_strlen(name); if ((pos != env && ((pos[-1] >= 'a' && pos[-1] <= 'z') || pos[-1] == '_')) || *name_end != '=') { // Seems to be middle of another flag name or value. env = pos + 1; continue; } pos = name_end; break; } const char *end; if (pos[0] != '=') { end = pos; } else { pos += 1; if (pos[0] == '"') { pos += 1; end = internal_strchr(pos, '"'); } else if (pos[0] == '\'') { pos += 1; end = internal_strchr(pos, '\''); } else { // Read until the next space or colon. end = pos + internal_strcspn(pos, " :"); } if (end == 0) end = pos + internal_strlen(pos); } *value = pos; *value_length = end - pos; return true; } static bool StartsWith(const char *flag, int flag_length, const char *value) { if (!flag || !value) return false; int value_length = internal_strlen(value); return (flag_length >= value_length) && (0 == internal_strncmp(flag, value, value_length)); } static LowLevelAllocator allocator_for_flags; // The linear scan is suboptimal, but the number of flags is relatively small. bool FlagInDescriptionList(const char *name) { IntrusiveList<FlagDescription>::Iterator it(&flag_descriptions); while (it.hasNext()) { if (!internal_strcmp(it.next()->name, name)) return true; } return false; } void AddFlagDescription(const char *name, const char *description) { if (FlagInDescriptionList(name)) return; FlagDescription *new_description = new(allocator_for_flags) FlagDescription; new_description->name = name; new_description->description = description; flag_descriptions.push_back(new_description); } // TODO(glider): put the descriptions inside CommonFlags. void PrintFlagDescriptions() { IntrusiveList<FlagDescription>::Iterator it(&flag_descriptions); Printf("Available flags for %s:\n", SanitizerToolName); while (it.hasNext()) { FlagDescription *descr = it.next(); Printf("\t%s\n\t\t- %s\n", descr->name, descr->description); } } void ParseFlag(const char *env, bool *flag, const char *name, const char *descr) { const char *value; int value_length; AddFlagDescription(name, descr); if (!GetFlagValue(env, name, &value, &value_length)) return; if (StartsWith(value, value_length, "0") || StartsWith(value, value_length, "no") || StartsWith(value, value_length, "false")) *flag = false; if (StartsWith(value, value_length, "1") || StartsWith(value, value_length, "yes") || StartsWith(value, value_length, "true")) *flag = true; } void ParseFlag(const char *env, int *flag, const char *name, const char *descr) { const char *value; int value_length; AddFlagDescription(name, descr); if (!GetFlagValue(env, name, &value, &value_length)) return; *flag = static_cast<int>(internal_atoll(value)); } void ParseFlag(const char *env, uptr *flag, const char *name, const char *descr) { const char *value; int value_length; AddFlagDescription(name, descr); if (!GetFlagValue(env, name, &value, &value_length)) return; *flag = static_cast<uptr>(internal_atoll(value)); } void ParseFlag(const char *env, const char **flag, const char *name, const char *descr) { const char *value; int value_length; AddFlagDescription(name, descr); if (!GetFlagValue(env, name, &value, &value_length)) return; // Copy the flag value. Don't use locks here, as flags are parsed at // tool startup. char *value_copy = (char*)(allocator_for_flags.Allocate(value_length + 1)); internal_memcpy(value_copy, value, value_length); value_copy[value_length] = '\0'; *flag = value_copy; } } // namespace __sanitizer