From 53f7e129091643944d484017105fa906613e42c0 Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Wed, 28 Nov 2012 17:34:24 +0000 Subject: [PATCH] Refactor -fsanitize, -f*-sanitizer arguments parsing. Provide a more careful diagnostic for invalid sets of sanitizers llvm-svn: 168794 --- clang/lib/Driver/SanitizerArgs.h | 64 +++++++++++++++++++++++++++++++- clang/lib/Driver/Tools.cpp | 48 ++---------------------- clang/test/Driver/fsanitize.c | 6 +++ 3 files changed, 72 insertions(+), 46 deletions(-) diff --git a/clang/lib/Driver/SanitizerArgs.h b/clang/lib/Driver/SanitizerArgs.h index ffc149374d9b..52a6c2ec4b13 100644 --- a/clang/lib/Driver/SanitizerArgs.h +++ b/clang/lib/Driver/SanitizerArgs.h @@ -71,18 +71,78 @@ class SanitizerArgs { /// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any /// invalid components. - static unsigned parse(const Driver &D, const Arg *A) { + static unsigned parse(const Driver &D, const Arg *A, bool DiagnoseErrors) { unsigned Kind = 0; for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) { if (unsigned K = parse(A->getValue(I))) Kind |= K; - else + else if (DiagnoseErrors) D.Diag(diag::err_drv_unsupported_option_argument) << A->getOption().getName() << A->getValue(I); } return Kind; } + /// Parse a single flag of the form -f[no]sanitize=, or + /// -f*-sanitizer. Sets the masks defining required change of Kind value. + /// Returns true if the flag was parsed successfully. + static bool parse(const Driver &D, const ArgList &Args, const Arg *A, + unsigned &Add, unsigned &Remove, bool DiagnoseErrors) { + Add = 0; + Remove = 0; + const char *DeprecatedReplacement = 0; + if (A->getOption().matches(options::OPT_faddress_sanitizer)) { + Add = Address; + DeprecatedReplacement = "-fsanitize=address"; + } else if (A->getOption().matches(options::OPT_fno_address_sanitizer)) { + Remove = Address; + DeprecatedReplacement = "-fno-sanitize=address"; + } else if (A->getOption().matches(options::OPT_fthread_sanitizer)) { + Add = Thread; + DeprecatedReplacement = "-fsanitize=thread"; + } else if (A->getOption().matches(options::OPT_fno_thread_sanitizer)) { + Remove = Thread; + DeprecatedReplacement = "-fno-sanitize=thread"; + } else if (A->getOption().matches(options::OPT_fcatch_undefined_behavior)) { + Add = Undefined; + DeprecatedReplacement = "-fsanitize=undefined"; + } else if (A->getOption().matches(options::OPT_fbounds_checking) || + A->getOption().matches(options::OPT_fbounds_checking_EQ)) { + Add = Bounds; + DeprecatedReplacement = "-fsanitize=bounds"; + } else if (A->getOption().matches(options::OPT_fsanitize_EQ)) { + Add = parse(D, A, DiagnoseErrors); + } else if (A->getOption().matches(options::OPT_fno_sanitize_EQ)) { + Remove = parse(D, A, DiagnoseErrors); + } else { + // Flag is not relevant to sanitizers. + return false; + } + // If this is a deprecated synonym, produce a warning directing users + // towards the new spelling. + if (DeprecatedReplacement && DiagnoseErrors) + D.Diag(diag::warn_drv_deprecated_arg) + << A->getAsString(Args) << DeprecatedReplacement; + return true; + } + + /// Produce an argument string from ArgList \p Args, which shows how it + /// provides a sanitizer kind in \p Mask. For example, the argument list + /// "-fsanitize=thread,vptr -faddress-sanitizer" with mask \c NeedsUbsanRt + /// would produce "-fsanitize=vptr". + static std::string lastArgumentForKind(const Driver &D, const ArgList &Args, + unsigned Kind) { + for (ArgList::const_reverse_iterator I = Args.rbegin(), E = Args.rend(); + I != E; ++I) { + unsigned Add, Remove; + if (parse(D, Args, *I, Add, Remove, false) && + (Add & Kind)) + return describeSanitizeArg(Args, *I, Kind); + Kind &= ~Remove; + } + llvm_unreachable("arg list didn't provide expected value"); + } + /// Produce an argument string from argument \p A, which shows how it provides /// a value in \p Mask. For instance, the argument /// "-fsanitize=address,alignment" with mask \c NeedsUbsanRt would produce diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index eb79c2fb88ba..34fbfcf14eee 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -1456,51 +1456,13 @@ static bool UseRelaxAll(Compilation &C, const ArgList &Args) { SanitizerArgs::SanitizerArgs(const Driver &D, const ArgList &Args) { Kind = 0; - const Arg *AsanArg, *TsanArg, *UbsanArg; for (ArgList::const_iterator I = Args.begin(), E = Args.end(); I != E; ++I) { - unsigned Add = 0, Remove = 0; - const char *DeprecatedReplacement = 0; - if ((*I)->getOption().matches(options::OPT_faddress_sanitizer)) { - Add = Address; - DeprecatedReplacement = "-fsanitize=address"; - } else if ((*I)->getOption().matches(options::OPT_fno_address_sanitizer)) { - Remove = Address; - DeprecatedReplacement = "-fno-sanitize=address"; - } else if ((*I)->getOption().matches(options::OPT_fthread_sanitizer)) { - Add = Thread; - DeprecatedReplacement = "-fsanitize=thread"; - } else if ((*I)->getOption().matches(options::OPT_fno_thread_sanitizer)) { - Remove = Thread; - DeprecatedReplacement = "-fno-sanitize=thread"; - } else if ((*I)->getOption().matches(options::OPT_fcatch_undefined_behavior)) { - Add = Undefined; - DeprecatedReplacement = "-fsanitize=undefined"; - } else if ((*I)->getOption().matches(options::OPT_fsanitize_EQ)) { - Add = parse(D, *I); - } else if ((*I)->getOption().matches(options::OPT_fno_sanitize_EQ)) { - Remove = parse(D, *I); - } else if ((*I)->getOption().matches(options::OPT_fbounds_checking) || - (*I)->getOption().matches(options::OPT_fbounds_checking_EQ)) { - Add = Bounds; - DeprecatedReplacement = "-fsanitize=bounds"; - } else { + unsigned Add, Remove; + if (!parse(D, Args, *I, Add, Remove, true)) continue; - } - (*I)->claim(); - Kind |= Add; Kind &= ~Remove; - - if (Add & NeedsAsanRt) AsanArg = *I; - if (Add & NeedsTsanRt) TsanArg = *I; - if (Add & NeedsUbsanRt) UbsanArg = *I; - - // If this is a deprecated synonym, produce a warning directing users - // towards the new spelling. - if (DeprecatedReplacement) - D.Diag(diag::warn_drv_deprecated_arg) - << (*I)->getAsString(Args) << DeprecatedReplacement; } // Only one runtime library can be used at once. @@ -1510,10 +1472,8 @@ SanitizerArgs::SanitizerArgs(const Driver &D, const ArgList &Args) { bool NeedsUbsan = needsUbsanRt(); if (NeedsAsan + NeedsTsan + NeedsUbsan > 1) D.Diag(diag::err_drv_argument_not_allowed_with) - << describeSanitizeArg(Args, NeedsAsan ? AsanArg : TsanArg, - NeedsAsan ? NeedsAsanRt : NeedsTsanRt) - << describeSanitizeArg(Args, NeedsUbsan ? UbsanArg : TsanArg, - NeedsUbsan ? NeedsUbsanRt : NeedsTsanRt); + << lastArgumentForKind(D, Args, NeedsAsan ? NeedsAsanRt : NeedsTsanRt) + << lastArgumentForKind(D, Args, NeedsUbsan ? NeedsUbsanRt : NeedsTsanRt); } /// If AddressSanitizer is enabled, add appropriate linker flags (Linux). diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c index 8b1586489d99..a7c947716a8b 100644 --- a/clang/test/Driver/fsanitize.c +++ b/clang/test/Driver/fsanitize.c @@ -20,6 +20,12 @@ // RUN: %clang -target x86_64-linux-gnu -faddress-sanitizer -fthread-sanitizer -fno-rtti %s -c -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-TSAN // CHECK-ASAN-TSAN: '-faddress-sanitizer' not allowed with '-fthread-sanitizer' +// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize=alignment -fsanitize=vptr -fno-sanitize=vptr %s -c -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-ASAN +// CHECK-UBSAN-ASAN: '-fsanitize=address' not allowed with '-fsanitize=alignment' + +// RUN: %clang -target x86_64-linux-gnu -fsanitize=vptr -fno-sanitize=vptr -fsanitize=undefined,address %s -c -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-VPTR-UBSAN-ASAN +// CHECK-VPTR-UBSAN-ASAN: '-fsanitize=address' not allowed with '-fsanitize=undefined' + // RUN: %clang -target x86_64-linux-gnu -fcatch-undefined-behavior -fthread-sanitizer -fno-thread-sanitizer -faddress-sanitizer -fno-address-sanitizer -fbounds-checking -c -o /dev/null %s 2>&1 | FileCheck %s --check-prefix=CHECK-DEPRECATED // CHECK-DEPRECATED: argument '-fcatch-undefined-behavior' is deprecated, use '-fsanitize=undefined' instead // CHECK-DEPRECATED: argument '-fthread-sanitizer' is deprecated, use '-fsanitize=thread' instead