[sanitizer] Flag parser rewrite.

The new parser is a lot stricter about syntax, reports unrecognized
flags, and will make it easier to implemented some of the planned features.

llvm-svn: 226169
This commit is contained in:
Evgeniy Stepanov 2015-01-15 15:13:43 +00:00
parent 1a1a7d0f30
commit f294d5b829
32 changed files with 471 additions and 281 deletions

View File

@ -234,7 +234,7 @@ append_list_if(COMPILER_RT_HAS_FUNWIND_TABLES_FLAG -funwind-tables SANITIZER_COM
append_list_if(COMPILER_RT_HAS_FNO_STACK_PROTECTOR_FLAG -fno-stack-protector SANITIZER_COMMON_CFLAGS)
append_list_if(COMPILER_RT_HAS_FVISIBILITY_HIDDEN_FLAG -fvisibility=hidden SANITIZER_COMMON_CFLAGS)
append_list_if(COMPILER_RT_HAS_FNO_FUNCTION_SECTIONS_FLAG -fno-function-sections SANITIZER_COMMON_CFLAGS)
append_list_if(COMPILER_RT_HAS_FNO_LTO_FLAG -fno-lto SANITIZER_COMMON_CFLAGS)
# append_list_if(COMPILER_RT_HAS_FNO_LTO_FLAG -fno-lto SANITIZER_COMMON_CFLAGS)
if(MSVC)
# Replace the /MD[d] flags with /MT.

View File

@ -32,6 +32,9 @@ static struct AsanDeactivatedFlags {
void OverrideFromActivationFlags() {
Flags f;
CommonFlags cf;
FlagParser parser;
RegisterAsanFlags(&parser, &f);
RegisterCommonFlags(&parser, &cf);
// Copy the current activation flags.
allocator_options.CopyTo(&f, &cf);
@ -44,15 +47,13 @@ static struct AsanDeactivatedFlags {
// FIXME: Add diagnostic to check that activation flags string doesn't
// contain any other flags.
if (const char *env = GetEnv("ASAN_ACTIVATION_OPTIONS")) {
cf.ParseFromString(env);
f.ParseFromString(env);
parser.ParseString(env);
}
// Override from getprop asan.options.
char buf[100];
GetExtraActivationFlags(buf, sizeof(buf));
cf.ParseFromString(buf);
f.ParseFromString(buf);
parser.ParseString(buf);
allocator_options.SetFrom(&f, &cf);
malloc_context_size = cf.malloc_context_size;

View File

@ -19,6 +19,7 @@
#include "lsan/lsan_common.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_flag_parser.h"
namespace __asan {
@ -45,14 +46,18 @@ void Flags::SetDefaults() {
#undef ASAN_FLAG
}
void Flags::ParseFromString(const char *str) {
#define ASAN_FLAG(Type, Name, DefaultValue, Description) \
ParseFlag(str, &Name, #Name, Description);
void RegisterAsanFlags(FlagParser *parser, Flags *f) {
#define ASAN_FLAG(Type, Name, DefaultValue, Description) \
RegisterFlag(parser, #Name, Description, &f->Name);
#include "asan_flags.inc"
#undef ASAN_FLAG
}
void InitializeFlags(Flags *f) {
FlagParser parser;
RegisterAsanFlags(&parser, f);
RegisterCommonFlags(&parser);
SetCommonFlagsDefaults();
{
CommonFlags cf;
@ -69,20 +74,17 @@ void InitializeFlags(Flags *f) {
// Override from compile definition.
const char *compile_def = MaybeUseAsanDefaultOptionsCompileDefinition();
ParseCommonFlagsFromString(compile_def);
f->ParseFromString(compile_def);
parser.ParseString(compile_def);
// Override from user-specified string.
const char *default_options = MaybeCallAsanDefaultOptions();
ParseCommonFlagsFromString(default_options);
f->ParseFromString(default_options);
parser.ParseString(default_options);
VReport(1, "Using the defaults from __asan_default_options: %s\n",
MaybeCallAsanDefaultOptions());
// Override from command line.
if (const char *env = GetEnv("ASAN_OPTIONS")) {
ParseCommonFlagsFromString(env);
f->ParseFromString(env);
parser.ParseString(env);
VReport(1, "Parsed ASAN_OPTIONS: %s\n", env);
}
@ -91,14 +93,13 @@ void InitializeFlags(Flags *f) {
if (!flags()->start_deactivated) {
char buf[100];
GetExtraActivationFlags(buf, sizeof(buf));
ParseCommonFlagsFromString(buf);
f->ParseFromString(buf);
parser.ParseString(buf);
if (buf[0] != '\0')
VReport(1, "Parsed activation flags: %s\n", buf);
}
if (common_flags()->help) {
PrintFlagDescriptions();
parser.PrintFlagDescriptions();
}
// Flag validation:

View File

@ -16,6 +16,7 @@
#define ASAN_FLAGS_H
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_flag_parser.h"
// ASan flag values can be defined in four ways:
// 1) initialized with default values at startup.
@ -34,13 +35,13 @@ struct Flags {
#undef ASAN_FLAG
void SetDefaults();
void ParseFromString(const char *str);
};
extern Flags asan_flags_dont_use_directly;
inline Flags *flags() {
return &asan_flags_dont_use_directly;
}
void RegisterAsanFlags(FlagParser *parser, Flags *f);
void InitializeFlags(Flags *f);
} // namespace __asan

View File

@ -30,7 +30,8 @@ set(ASAN_UNITTEST_COMMON_CFLAGS
-fno-rtti
-O2
-Wno-format
-Werror=sign-compare)
-Werror=sign-compare
-Wno-non-virtual-dtor)
append_list_if(COMPILER_RT_HAS_WVARIADIC_MACROS_FLAG -Wno-variadic-macros ASAN_UNITTEST_COMMON_CFLAGS)
# -gline-tables-only must be enough for ASan, so use it if possible.

View File

@ -5,7 +5,7 @@ set(DFSAN_RTL_SOURCES
dfsan.cc
dfsan_custom.cc
dfsan_interceptors.cc)
set(DFSAN_COMMON_CFLAGS ${SANITIZER_COMMON_CFLAGS})
set(DFSAN_COMMON_CFLAGS ${SANITIZER_COMMON_CFLAGS} -fno-rtti)
# Prevent clang from generating libc calls.
append_list_if(COMPILER_RT_HAS_FFREESTANDING_FLAG -ffreestanding DFSAN_COMMON_CFLAGS)

View File

@ -22,6 +22,7 @@
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_flag_parser.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "dfsan/dfsan.h"
@ -316,16 +317,18 @@ void Flags::SetDefaults() {
#undef DFSAN_FLAG
}
void Flags::ParseFromString(const char *str) {
#define DFSAN_FLAG(Type, Name, DefaultValue, Description) \
ParseFlag(str, &Name, #Name, Description);
void RegisterDfsanFlags(FlagParser *parser, Flags *f) {
#define DFSAN_FLAG(Type, Name, DefaultValue, Description) \
RegisterFlag(parser, #Name, Description, &f->Name);
#include "dfsan_flags.inc"
#undef DFSAN_FLAG
}
static void InitializeFlags(Flags &f, const char *env) {
FlagParser parser;
RegisterDfsanFlags(&parser, &f);
f.SetDefaults();
f.ParseFromString(env);
parser.ParseString(env);
}
static void dfsan_fini() {

View File

@ -61,7 +61,6 @@ struct Flags {
#undef DFSAN_FLAG
void SetDefaults();
void ParseFromString(const char *str);
};
extern Flags flags_data;

View File

@ -16,6 +16,7 @@
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_flag_parser.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
@ -42,19 +43,20 @@ void Flags::SetDefaults() {
#undef LSAN_FLAG
}
void Flags::ParseFromString(const char *str) {
#define LSAN_FLAG(Type, Name, DefaultValue, Description) \
ParseFlag(str, &Name, #Name, Description);
static void RegisterLsanFlags(FlagParser *parser, Flags *f) {
#define LSAN_FLAG(Type, Name, DefaultValue, Description) \
RegisterFlag(parser, #Name, Description, &f->Name);
#include "lsan_flags.inc"
#undef LSAN_FLAG
}
static void InitializeFlags(bool standalone) {
Flags *f = flags();
f->SetDefaults();
FlagParser parser;
RegisterLsanFlags(&parser, f);
RegisterCommonFlags(&parser);
const char *options = GetEnv("LSAN_OPTIONS");
f->ParseFromString(options);
f->SetDefaults();
// Set defaults for common flags (only in standalone mode) and parse
// them from LSAN_OPTIONS.
@ -67,7 +69,9 @@ static void InitializeFlags(bool standalone) {
cf.detect_leaks = true;
OverrideCommonFlags(cf);
}
ParseCommonFlagsFromString(options);
const char *options = GetEnv("LSAN_OPTIONS");
parser.ParseString(options);
}
#define LOG_POINTERS(...) \

View File

@ -43,7 +43,6 @@ struct Flags {
#undef LSAN_FLAG
void SetDefaults();
void ParseFromString(const char *str);
uptr pointer_alignment() const {
return use_unaligned ? 1 : sizeof(uptr);
}

View File

@ -19,13 +19,13 @@
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_flag_parser.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
// ACHTUNG! No system header includes in this file.
using namespace __sanitizer;
@ -102,20 +102,40 @@ void Flags::SetDefaults() {
#undef MSAN_FLAG
}
void Flags::ParseFromString(const char *str) {
// keep_going is an old name for halt_on_error,
// and it has inverse meaning.
halt_on_error = !halt_on_error;
ParseFlag(str, &halt_on_error, "keep_going", "");
halt_on_error = !halt_on_error;
// keep_going is an old name for halt_on_error,
// and it has inverse meaning.
class FlagHandlerKeepGoing : public FlagHandlerBase {
bool *halt_on_error_;
#define MSAN_FLAG(Type, Name, DefaultValue, Description) \
ParseFlag(str, &Name, #Name, Description);
public:
explicit FlagHandlerKeepGoing(bool *halt_on_error)
: halt_on_error_(halt_on_error) {}
bool Parse(const char *value) {
bool tmp;
FlagHandler<bool> h(&tmp);
if (!h.Parse(value)) return false;
*halt_on_error_ = !tmp;
return true;
}
};
void RegisterMsanFlags(FlagParser *parser, Flags *f) {
#define MSAN_FLAG(Type, Name, DefaultValue, Description) \
RegisterFlag(parser, #Name, Description, &f->Name);
#include "msan_flags.inc"
#undef MSAN_FLAG
FlagHandlerKeepGoing *fh_keep_going =
new (INTERNAL_ALLOC) FlagHandlerKeepGoing(&f->halt_on_error); // NOLINT
parser->RegisterHandler("keep_going", fh_keep_going,
"deprecated, use halt_on_error");
}
static void InitializeFlags(Flags *f, const char *options) {
FlagParser parser;
RegisterMsanFlags(&parser, f);
RegisterCommonFlags(&parser);
SetCommonFlagsDefaults();
{
CommonFlags cf;
@ -132,13 +152,12 @@ static void InitializeFlags(Flags *f, const char *options) {
f->SetDefaults();
// Override from user-specified string.
if (__msan_default_options) {
f->ParseFromString(__msan_default_options());
ParseCommonFlagsFromString(__msan_default_options());
}
if (__msan_default_options)
parser.ParseString(__msan_default_options());
f->ParseFromString(options);
ParseCommonFlagsFromString(options);
parser.ParseString(options);
if (common_flags()->help) parser.PrintFlagDescriptions();
// Check flag values:
if (f->exit_code < 0 || f->exit_code > 127) {
@ -328,7 +347,6 @@ void __msan_init() {
const char *msan_options = GetEnv("MSAN_OPTIONS");
InitializeFlags(&msan_flags, msan_options);
if (common_flags()->help) PrintFlagDescriptions();
__sanitizer_set_report_path(common_flags()->log_path);
InitializeInterceptors();

View File

@ -21,7 +21,6 @@ struct Flags {
#undef MSAN_FLAG
void SetDefaults();
void ParseFromString(const char *str);
};
Flags *flags();

View File

@ -7,6 +7,7 @@ set(SANITIZER_SOURCES
sanitizer_deadlock_detector1.cc
sanitizer_deadlock_detector2.cc
sanitizer_flags.cc
sanitizer_flag_parser.cc
sanitizer_libc.cc
sanitizer_libignore.cc
sanitizer_linux.cc
@ -63,6 +64,7 @@ set(SANITIZER_HEADERS
sanitizer_common_syscalls.inc
sanitizer_deadlock_detector.h
sanitizer_deadlock_detector_interface.h
sanitizer_flag_parser.h
sanitizer_flags.h
sanitizer_flags.inc
sanitizer_internal_defs.h

View File

@ -49,6 +49,15 @@ void *InternalAlloc(uptr size, InternalAllocatorCache *cache = 0);
void InternalFree(void *p, InternalAllocatorCache *cache = 0);
InternalAllocator *internal_allocator();
enum InternalAllocEnum {
INTERNAL_ALLOC
};
} // namespace __sanitizer
inline void *operator new(__sanitizer::operator_new_size_type size,
InternalAllocEnum) {
return InternalAlloc(size);
}
#endif // SANITIZER_ALLOCATOR_INTERNAL_H

View File

@ -0,0 +1,117 @@
//===-- sanitizer_flag_parser.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_flag_parser.h"
#include "sanitizer_common.h"
#include "sanitizer_libc.h"
#include "sanitizer_flags.h"
#include "sanitizer_flag_parser.h"
#include "sanitizer_allocator_internal.h"
namespace __sanitizer {
void FlagParser::PrintFlagDescriptions() {
Printf("Available flags for %s:\n", SanitizerToolName);
for (int i = 0; i < n_flags_; ++i)
Printf("\t%s\n\t\t- %s\n", flags_[i].name, flags_[i].desc);
}
void FlagParser::fatal_error(const char *err) {
Printf("ERROR: %s\n", err);
Die();
}
bool FlagParser::is_space(char c) {
return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' ||
c == '\r';
}
void FlagParser::skip_whitespace() {
while (is_space(buf_[pos_])) ++pos_;
}
void FlagParser::parse_flag() {
uptr name_start = pos_;
while (buf_[pos_] != 0 && buf_[pos_] != '=' && !is_space(buf_[pos_])) ++pos_;
if (buf_[pos_] != '=') fatal_error("expected '='");
const char *name = internal_strndup(buf_ + name_start, pos_ - name_start);
uptr value_start = ++pos_;
const char *value;
if (buf_[pos_] == '\'' || buf_[pos_] == '"') {
char quote = buf_[pos_++];
while (buf_[pos_] != 0 && buf_[pos_] != quote) ++pos_;
if (buf_[pos_] == 0) fatal_error("unterminated string");
value = internal_strndup(buf_ + value_start + 1, pos_ - value_start - 1);
++pos_; // consume the closing quote
} else {
while (buf_[pos_] != 0 && !is_space(buf_[pos_])) ++pos_;
if (buf_[pos_] != 0 && !is_space(buf_[pos_]))
fatal_error("expected separator or eol");
value = internal_strndup(buf_ + value_start, pos_ - value_start);
}
bool res = run_handler(name, value);
if (!res) {
Printf("Flag parsing failed.");
Die();
}
InternalFree((void *)name);
InternalFree((void *)value);
}
void FlagParser::ParseString(const char *s) {
if (!s) return;
buf_ = s;
pos_ = 0;
while (true) {
skip_whitespace();
if (buf_[pos_] == 0) break;
parse_flag();
}
// Do a sanity check for certain flags.
if (common_flags_dont_use.malloc_context_size < 1)
common_flags_dont_use.malloc_context_size = 1;
}
bool FlagParser::run_handler(const char *name, const char *value) {
for (int i = 0; i < n_flags_; ++i) {
if (internal_strcmp(name, flags_[i].name) == 0)
return flags_[i].handler->Parse(value);
}
Printf("ERROR: Unknown flag: '%s'\n", name);
return false;
}
void FlagParser::RegisterHandler(const char *name, FlagHandlerBase *handler,
const char *desc) {
CHECK(n_flags_ < kMaxFlags);
flags_[n_flags_].name = name;
flags_[n_flags_].desc = desc;
flags_[n_flags_].handler = handler;
++n_flags_;
}
FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) {
flags_ = (Flag *)InternalAlloc(sizeof(Flag) * kMaxFlags);
}
FlagParser::~FlagParser() {
for (int i = 0; i < n_flags_; ++i)
InternalFree(flags_[i].handler);
InternalFree(flags_);
}
} // namespace __sanitizer

View File

@ -0,0 +1,117 @@
//===-- sanitizer_flag_parser.h ---------------------------------*- C++ -*-===//
//
// 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.
//
//===----------------------------------------------------------------------===//
#ifndef SANITIZER_FLAG_REGISTRY_H
#define SANITIZER_FLAG_REGISTRY_H
#include "sanitizer_internal_defs.h"
#include "sanitizer_libc.h"
#include "sanitizer_common.h"
#include "sanitizer_allocator_internal.h"
namespace __sanitizer {
class FlagHandlerBase {
public:
virtual bool Parse(const char *value) { return false; }
};
template <typename T>
class FlagHandler : public FlagHandlerBase {
T *t_;
public:
explicit FlagHandler(T *t) : t_(t) {}
bool Parse(const char *value);
};
template <>
inline bool FlagHandler<bool>::Parse(const char *value) {
if (internal_strcmp(value, "0") == 0 ||
internal_strcmp(value, "no") == 0 ||
internal_strcmp(value, "false") == 0) {
*t_ = false;
return true;
}
if (internal_strcmp(value, "1") == 0 ||
internal_strcmp(value, "yes") == 0 ||
internal_strcmp(value, "true") == 0) {
*t_ = true;
return true;
}
Printf("ERROR: Invalid value for bool option: '%s'\n", value);
return false;
}
template <>
inline bool FlagHandler<const char *>::Parse(const char *value) {
*t_ = internal_strdup(value);
return true;
}
template <>
inline bool FlagHandler<int>::Parse(const char *value) {
char *value_end;
*t_ = internal_simple_strtoll(value, &value_end, 10);
bool ok = *value_end == 0;
if (!ok) Printf("ERROR: Invalid value for int option: '%s'\n", value);
return ok;
}
template <>
inline bool FlagHandler<uptr>::Parse(const char *value) {
char *value_end;
*t_ = internal_simple_strtoll(value, &value_end, 10);
bool ok = *value_end == 0;
if (!ok) Printf("ERROR: Invalid value for uptr option: '%s'\n", value);
return ok;
}
class FlagParser {
static const int kMaxFlags = 200;
struct Flag {
const char *name;
const char *desc;
FlagHandlerBase *handler;
} *flags_;
int n_flags_;
const char *buf_;
uptr pos_;
public:
FlagParser();
~FlagParser();
void RegisterHandler(const char *name, FlagHandlerBase *handler,
const char *desc);
void ParseString(const char *s);
void PrintFlagDescriptions();
private:
void fatal_error(const char *err);
bool is_space(char c);
void skip_whitespace();
void parse_flag();
bool run_handler(const char *name, const char *value);
};
template <typename T>
static void RegisterFlag(FlagParser *parser, const char *name, const char *desc,
T *var) {
FlagHandler<T> *fh = new (INTERNAL_ALLOC) FlagHandler<T>(var); // NOLINT
parser->RegisterHandler(name, fh, desc);
}
} // namespace __sanitizer
#endif // SANITIZER_FLAG_REGISTRY_H

View File

@ -16,6 +16,7 @@
#include "sanitizer_common.h"
#include "sanitizer_libc.h"
#include "sanitizer_list.h"
#include "sanitizer_flag_parser.h"
namespace __sanitizer {
@ -40,150 +41,15 @@ void CommonFlags::SetDefaults() {
#undef COMMON_FLAG
}
void CommonFlags::ParseFromString(const char *str) {
#define COMMON_FLAG(Type, Name, DefaultValue, Description) \
ParseFlag(str, &Name, #Name, Description);
#include "sanitizer_flags.inc"
#undef COMMON_FLAG
// Do a sanity check for certain flags.
if (malloc_context_size < 1)
malloc_context_size = 1;
}
void CommonFlags::CopyFrom(const CommonFlags &other) {
internal_memcpy(this, &other, sizeof(*this));
}
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, " :\r\n\t");
}
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;
void RegisterCommonFlags(FlagParser *parser, CommonFlags *cf) {
#define COMMON_FLAG(Type, Name, DefaultValue, Description) \
RegisterFlag(parser, #Name, Description, &cf->Name);
#include "sanitizer_flags.inc"
#undef COMMON_FLAG
}
} // namespace __sanitizer

View File

@ -18,22 +18,12 @@
namespace __sanitizer {
void ParseFlag(const char *env, bool *flag,
const char *name, const char *descr);
void ParseFlag(const char *env, int *flag,
const char *name, const char *descr);
void ParseFlag(const char *env, uptr *flag,
const char *name, const char *descr);
void ParseFlag(const char *env, const char **flag,
const char *name, const char *descr);
struct CommonFlags {
#define COMMON_FLAG(Type, Name, DefaultValue, Description) Type Name;
#include "sanitizer_flags.inc"
#undef COMMON_FLAG
void SetDefaults();
void ParseFromString(const char *str);
void CopyFrom(const CommonFlags &other);
};
@ -47,10 +37,6 @@ inline void SetCommonFlagsDefaults() {
common_flags_dont_use.SetDefaults();
}
inline void ParseCommonFlagsFromString(const char *str) {
common_flags_dont_use.ParseFromString(str);
}
// This function can only be used to setup tool-specific overrides for
// CommonFlags defaults. Generally, it should only be used right after
// SetCommonFlagsDefaults(), but before ParseCommonFlagsFromString(), and
@ -60,8 +46,9 @@ inline void OverrideCommonFlags(const CommonFlags &cf) {
common_flags_dont_use.CopyFrom(cf);
}
void PrintFlagDescriptions();
class FlagParser;
void RegisterCommonFlags(FlagParser *parser,
CommonFlags *cf = &common_flags_dont_use);
} // namespace __sanitizer
#endif // SANITIZER_FLAGS_H

View File

@ -101,6 +101,14 @@ char* internal_strdup(const char *s) {
return s2;
}
char* internal_strndup(const char *s, uptr n) {
uptr len = internal_strnlen(s, n);
char *s2 = (char*)InternalAlloc(len + 1);
internal_memcpy(s2, s, len);
s2[len] = 0;
return s2;
}
int internal_strcmp(const char *s1, const char *s2) {
while (true) {
unsigned c1 = *s1;

View File

@ -38,6 +38,7 @@ char *internal_strchrnul(const char *s, int c);
int internal_strcmp(const char *s1, const char *s2);
uptr internal_strcspn(const char *s, const char *reject);
char *internal_strdup(const char *s);
char *internal_strndup(const char *s, uptr n);
uptr internal_strlen(const char *s);
char *internal_strncat(char *dst, const char *src, uptr n);
int internal_strncmp(const char *s1, const char *s2, uptr n);

View File

@ -12,7 +12,9 @@
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_flag_parser.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_allocator_internal.h"
#include "gtest/gtest.h"
#include <string.h>
@ -20,58 +22,74 @@
namespace __sanitizer {
static const char kFlagName[] = "flag_name";
static const char kFlagDesc[] = "flag description";
template <typename T>
static void TestFlag(T start_value, const char *env, T final_value) {
T flag = start_value;
ParseFlag(env, &flag, kFlagName, "flag description");
FlagParser parser;
RegisterFlag(&parser, kFlagName, kFlagDesc, &flag);
parser.ParseString(env);
EXPECT_EQ(final_value, flag);
}
static void TestStrFlag(const char *start_value, const char *env,
const char *final_value) {
template <>
void TestFlag(const char *start_value, const char *env,
const char *final_value) {
const char *flag = start_value;
ParseFlag(env, &flag, kFlagName, "flag description");
FlagParser parser;
RegisterFlag(&parser, kFlagName, kFlagDesc, &flag);
parser.ParseString(env);
EXPECT_EQ(0, internal_strcmp(final_value, flag));
}
TEST(SanitizerCommon, BooleanFlags) {
TestFlag(true, "--flag_name", true);
TestFlag(false, "flag_name", false);
TestFlag(false, "--flag_name=1", true);
TestFlag(true, "asdas flag_name=0 asdas", false);
TestFlag(true, " --flag_name=0 ", false);
TestFlag(false, "flag_name=1", true);
TestFlag(false, "flag_name=yes", true);
TestFlag(false, "flag_name=true", true);
TestFlag(true, "flag_name=0", false);
TestFlag(true, "flag_name=no", false);
TestFlag(true, "flag_name=false", false);
}
TEST(SanitizerCommon, IntFlags) {
TestFlag(-11, 0, -11);
TestFlag(-11, "flag_name", -11);
TestFlag(-11, "--flag_name=", 0);
TestFlag(-11, "--flag_name=42", 42);
TestFlag(-11, "--flag_name=-42", -42);
TestFlag(-11, "flag_name=0", 0);
TestFlag(-11, "flag_name=42", 42);
TestFlag(-11, "flag_name=-42", -42);
EXPECT_DEATH(TestFlag(-11, "flag_name", 0), "expected '='");
EXPECT_DEATH(TestFlag(-11, "--flag_name=42", 0),
"Unknown flag: '--flag_name'");
}
TEST(SanitizerCommon, StrFlags) {
TestStrFlag("zzz", 0, "zzz");
TestStrFlag("zzz", "flag_name", "zzz");
TestStrFlag("zzz", "--flag_name=", "");
TestStrFlag("", "--flag_name=abc", "abc");
TestStrFlag("", "--flag_name='abc zxc'", "abc zxc");
TestStrFlag("", "--flag_name='abc zxcc'", "abc zxcc");
TestStrFlag("", "--flag_name=\"abc qwe\" asd", "abc qwe");
TestStrFlag("", "other_flag_name=zzz", "");
TestFlag("zzz", 0, "zzz");
TestFlag("zzz", "flag_name=", "");
TestFlag("zzz", "flag_name=abc", "abc");
TestFlag("", "flag_name=abc", "abc");
TestFlag("", "flag_name='abc zxc'", "abc zxc");
// TestStrFlag("", "flag_name=\"abc qwe\" asd", "abc qwe");
}
static void TestTwoFlags(const char *env, bool expected_flag1,
const char *expected_flag2) {
const char *expected_flag2,
const char *name1 = "flag1",
const char *name2 = "flag2") {
bool flag1 = !expected_flag1;
const char *flag2 = "";
ParseFlag(env, &flag1, "flag1", "flag1 description");
ParseFlag(env, &flag2, "flag2", "flag2 description");
FlagParser parser;
RegisterFlag(&parser, name1, kFlagDesc, &flag1);
RegisterFlag(&parser, name2, kFlagDesc, &flag2);
parser.ParseString(env);
EXPECT_EQ(expected_flag1, flag1);
EXPECT_EQ(0, internal_strcmp(flag2, expected_flag2));
}
@ -86,8 +104,20 @@ TEST(SanitizerCommon, MultipleFlags) {
TestTwoFlags("flag2=qxx\tflag1=yes", true, "qxx");
}
TEST(SanitizerCommon, CommonSuffixFlags) {
TestTwoFlags("flag=1 other_flag='zzz'", true, "zzz", "flag", "other_flag");
TestTwoFlags("other_flag='zzz' flag=1", true, "zzz", "flag", "other_flag");
TestTwoFlags("other_flag=' flag=0 ' flag=1", true, " flag=0 ", "flag",
"other_flag");
TestTwoFlags("flag=1 other_flag=' flag=0 '", true, " flag=0 ", "flag",
"other_flag");
}
TEST(SanitizerCommon, CommonFlags) {
CommonFlags cf;
FlagParser parser;
RegisterCommonFlags(&parser, &cf);
cf.SetDefaults();
EXPECT_TRUE(cf.symbolize);
EXPECT_STREQ(".", cf.coverage_dir);
@ -97,7 +127,7 @@ TEST(SanitizerCommon, CommonFlags) {
cf.coverage_direct = true;
cf.log_path = "path/one";
cf.ParseFromString("symbolize=1:coverage_direct=false log_path='path/two'");
parser.ParseString("symbolize=1:coverage_direct=false log_path='path/two'");
EXPECT_TRUE(cf.symbolize);
EXPECT_TRUE(cf.coverage);
EXPECT_FALSE(cf.coverage_direct);

View File

@ -11,6 +11,7 @@
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_flag_parser.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
@ -80,8 +81,10 @@ void InitializeFlags(Flags *f, const char *env) {
}
// Override from command line.
ParseFlag(env, &f->second_deadlock_stack, "second_deadlock_stack", "");
ParseCommonFlagsFromString(env);
FlagParser parser;
RegisterFlag(&parser, "second_deadlock_stack", "", &f->second_deadlock_stack);
RegisterCommonFlags(&parser);
parser.ParseString(env);
}
void Initialize() {

View File

@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_flag_parser.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "tsan_flags.h"
#include "tsan_rtl.h"
@ -41,16 +42,18 @@ void Flags::SetDefaults() {
second_deadlock_stack = false;
}
void Flags::ParseFromString(const char *str) {
#define TSAN_FLAG(Type, Name, DefaultValue, Description) \
ParseFlag(str, &Name, #Name, Description);
void RegisterTsanFlags(FlagParser *parser, Flags *f) {
#define TSAN_FLAG(Type, Name, DefaultValue, Description) \
RegisterFlag(parser, #Name, Description, &f->Name);
#include "tsan_flags.inc"
#undef TSAN_FLAG
// DDFlags
ParseFlag(str, &second_deadlock_stack, "second_deadlock_stack", "");
}
void InitializeFlags(Flags *f, const char *env) {
FlagParser parser;
RegisterTsanFlags(&parser, f);
RegisterCommonFlags(&parser);
f->SetDefaults();
SetCommonFlagsDefaults();
@ -66,11 +69,9 @@ void InitializeFlags(Flags *f, const char *env) {
}
// Let a frontend override.
f->ParseFromString(__tsan_default_options());
ParseCommonFlagsFromString(__tsan_default_options());
parser.ParseString(__tsan_default_options());
// Override from command line.
f->ParseFromString(env);
ParseCommonFlagsFromString(env);
parser.ParseString(env);
// Sanity check.
if (!f->report_bugs) {
@ -80,7 +81,7 @@ void InitializeFlags(Flags *f, const char *env) {
}
if (common_flags()->help)
PrintFlagDescriptions();
parser.PrintFlagDescriptions();
if (f->history_size < 0 || f->history_size > 7) {
Printf("ThreadSanitizer: incorrect value for history_size"

View File

@ -14,6 +14,7 @@
#include "ubsan_flags.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_flag_parser.h"
namespace __ubsan {
@ -21,18 +22,6 @@ static const char *MaybeCallUbsanDefaultOptions() {
return (&__ubsan_default_options) ? __ubsan_default_options() : "";
}
void InitializeCommonFlags() {
SetCommonFlagsDefaults();
CommonFlags cf;
cf.CopyFrom(*common_flags());
cf.print_summary = false;
OverrideCommonFlags(cf);
// Override from user-specified string.
ParseCommonFlagsFromString(MaybeCallUbsanDefaultOptions());
// Override from environment variable.
ParseCommonFlagsFromString(GetEnv("UBSAN_OPTIONS"));
}
Flags ubsan_flags;
void Flags::SetDefaults() {
@ -41,20 +30,42 @@ void Flags::SetDefaults() {
#undef UBSAN_FLAG
}
void Flags::ParseFromString(const char *str) {
#define UBSAN_FLAG(Type, Name, DefaultValue, Description) \
ParseFlag(str, &Name, #Name, Description);
void RegisterUbsanFlags(FlagParser *parser, Flags *f) {
#define UBSAN_FLAG(Type, Name, DefaultValue, Description) \
RegisterFlag(parser, #Name, Description, &f->Name);
#include "ubsan_flags.inc"
#undef UBSAN_FLAG
}
void InitializeFlags() {
void InitializeFlags(bool standalone) {
Flags *f = flags();
FlagParser parser;
RegisterUbsanFlags(&parser, f);
if (standalone) {
RegisterCommonFlags(&parser);
SetCommonFlagsDefaults();
CommonFlags cf;
cf.CopyFrom(*common_flags());
cf.print_summary = false;
OverrideCommonFlags(cf);
} else {
// Ignore common flags if not standalone.
// This is inconsistent with LSan, which allows common flags in LSAN_FLAGS.
// This is caused by undefined initialization order between ASan and UBsan,
// which makes it impossible to make sure that common flags from ASAN_OPTIONS
// have not been used (in __asan_init) before they are overwritten with flags
// from UBSAN_OPTIONS.
CommonFlags cf_ignored;
RegisterCommonFlags(&parser, &cf_ignored);
}
f->SetDefaults();
// Override from user-specified string.
f->ParseFromString(MaybeCallUbsanDefaultOptions());
parser.ParseString(MaybeCallUbsanDefaultOptions());
// Override from environment variable.
f->ParseFromString(GetEnv("UBSAN_OPTIONS"));
parser.ParseString(GetEnv("UBSAN_OPTIONS"));
}
} // namespace __ubsan

View File

@ -23,14 +23,12 @@ struct Flags {
#undef UBSAN_FLAG
void SetDefaults();
void ParseFromString(const char *str);
};
extern Flags ubsan_flags;
inline Flags *flags() { return &ubsan_flags; }
void InitializeCommonFlags();
void InitializeFlags();
void InitializeFlags(bool standalone);
} // namespace __ubsan

View File

@ -31,6 +31,7 @@ void __ubsan::InitIfNecessary() {
#endif
if (LIKELY(ubsan_inited))
return;
bool standalone = false;
if (0 == internal_strcmp(SanitizerToolName, "SanitizerTool")) {
// WARNING: If this condition holds, then either UBSan runs in a standalone
// mode, or initializer for another sanitizer hasn't run yet. In a latter
@ -38,10 +39,10 @@ void __ubsan::InitIfNecessary() {
// common flags. It means, that we are not allowed to *use* common flags
// in this function.
SanitizerToolName = "UndefinedBehaviorSanitizer";
InitializeCommonFlags();
standalone = true;
}
// Initialize UBSan-specific flags.
InitializeFlags();
InitializeFlags(standalone);
SuppressionContext::InitIfNecessary();
InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
ubsan_inited = true;

View File

@ -0,0 +1,9 @@
// RUN: %clangxx_asan -O0 %s -o %t
// RUN: ASAN_OPTIONS=help=1 %run %t 2>&1 | FileCheck %s
int main() {
}
// CHECK: Available flags for AddressSanitizer:
// CHECK-DAG: handle_segv
// CHECK-DAG: check_initialization_order

View File

@ -0,0 +1,7 @@
// RUN: %clangxx_asan -O0 %s -o %t
// RUN: ASAN_OPTIONS=invalid_option_name=10 not %run %t 2>&1 | FileCheck %s
int main() {
}
// CHECK: Unknown flag{{.*}}invalid_option_name

View File

@ -4,12 +4,12 @@
// __asan_default_options() are not supported on Windows.
// XFAIL: win32
const char *kAsanDefaultOptions="verbosity=1 foo=bar";
const char *kAsanDefaultOptions="verbosity=1 strip_path_prefix=bar";
extern "C"
__attribute__((no_sanitize_address))
const char *__asan_default_options() {
// CHECK: Using the defaults from __asan_default_options: {{.*}} foo=bar
// CHECK: Using the defaults from __asan_default_options: {{.*}} strip_path_prefix=bar
return kAsanDefaultOptions;
}

View File

@ -1,5 +1,5 @@
// Test for __lsan_ignore_object().
// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0:verbosity=2"
// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0"
// RUN: %clangxx_lsan %s -o %t
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
@ -20,5 +20,4 @@ int main() {
return 0;
}
// CHECK: Test alloc: [[ADDR:.*]].
// CHECK: ignoring heap object at [[ADDR]]
// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s)

View File

@ -1,5 +1,4 @@
// Test for incorrect use of __lsan_ignore_object().
// RUN: LSAN_BASE="verbosity=2"
// RUN: %clangxx_lsan %s -o %t
// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t 2>&1 | FileCheck %s
@ -18,5 +17,4 @@ int main() {
return 0;
}
// CHECK: Test alloc: [[ADDR:.*]].
// CHECK: heap object at [[ADDR]] is already being ignored
// CHECK: no heap object found at [[ADDR]]
// CHECK-NOT: SUMMARY: {{.*}} leaked

View File

@ -12,16 +12,16 @@
// RUN: %run %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-MEMBER --strict-whitespace
// RUN: (echo "vptr_check:S"; echo "vptr_check:T"; echo "vptr_check:U") > %t.supp
// RUN: ASAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t mS 2>&1
// RUN: ASAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t fS 2>&1
// RUN: ASAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t cS 2>&1
// RUN: ASAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t mV 2>&1
// RUN: ASAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t fV 2>&1
// RUN: ASAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t cV 2>&1
// RUN: ASAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t oU 2>&1
// RUN: ASAN_OPTIONS="suppressions='%t.supp'" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t mS 2>&1
// RUN: ASAN_OPTIONS="suppressions='%t.supp'" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t fS 2>&1
// RUN: ASAN_OPTIONS="suppressions='%t.supp'" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t cS 2>&1
// RUN: ASAN_OPTIONS="suppressions='%t.supp'" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t mV 2>&1
// RUN: ASAN_OPTIONS="suppressions='%t.supp'" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t fV 2>&1
// RUN: ASAN_OPTIONS="suppressions='%t.supp'" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t cV 2>&1
// RUN: ASAN_OPTIONS="suppressions='%t.supp'" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t oU 2>&1
// RUN: echo "vptr_check:S" > %t.loc-supp
// RUN: ASAN_OPTIONS="suppressions='%t.loc-supp':halt_on_error=1" UBSAN_OPTIONS="suppressions='%t.loc-supp':halt_on_error=1" not %run %t x- 2>&1 | FileCheck %s --check-prefix=CHECK-LOC-SUPPRESS
// RUN: ASAN_OPTIONS="suppressions='%t.loc-supp'" UBSAN_OPTIONS="suppressions='%t.loc-supp':halt_on_error=1" not %run %t x- 2>&1 | FileCheck %s --check-prefix=CHECK-LOC-SUPPRESS
// FIXME: This test produces linker errors on Darwin.
// XFAIL: darwin