[clang] 17f7424 - [analyzer][NFC] Refactor GenericTaintChecker to use CallDescriptionMap

Endre Fülöp via cfe-commits cfe-commits at lists.llvm.org
Tue Jan 18 07:04:11 PST 2022


Author: Endre Fülöp
Date: 2022-01-18T16:04:04+01:00
New Revision: 17f74240e6c3bb64fe741f12d67903e6b7fc38cb

URL: https://github.com/llvm/llvm-project/commit/17f74240e6c3bb64fe741f12d67903e6b7fc38cb
DIFF: https://github.com/llvm/llvm-project/commit/17f74240e6c3bb64fe741f12d67903e6b7fc38cb.diff

LOG: [analyzer][NFC] Refactor GenericTaintChecker to use CallDescriptionMap

GenericTaintChecker now uses CallDescriptionMap to describe the possible
operation in code which trigger the introduction (sources), the removal
(filters), the passing along (propagations) and detection (sinks) of
tainted values.

Reviewed By: steakhal, NoQ

Differential Revision: https://reviews.llvm.org/D116025

Added: 
    

Modified: 
    clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
    clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
index a383012dc3516..94fa0d9ecdc31 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
@@ -84,6 +84,8 @@ class CheckerContext {
     return Eng.getContext();
   }
 
+  const ASTContext &getASTContext() const { return Eng.getContext(); }
+
   const LangOptions &getLangOpts() const {
     return Eng.getContext().getLangOpts();
   }

diff  --git a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
index 66ef781871ec9..1b61b4982931f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
@@ -22,15 +22,14 @@
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
 #include "llvm/Support/YAMLTraits.h"
 
-#include <algorithm>
 #include <limits>
 #include <memory>
-#include <unordered_map>
 #include <utility>
 
 using namespace clang;
@@ -38,577 +37,651 @@ using namespace ento;
 using namespace taint;
 
 namespace {
-class GenericTaintChecker : public Checker<check::PreCall, check::PostCall> {
-public:
-  static void *getTag() {
-    static int Tag;
-    return &Tag;
+
+class GenericTaintChecker;
+
+/// Check for CWE-134: Uncontrolled Format String.
+constexpr llvm::StringLiteral MsgUncontrolledFormatString =
+    "Untrusted data is used as a format string "
+    "(CWE-134: Uncontrolled Format String)";
+
+/// Check for:
+/// CERT/STR02-C. "Sanitize data passed to complex subsystems"
+/// CWE-78, "Failure to Sanitize Data into an OS Command"
+constexpr llvm::StringLiteral MsgSanitizeSystemArgs =
+    "Untrusted data is passed to a system call "
+    "(CERT/STR02-C. Sanitize data passed to complex subsystems)";
+
+/// Check if tainted data is used as a buffer size in strn.. functions,
+/// and allocators.
+constexpr llvm::StringLiteral MsgTaintedBufferSize =
+    "Untrusted data is used to specify the buffer size "
+    "(CERT/STR31-C. Guarantee that storage for strings has sufficient space "
+    "for character data and the null terminator)";
+
+/// Check if tainted data is used as a custom sink's parameter.
+constexpr llvm::StringLiteral MsgCustomSink =
+    "Untrusted data is passed to a user-defined sink";
+
+using ArgIdxTy = int;
+using ArgVecTy = llvm::SmallVector<ArgIdxTy, 2>;
+
+/// Denotes the return value.
+constexpr ArgIdxTy ReturnValueIndex{-1};
+
+static ArgIdxTy fromArgumentCount(unsigned Count) {
+  assert(Count <=
+             static_cast<std::size_t>(std::numeric_limits<ArgIdxTy>::max()) &&
+         "ArgIdxTy is not large enough to represent the number of arguments.");
+  return Count;
+}
+
+/// Check if the region the expression evaluates to is the standard input,
+/// and thus, is tainted.
+/// FIXME: Move this to Taint.cpp.
+bool isStdin(SVal Val, const ASTContext &ACtx) {
+  // FIXME: What if Val is NonParamVarRegion?
+
+  // The region should be symbolic, we do not know it's value.
+  const auto *SymReg = dyn_cast_or_null<SymbolicRegion>(Val.getAsRegion());
+  if (!SymReg)
+    return false;
+
+  // Get it's symbol and find the declaration region it's pointing to.
+  const auto *Sm = dyn_cast<SymbolRegionValue>(SymReg->getSymbol());
+  if (!Sm)
+    return false;
+  const auto *DeclReg = dyn_cast<DeclRegion>(Sm->getRegion());
+  if (!DeclReg)
+    return false;
+
+  // This region corresponds to a declaration, find out if it's a global/extern
+  // variable named stdin with the proper type.
+  if (const auto *D = dyn_cast_or_null<VarDecl>(DeclReg->getDecl())) {
+    D = D->getCanonicalDecl();
+    // FIXME: This should look for an exact match.
+    if (D->getName().contains("stdin") && D->isExternC()) {
+      const QualType FILETy = ACtx.getFILEType().getCanonicalType();
+      const QualType Ty = D->getType().getCanonicalType();
+
+      if (Ty->isPointerType())
+        return Ty->getPointeeType() == FILETy;
+    }
   }
+  return false;
+}
 
-  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
-  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+SVal getPointeeOf(const CheckerContext &C, Loc LValue) {
+  const QualType ArgTy = LValue.getType(C.getASTContext());
+  if (!ArgTy->isPointerType() || !ArgTy->getPointeeType()->isVoidType())
+    return C.getState()->getSVal(LValue);
 
-  void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
-                  const char *Sep) const override;
+  // Do not dereference void pointers. Treat them as byte pointers instead.
+  // FIXME: we might want to consider more than just the first byte.
+  return C.getState()->getSVal(LValue, C.getASTContext().CharTy);
+}
+
+/// Given a pointer/reference argument, return the value it refers to.
+Optional<SVal> getPointeeOf(const CheckerContext &C, SVal Arg) {
+  if (auto LValue = Arg.getAs<Loc>())
+    return getPointeeOf(C, *LValue);
+  return None;
+}
 
-  using ArgVector = SmallVector<unsigned, 2>;
-  using SignedArgVector = SmallVector<int, 2>;
+/// Given a pointer, return the SVal of its pointee or if it is tainted,
+/// otherwise return the pointer's SVal if tainted.
+/// Also considers stdin as a taint source.
+Optional<SVal> getTaintedPointeeOrPointer(const CheckerContext &C, SVal Arg) {
+  const ProgramStateRef State = C.getState();
 
-  enum class VariadicType { None, Src, Dst };
+  if (auto Pointee = getPointeeOf(C, Arg))
+    if (isTainted(State, *Pointee)) // FIXME: isTainted(...) ? Pointee : None;
+      return Pointee;
 
-  /// Used to parse the configuration file.
-  struct TaintConfiguration {
-    using NameScopeArgs = std::tuple<std::string, std::string, ArgVector>;
-
-    struct Propagation {
-      std::string Name;
-      std::string Scope;
-      ArgVector SrcArgs;
-      SignedArgVector DstArgs;
-      VariadicType VarType;
-      unsigned VarIndex;
-    };
-
-    std::vector<Propagation> Propagations;
-    std::vector<NameScopeArgs> Filters;
-    std::vector<NameScopeArgs> Sinks;
-
-    TaintConfiguration() = default;
-    TaintConfiguration(const TaintConfiguration &) = default;
-    TaintConfiguration(TaintConfiguration &&) = default;
-    TaintConfiguration &operator=(const TaintConfiguration &) = default;
-    TaintConfiguration &operator=(TaintConfiguration &&) = default;
-  };
+  if (isTainted(State, Arg))
+    return Arg;
+
+  // FIXME: This should be done by the isTainted() API.
+  if (isStdin(Arg, C.getASTContext()))
+    return Arg;
+
+  return None;
+}
 
-  /// Convert SignedArgVector to ArgVector.
-  ArgVector convertToArgVector(CheckerManager &Mgr, const std::string &Option,
-                               const SignedArgVector &Args);
+bool isTaintedOrPointsToTainted(const Expr *E, const ProgramStateRef &State,
+                                CheckerContext &C) {
+  return getTaintedPointeeOrPointer(C, C.getSVal(E)).hasValue();
+}
 
-  /// Parse the config.
-  void parseConfiguration(CheckerManager &Mgr, const std::string &Option,
-                          TaintConfiguration &&Config);
+/// ArgSet is used to describe arguments relevant for taint detection or
+/// taint application. A discrete set of argument indexes and a variadic
+/// argument list signified by a starting index are supported.
+class ArgSet {
+public:
+  ArgSet() = default;
+  ArgSet(ArgVecTy &&DiscreteArgs, Optional<ArgIdxTy> VariadicIndex = None)
+      : DiscreteArgs(std::move(DiscreteArgs)),
+        VariadicIndex(std::move(VariadicIndex)) {}
 
-  static const unsigned InvalidArgIndex{std::numeric_limits<unsigned>::max()};
-  /// Denotes the return vale.
-  static const unsigned ReturnValueIndex{std::numeric_limits<unsigned>::max() -
-                                         1};
+  bool contains(ArgIdxTy ArgIdx) const {
+    if (llvm::is_contained(DiscreteArgs, ArgIdx))
+      return true;
 
-private:
-  mutable std::unique_ptr<BugType> BT;
-  void initBugType() const {
-    if (!BT)
-      BT = std::make_unique<BugType>(this, "Use of Untrusted Data",
-                                     "Untrusted Data");
+    return VariadicIndex && ArgIdx >= *VariadicIndex;
   }
 
-  struct FunctionData {
-    FunctionData() = delete;
-    FunctionData(const FunctionDecl *FDecl, StringRef Name,
-                 std::string FullName)
-        : FDecl(FDecl), Name(Name), FullName(std::move(FullName)) {}
-    FunctionData(const FunctionData &) = default;
-    FunctionData(FunctionData &&) = default;
-    FunctionData &operator=(const FunctionData &) = delete;
-    FunctionData &operator=(FunctionData &&) = delete;
-
-    static Optional<FunctionData> create(const CallEvent &Call,
-                                         const CheckerContext &C) {
-      if (!Call.getDecl())
-        return None;
-
-      const FunctionDecl *FDecl = Call.getDecl()->getAsFunction();
-      if (!FDecl || (FDecl->getKind() != Decl::Function &&
-                     FDecl->getKind() != Decl::CXXMethod))
-        return None;
-
-      StringRef Name = C.getCalleeName(FDecl);
-      std::string FullName = FDecl->getQualifiedNameAsString();
-      if (Name.empty() || FullName.empty())
-        return None;
-
-      return FunctionData{FDecl, Name, std::move(FullName)};
-    }
+  bool isEmpty() const { return DiscreteArgs.empty() && !VariadicIndex; }
 
-    bool isInScope(StringRef Scope) const {
-      return StringRef(FullName).startswith(Scope);
+  ArgVecTy ArgsUpTo(ArgIdxTy LastArgIdx) const {
+    ArgVecTy Args;
+    for (ArgIdxTy I = ReturnValueIndex; I <= LastArgIdx; ++I) {
+      if (contains(I))
+        Args.push_back(I);
     }
+    return Args;
+  }
 
-    const FunctionDecl *const FDecl;
-    const StringRef Name;
-    const std::string FullName;
-  };
+private:
+  ArgVecTy DiscreteArgs;
+  Optional<ArgIdxTy> VariadicIndex;
+};
 
-  /// Catch taint related bugs. Check if tainted data is passed to a
-  /// system call etc. Returns true on matching.
-  bool checkPre(const CallEvent &Call, const FunctionData &FData,
-                CheckerContext &C) const;
+/// A struct used to specify taint propagation rules for a function.
+///
+/// If any of the possible taint source arguments is tainted, all of the
+/// destination arguments should also be tainted. If ReturnValueIndex is added
+/// to the dst list, the return value will be tainted.
+class GenericTaintRule {
+  /// Arguments which are taints sinks and should be checked, and a report
+  /// should be emitted if taint reaches these.
+  ArgSet SinkArgs;
+  /// Arguments which should be sanitized on function return.
+  ArgSet FilterArgs;
+  /// Arguments which can participate in taint propagationa. If any of the
+  /// arguments in PropSrcArgs is tainted, all arguments in  PropDstArgs should
+  /// be tainted.
+  ArgSet PropSrcArgs;
+  ArgSet PropDstArgs;
+
+  /// A message that explains why the call is sensitive to taint.
+  Optional<StringRef> SinkMsg;
+
+  GenericTaintRule() = default;
+
+  GenericTaintRule(ArgSet &&Sink, ArgSet &&Filter, ArgSet &&Src, ArgSet &&Dst,
+                   Optional<StringRef> SinkMsg = None)
+      : SinkArgs(std::move(Sink)), FilterArgs(std::move(Filter)),
+        PropSrcArgs(std::move(Src)), PropDstArgs(std::move(Dst)),
+        SinkMsg(SinkMsg) {}
 
-  /// Add taint sources on a pre-visit. Returns true on matching.
-  bool addSourcesPre(const CallEvent &Call, const FunctionData &FData,
-                     CheckerContext &C) const;
+public:
+  /// Make a rule that reports a warning if taint reaches any of \p FilterArgs
+  /// arguments.
+  static GenericTaintRule Sink(ArgSet &&SinkArgs,
+                               Optional<StringRef> Msg = None) {
+    return {std::move(SinkArgs), {}, {}, {}, Msg};
+  }
 
-  /// Mark filter's arguments not tainted on a pre-visit. Returns true on
-  /// matching.
-  bool addFiltersPre(const CallEvent &Call, const FunctionData &FData,
-                     CheckerContext &C) const;
+  /// Make a rule that sanitizes all FilterArgs arguments.
+  static GenericTaintRule Filter(ArgSet &&FilterArgs) {
+    return {{}, std::move(FilterArgs), {}, {}};
+  }
 
-  /// Propagate taint generated at pre-visit. Returns true on matching.
-  static bool propagateFromPre(const CallEvent &Call, CheckerContext &C);
+  /// Make a rule that unconditionally taints all Args.
+  /// If Func is provided, it must also return true for taint to propagate.
+  static GenericTaintRule Source(ArgSet &&SourceArgs) {
+    return {{}, {}, {}, std::move(SourceArgs)};
+  }
 
-  /// Check if the region the expression evaluates to is the standard input,
-  /// and thus, is tainted.
-  static bool isStdin(const Expr *E, CheckerContext &C);
+  /// Make a rule that taints all PropDstArgs if any of PropSrcArgs is tainted.
+  static GenericTaintRule Prop(ArgSet &&SrcArgs, ArgSet &&DstArgs) {
+    return {{}, {}, std::move(SrcArgs), std::move(DstArgs)};
+  }
 
-  /// Given a pointer argument, return the value it points to.
-  static Optional<SVal> getPointeeOf(CheckerContext &C, const Expr *Arg);
+  /// Make a rule that taints all PropDstArgs if any of PropSrcArgs is tainted.
+  static GenericTaintRule SinkProp(ArgSet &&SinkArgs, ArgSet &&SrcArgs,
+                                   ArgSet &&DstArgs,
+                                   Optional<StringRef> Msg = None) {
+    return {
+        std::move(SinkArgs), {}, std::move(SrcArgs), std::move(DstArgs), Msg};
+  }
 
-  /// Check for CWE-134: Uncontrolled Format String.
-  static constexpr llvm::StringLiteral MsgUncontrolledFormatString =
-      "Untrusted data is used as a format string "
-      "(CWE-134: Uncontrolled Format String)";
-  bool checkUncontrolledFormatString(const CallEvent &Call,
-                                     CheckerContext &C) const;
+  /// Process a function which could either be a taint source, a taint sink, a
+  /// taint filter or a taint propagator.
+  void process(const GenericTaintChecker &Checker, const CallEvent &Call,
+               CheckerContext &C) const;
 
-  /// Check for:
-  /// CERT/STR02-C. "Sanitize data passed to complex subsystems"
-  /// CWE-78, "Failure to Sanitize Data into an OS Command"
-  static constexpr llvm::StringLiteral MsgSanitizeSystemArgs =
-      "Untrusted data is passed to a system call "
-      "(CERT/STR02-C. Sanitize data passed to complex subsystems)";
-  bool checkSystemCall(const CallEvent &Call, StringRef Name,
-                       CheckerContext &C) const;
-
-  /// Check if tainted data is used as a buffer size ins strn.. functions,
-  /// and allocators.
-  static constexpr llvm::StringLiteral MsgTaintedBufferSize =
-      "Untrusted data is used to specify the buffer size "
-      "(CERT/STR31-C. Guarantee that storage for strings has sufficient space "
-      "for character data and the null terminator)";
-  bool checkTaintedBufferSize(const CallEvent &Call, CheckerContext &C) const;
-
-  /// Check if tainted data is used as a custom sink's parameter.
-  static constexpr llvm::StringLiteral MsgCustomSink =
-      "Untrusted data is passed to a user-defined sink";
-  bool checkCustomSinks(const CallEvent &Call, const FunctionData &FData,
-                        CheckerContext &C) const;
+  /// Handles the resolution of indexes of type ArgIdxTy to Expr*-s.
+  static const Expr *GetArgExpr(ArgIdxTy ArgIdx, const CallEvent &Call) {
+    return ArgIdx == ReturnValueIndex ? Call.getOriginExpr()
+                                      : Call.getArgExpr(ArgIdx);
+  };
 
-  /// Generate a report if the expression is tainted or points to tainted data.
-  bool generateReportIfTainted(const Expr *E, StringRef Msg,
-                               CheckerContext &C) const;
+  /// Functions for custom taintedness propagation.
+  static bool UntrustedEnv(CheckerContext &C);
+};
+
+using RuleLookupTy = CallDescriptionMap<GenericTaintRule>;
+
+/// Used to parse the configuration file.
+struct TaintConfiguration {
+  using NameScopeArgs = std::tuple<std::string, std::string, ArgVecTy>;
+  enum class VariadicType { None, Src, Dst };
+
+  struct Common {
+    std::string Name;
+    std::string Scope;
+  };
+
+  struct Sink : Common {
+    ArgVecTy SinkArgs;
+  };
+
+  struct Filter : Common {
+    ArgVecTy FilterArgs;
+  };
 
-  struct TaintPropagationRule;
-  template <typename T>
-  using ConfigDataMap =
-      std::unordered_multimap<std::string, std::pair<std::string, T>>;
-  using NameRuleMap = ConfigDataMap<TaintPropagationRule>;
-  using NameArgMap = ConfigDataMap<ArgVector>;
-
-  /// Find a function with the given name and scope. Returns the first match
-  /// or the end of the map.
-  template <typename T>
-  static auto findFunctionInConfig(const ConfigDataMap<T> &Map,
-                                   const FunctionData &FData);
-
-  /// A struct used to specify taint propagation rules for a function.
-  ///
-  /// If any of the possible taint source arguments is tainted, all of the
-  /// destination arguments should also be tainted. Use InvalidArgIndex in the
-  /// src list to specify that all of the arguments can introduce taint. Use
-  /// InvalidArgIndex in the dst arguments to signify that all the non-const
-  /// pointer and reference arguments might be tainted on return. If
-  /// ReturnValueIndex is added to the dst list, the return value will be
-  /// tainted.
-  struct TaintPropagationRule {
-    using PropagationFuncType = bool (*)(bool IsTainted, const CallEvent &Call,
-                                         CheckerContext &C);
-
-    /// List of arguments which can be taint sources and should be checked.
-    ArgVector SrcArgs;
-    /// List of arguments which should be tainted on function return.
-    ArgVector DstArgs;
-    /// Index for the first variadic parameter if exist.
-    unsigned VariadicIndex;
-    /// Show when a function has variadic parameters. If it has, it marks all
-    /// of them as source or destination.
+  struct Propagation : Common {
+    ArgVecTy SrcArgs;
+    ArgVecTy DstArgs;
     VariadicType VarType;
-    /// Special function for tainted source determination. If defined, it can
-    /// override the default behavior.
-    PropagationFuncType PropagationFunc;
-
-    TaintPropagationRule()
-        : VariadicIndex(InvalidArgIndex), VarType(VariadicType::None),
-          PropagationFunc(nullptr) {}
-
-    TaintPropagationRule(ArgVector &&Src, ArgVector &&Dst,
-                         VariadicType Var = VariadicType::None,
-                         unsigned VarIndex = InvalidArgIndex,
-                         PropagationFuncType Func = nullptr)
-        : SrcArgs(std::move(Src)), DstArgs(std::move(Dst)),
-          VariadicIndex(VarIndex), VarType(Var), PropagationFunc(Func) {}
-
-    /// Get the propagation rule for a given function.
-    static TaintPropagationRule
-    getTaintPropagationRule(const NameRuleMap &CustomPropagations,
-                            const FunctionData &FData, CheckerContext &C);
-
-    void addSrcArg(unsigned A) { SrcArgs.push_back(A); }
-    void addDstArg(unsigned A) { DstArgs.push_back(A); }
-
-    bool isNull() const {
-      return SrcArgs.empty() && DstArgs.empty() &&
-             VariadicType::None == VarType;
-    }
+    ArgIdxTy VarIndex;
+  };
 
-    bool isDestinationArgument(unsigned ArgNum) const {
-      return llvm::is_contained(DstArgs, ArgNum);
-    }
+  std::vector<Propagation> Propagations;
+  std::vector<Filter> Filters;
+  std::vector<Sink> Sinks;
 
-    static bool isTaintedOrPointsToTainted(const Expr *E,
-                                           const ProgramStateRef &State,
-                                           CheckerContext &C) {
-      if (isTainted(State, E, C.getLocationContext()) || isStdin(E, C))
-        return true;
+  TaintConfiguration() = default;
+  TaintConfiguration(const TaintConfiguration &) = default;
+  TaintConfiguration(TaintConfiguration &&) = default;
+  TaintConfiguration &operator=(const TaintConfiguration &) = default;
+  TaintConfiguration &operator=(TaintConfiguration &&) = default;
+};
 
-      if (!E->getType().getTypePtr()->isPointerType())
-        return false;
+struct GenericTaintRuleParser {
+  GenericTaintRuleParser(CheckerManager &Mgr) : Mgr(Mgr) {}
+  /// Container type used to gather call identification objects grouped into
+  /// pairs with their corresponding taint rules. It is temporary as it is used
+  /// to finally initialize RuleLookupTy, which is considered to be immutable.
+  using RulesContTy = std::vector<std::pair<CallDescription, GenericTaintRule>>;
+  RulesContTy parseConfiguration(const std::string &Option,
+                                 TaintConfiguration &&Config) const;
 
-      Optional<SVal> V = getPointeeOf(C, E);
-      return (V && isTainted(State, *V));
-    }
+private:
+  using NamePartsTy = llvm::SmallVector<SmallString<32>, 2>;
 
-    /// Pre-process a function which propagates taint according to the
-    /// taint rule.
-    ProgramStateRef process(const CallEvent &Call, CheckerContext &C) const;
+  /// Validate part of the configuration, which contains a list of argument
+  /// indexes.
+  void validateArgVector(const std::string &Option, const ArgVecTy &Args) const;
 
-    // Functions for custom taintedness propagation.
-    static bool postSocket(bool IsTainted, const CallEvent &Call,
-                           CheckerContext &C);
-  };
+  template <typename Config> static NamePartsTy parseNameParts(const Config &C);
 
-  /// Defines a map between the propagation function's name, scope
-  /// and TaintPropagationRule.
-  NameRuleMap CustomPropagations;
+  // Takes the config and creates a CallDescription for it and associates a Rule
+  // with that.
+  template <typename Config>
+  static void consumeRulesFromConfig(const Config &C, GenericTaintRule &&Rule,
+                                     RulesContTy &Rules);
 
-  /// Defines a map between the filter function's name, scope and filtering
-  /// args.
-  NameArgMap CustomFilters;
+  void parseConfig(const std::string &Option, TaintConfiguration::Sink &&P,
+                   RulesContTy &Rules) const;
+  void parseConfig(const std::string &Option, TaintConfiguration::Filter &&P,
+                   RulesContTy &Rules) const;
+  void parseConfig(const std::string &Option,
+                   TaintConfiguration::Propagation &&P,
+                   RulesContTy &Rules) const;
 
-  /// Defines a map between the sink function's name, scope and sinking args.
-  NameArgMap CustomSinks;
+  CheckerManager &Mgr;
 };
 
-const unsigned GenericTaintChecker::ReturnValueIndex;
-const unsigned GenericTaintChecker::InvalidArgIndex;
+class GenericTaintChecker : public Checker<check::PreCall, check::PostCall> {
+public:
+  static void *getTag() {
+    static int Tag;
+    return &Tag;
+  }
 
-// FIXME: these lines can be removed in C++17
-constexpr llvm::StringLiteral GenericTaintChecker::MsgUncontrolledFormatString;
-constexpr llvm::StringLiteral GenericTaintChecker::MsgSanitizeSystemArgs;
-constexpr llvm::StringLiteral GenericTaintChecker::MsgTaintedBufferSize;
-constexpr llvm::StringLiteral GenericTaintChecker::MsgCustomSink;
-} // end of anonymous namespace
+  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
 
-using TaintConfig = GenericTaintChecker::TaintConfiguration;
+  void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+                  const char *Sep) const override;
 
-LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfig::Propagation)
-LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfig::NameScopeArgs)
+  /// Generate a report if the expression is tainted or points to tainted data.
+  bool generateReportIfTainted(const Expr *E, StringRef Msg,
+                               CheckerContext &C) const;
+
+private:
+  const BugType BT{this, "Use of Untrusted Data", "Untrusted Data"};
+
+  bool checkUncontrolledFormatString(const CallEvent &Call,
+                                     CheckerContext &C) const;
+
+  void taintUnsafeSocketProtocol(const CallEvent &Call,
+                                 CheckerContext &C) const;
+
+  /// Default taint rules are initilized with the help of a CheckerContext to
+  /// access the names of built-in functions like memcpy.
+  void initTaintRules(CheckerContext &C) const;
+
+  /// CallDescription currently cannot restrict matches to the global namespace
+  /// only, which is why multiple CallDescriptionMaps are used, as we want to
+  /// disambiguate global C functions from functions inside user-defined
+  /// namespaces.
+  // TODO: Remove separation to simplify matching logic once CallDescriptions
+  // are more expressive.
+
+  mutable Optional<RuleLookupTy> StaticTaintRules;
+  mutable Optional<RuleLookupTy> DynamicTaintRules;
+};
+} // end of anonymous namespace
+
+/// YAML serialization mapping.
+LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfiguration::Sink)
+LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfiguration::Filter)
+LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfiguration::Propagation)
 
 namespace llvm {
 namespace yaml {
-template <> struct MappingTraits<TaintConfig> {
-  static void mapping(IO &IO, TaintConfig &Config) {
+template <> struct MappingTraits<TaintConfiguration> {
+  static void mapping(IO &IO, TaintConfiguration &Config) {
     IO.mapOptional("Propagations", Config.Propagations);
     IO.mapOptional("Filters", Config.Filters);
     IO.mapOptional("Sinks", Config.Sinks);
   }
 };
 
-template <> struct MappingTraits<TaintConfig::Propagation> {
-  static void mapping(IO &IO, TaintConfig::Propagation &Propagation) {
+template <> struct MappingTraits<TaintConfiguration::Sink> {
+  static void mapping(IO &IO, TaintConfiguration::Sink &Sink) {
+    IO.mapRequired("Name", Sink.Name);
+    IO.mapOptional("Scope", Sink.Scope);
+    IO.mapRequired("Args", Sink.SinkArgs);
+  }
+};
+
+template <> struct MappingTraits<TaintConfiguration::Filter> {
+  static void mapping(IO &IO, TaintConfiguration::Filter &Filter) {
+    IO.mapRequired("Name", Filter.Name);
+    IO.mapOptional("Scope", Filter.Scope);
+    IO.mapRequired("Args", Filter.FilterArgs);
+  }
+};
+
+template <> struct MappingTraits<TaintConfiguration::Propagation> {
+  static void mapping(IO &IO, TaintConfiguration::Propagation &Propagation) {
     IO.mapRequired("Name", Propagation.Name);
     IO.mapOptional("Scope", Propagation.Scope);
     IO.mapOptional("SrcArgs", Propagation.SrcArgs);
     IO.mapOptional("DstArgs", Propagation.DstArgs);
-    IO.mapOptional("VariadicType", Propagation.VarType,
-                   GenericTaintChecker::VariadicType::None);
-    IO.mapOptional("VariadicIndex", Propagation.VarIndex,
-                   GenericTaintChecker::InvalidArgIndex);
-  }
-};
-
-template <> struct ScalarEnumerationTraits<GenericTaintChecker::VariadicType> {
-  static void enumeration(IO &IO, GenericTaintChecker::VariadicType &Value) {
-    IO.enumCase(Value, "None", GenericTaintChecker::VariadicType::None);
-    IO.enumCase(Value, "Src", GenericTaintChecker::VariadicType::Src);
-    IO.enumCase(Value, "Dst", GenericTaintChecker::VariadicType::Dst);
+    IO.mapOptional("VariadicType", Propagation.VarType);
+    IO.mapOptional("VariadicIndex", Propagation.VarIndex);
   }
 };
 
-template <> struct MappingTraits<TaintConfig::NameScopeArgs> {
-  static void mapping(IO &IO, TaintConfig::NameScopeArgs &NSA) {
-    IO.mapRequired("Name", std::get<0>(NSA));
-    IO.mapOptional("Scope", std::get<1>(NSA));
-    IO.mapRequired("Args", std::get<2>(NSA));
+template <> struct ScalarEnumerationTraits<TaintConfiguration::VariadicType> {
+  static void enumeration(IO &IO, TaintConfiguration::VariadicType &Value) {
+    IO.enumCase(Value, "None", TaintConfiguration::VariadicType::None);
+    IO.enumCase(Value, "Src", TaintConfiguration::VariadicType::Src);
+    IO.enumCase(Value, "Dst", TaintConfiguration::VariadicType::Dst);
   }
 };
 } // namespace yaml
 } // namespace llvm
 
 /// A set which is used to pass information from call pre-visit instruction
-/// to the call post-visit. The values are unsigned integers, which are either
+/// to the call post-visit. The values are signed integers, which are either
 /// ReturnValueIndex, or indexes of the pointer/reference argument, which
 /// points to data, which should be tainted on return.
-REGISTER_SET_WITH_PROGRAMSTATE(TaintArgsOnPostVisit, unsigned)
-
-GenericTaintChecker::ArgVector
-GenericTaintChecker::convertToArgVector(CheckerManager &Mgr,
-                                        const std::string &Option,
-                                        const SignedArgVector &Args) {
-  ArgVector Result;
-  for (int Arg : Args) {
-    if (Arg == -1)
-      Result.push_back(ReturnValueIndex);
-    else if (Arg < -1) {
-      Result.push_back(InvalidArgIndex);
+REGISTER_SET_WITH_PROGRAMSTATE(TaintArgsOnPostVisit, ArgIdxTy)
+
+void GenericTaintRuleParser::validateArgVector(const std::string &Option,
+                                               const ArgVecTy &Args) const {
+  for (ArgIdxTy Arg : Args) {
+    if (Arg < ReturnValueIndex) {
       Mgr.reportInvalidCheckerOptionValue(
-          this, Option,
+          Mgr.getChecker<GenericTaintChecker>(), Option,
           "an argument number for propagation rules greater or equal to -1");
-    } else
-      Result.push_back(static_cast<unsigned>(Arg));
+    }
   }
-  return Result;
 }
 
-void GenericTaintChecker::parseConfiguration(CheckerManager &Mgr,
-                                             const std::string &Option,
-                                             TaintConfiguration &&Config) {
-  for (auto &P : Config.Propagations) {
-    GenericTaintChecker::CustomPropagations.emplace(
-        P.Name,
-        std::make_pair(P.Scope, TaintPropagationRule{
-                                    std::move(P.SrcArgs),
-                                    convertToArgVector(Mgr, Option, P.DstArgs),
-                                    P.VarType, P.VarIndex}));
+template <typename Config>
+GenericTaintRuleParser::NamePartsTy
+GenericTaintRuleParser::parseNameParts(const Config &C) {
+  NamePartsTy NameParts;
+  if (!C.Scope.empty()) {
+    // If the Scope argument contains multiple "::" parts, those are considered
+    // namespace identifiers.
+    llvm::SmallVector<StringRef, 2> NSParts;
+    StringRef{C.Scope}.split(NSParts, "::", /*MaxSplit*/ -1,
+                             /*KeepEmpty*/ false);
+    NameParts.append(NSParts.begin(), NSParts.end());
   }
+  NameParts.emplace_back(C.Name);
+  return NameParts;
+}
 
-  for (auto &F : Config.Filters) {
-    GenericTaintChecker::CustomFilters.emplace(
-        std::get<0>(F),
-        std::make_pair(std::move(std::get<1>(F)), std::move(std::get<2>(F))));
-  }
+template <typename Config>
+void GenericTaintRuleParser::consumeRulesFromConfig(const Config &C,
+                                                    GenericTaintRule &&Rule,
+                                                    RulesContTy &Rules) {
+  NamePartsTy NameParts = parseNameParts(C);
+  llvm::SmallVector<const char *, 2> CallDescParts{NameParts.size()};
+  llvm::transform(NameParts, CallDescParts.begin(),
+                  [](SmallString<32> &S) { return S.c_str(); });
+  Rules.emplace_back(CallDescParts, std::move(Rule));
+}
 
-  for (auto &S : Config.Sinks) {
-    GenericTaintChecker::CustomSinks.emplace(
-        std::get<0>(S),
-        std::make_pair(std::move(std::get<1>(S)), std::move(std::get<2>(S))));
-  }
+void GenericTaintRuleParser::parseConfig(const std::string &Option,
+                                         TaintConfiguration::Sink &&S,
+                                         RulesContTy &Rules) const {
+  validateArgVector(Option, S.SinkArgs);
+  consumeRulesFromConfig(S, GenericTaintRule::Sink(std::move(S.SinkArgs)),
+                         Rules);
 }
 
-template <typename T>
-auto GenericTaintChecker::findFunctionInConfig(const ConfigDataMap<T> &Map,
-                                               const FunctionData &FData) {
-  auto Range = Map.equal_range(std::string(FData.Name));
-  auto It =
-      std::find_if(Range.first, Range.second, [&FData](const auto &Entry) {
-        const auto &Value = Entry.second;
-        StringRef Scope = Value.first;
-        return Scope.empty() || FData.isInScope(Scope);
-      });
-  return It != Range.second ? It : Map.end();
+void GenericTaintRuleParser::parseConfig(const std::string &Option,
+                                         TaintConfiguration::Filter &&S,
+                                         RulesContTy &Rules) const {
+  validateArgVector(Option, S.FilterArgs);
+  consumeRulesFromConfig(S, GenericTaintRule::Filter(std::move(S.FilterArgs)),
+                         Rules);
 }
 
-GenericTaintChecker::TaintPropagationRule
-GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
-    const NameRuleMap &CustomPropagations, const FunctionData &FData,
-    CheckerContext &C) {
-  // TODO: Currently, we might lose precision here: we always mark a return
-  // value as tainted even if it's just a pointer, pointing to tainted data.
+void GenericTaintRuleParser::parseConfig(const std::string &Option,
+                                         TaintConfiguration::Propagation &&P,
+                                         RulesContTy &Rules) const {
+  validateArgVector(Option, P.SrcArgs);
+  validateArgVector(Option, P.DstArgs);
+  bool IsSrcVariadic = P.VarType == TaintConfiguration::VariadicType::Src;
+  bool IsDstVariadic = P.VarType == TaintConfiguration::VariadicType::Dst;
+  Optional<ArgIdxTy> JustVarIndex = P.VarIndex;
+
+  ArgSet SrcDesc(std::move(P.SrcArgs), IsSrcVariadic ? JustVarIndex : None);
+  ArgSet DstDesc(std::move(P.DstArgs), IsDstVariadic ? JustVarIndex : None);
+
+  consumeRulesFromConfig(
+      P, GenericTaintRule::Prop(std::move(SrcDesc), std::move(DstDesc)), Rules);
+}
+
+GenericTaintRuleParser::RulesContTy
+GenericTaintRuleParser::parseConfiguration(const std::string &Option,
+                                           TaintConfiguration &&Config) const {
+
+  RulesContTy Rules;
+
+  for (auto &F : Config.Filters)
+    parseConfig(Option, std::move(F), Rules);
+
+  for (auto &S : Config.Sinks)
+    parseConfig(Option, std::move(S), Rules);
 
+  for (auto &P : Config.Propagations)
+    parseConfig(Option, std::move(P), Rules);
+
+  return Rules;
+}
+
+void GenericTaintChecker::initTaintRules(CheckerContext &C) const {
   // Check for exact name match for functions without builtin substitutes.
   // Use qualified name, because these are C functions without namespace.
-  TaintPropagationRule Rule =
-      llvm::StringSwitch<TaintPropagationRule>(FData.FullName)
-          // Source functions
-          // TODO: Add support for vfscanf & family.
-          .Case("fdopen", {{}, {ReturnValueIndex}})
-          .Case("fopen", {{}, {ReturnValueIndex}})
-          .Case("freopen", {{}, {ReturnValueIndex}})
-          .Case("getch", {{}, {ReturnValueIndex}})
-          .Case("getchar", {{}, {ReturnValueIndex}})
-          .Case("getchar_unlocked", {{}, {ReturnValueIndex}})
-          .Case("gets", {{}, {0, ReturnValueIndex}})
-          .Case("scanf", {{}, {}, VariadicType::Dst, 1})
-          .Case("socket", {{},
-                           {ReturnValueIndex},
-                           VariadicType::None,
-                           InvalidArgIndex,
-                           &TaintPropagationRule::postSocket})
-          .Case("wgetch", {{}, {ReturnValueIndex}})
-          // Propagating functions
-          .Case("atoi", {{0}, {ReturnValueIndex}})
-          .Case("atol", {{0}, {ReturnValueIndex}})
-          .Case("atoll", {{0}, {ReturnValueIndex}})
-          .Case("fgetc", {{0}, {ReturnValueIndex}})
-          .Case("fgetln", {{0}, {ReturnValueIndex}})
-          .Case("fgets", {{2}, {0, ReturnValueIndex}})
-          .Case("fscanf", {{0}, {}, VariadicType::Dst, 2})
-          .Case("sscanf", {{0}, {}, VariadicType::Dst, 2})
-          .Case("getc", {{0}, {ReturnValueIndex}})
-          .Case("getc_unlocked", {{0}, {ReturnValueIndex}})
-          .Case("getdelim", {{3}, {0}})
-          .Case("getline", {{2}, {0}})
-          .Case("getw", {{0}, {ReturnValueIndex}})
-          .Case("pread", {{0, 1, 2, 3}, {1, ReturnValueIndex}})
-          .Case("read", {{0, 2}, {1, ReturnValueIndex}})
-          .Case("strchr", {{0}, {ReturnValueIndex}})
-          .Case("strrchr", {{0}, {ReturnValueIndex}})
-          .Case("tolower", {{0}, {ReturnValueIndex}})
-          .Case("toupper", {{0}, {ReturnValueIndex}})
-          .Default({});
-
-  if (!Rule.isNull())
-    return Rule;
+
+  if (StaticTaintRules || DynamicTaintRules)
+    return;
+
+  using RulesConstructionTy =
+      std::vector<std::pair<CallDescription, GenericTaintRule>>;
+  using TR = GenericTaintRule;
+
+  const Builtin::Context &BI = C.getASTContext().BuiltinInfo;
+
+  RulesConstructionTy GlobalCRules{
+      // Sources
+      {{"fdopen"}, TR::Source({{ReturnValueIndex}})},
+      {{"fopen"}, TR::Source({{ReturnValueIndex}})},
+      {{"freopen"}, TR::Source({{ReturnValueIndex}})},
+      {{"getch"}, TR::Source({{ReturnValueIndex}})},
+      {{"getchar"}, TR::Source({{ReturnValueIndex}})},
+      {{"getchar_unlocked"}, TR::Source({{ReturnValueIndex}})},
+      {{"gets"}, TR::Source({{0}, ReturnValueIndex})},
+      {{"scanf"}, TR::Source({{}, 1})},
+      {{"wgetch"}, TR::Source({{}, ReturnValueIndex})},
+
+      // Props
+      {{"atoi"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+      {{"atol"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+      {{"atoll"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+      {{"fgetc"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+      {{"fgetln"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+      {{"fgets"}, TR::Prop({{2}}, {{0}, ReturnValueIndex})},
+      {{"fscanf"}, TR::Prop({{0}}, {{}, 2})},
+      {{"sscanf"}, TR::Prop({{0}}, {{}, 2})},
+      {{"getc"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+      {{"getc_unlocked"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+      {{"getdelim"}, TR::Prop({{3}}, {{0}})},
+      {{"getline"}, TR::Prop({{2}}, {{0}})},
+      {{"getw"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+      {{"pread"}, TR::Prop({{0, 1, 2, 3}}, {{1, ReturnValueIndex}})},
+      {{"read"}, TR::Prop({{0, 2}}, {{1, ReturnValueIndex}})},
+      {{"strchr"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+      {{"strrchr"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+      {{"tolower"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+      {{"toupper"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+      {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrncat)}},
+       TR::Prop({{1, 2}}, {{0, ReturnValueIndex}})},
+      {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrlcpy)}},
+       TR::Prop({{1, 2}}, {{0}})},
+      {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrlcat)}},
+       TR::Prop({{1, 2}}, {{0}})},
+      {{CDF_MaybeBuiltin, {"snprintf"}},
+       TR::Prop({{1}, 3}, {{0, ReturnValueIndex}})},
+      {{CDF_MaybeBuiltin, {"sprintf"}},
+       TR::Prop({{1}, 2}, {{0, ReturnValueIndex}})},
+      {{CDF_MaybeBuiltin, {"strcpy"}},
+       TR::Prop({{1}}, {{0, ReturnValueIndex}})},
+      {{CDF_MaybeBuiltin, {"stpcpy"}},
+       TR::Prop({{1}}, {{0, ReturnValueIndex}})},
+      {{CDF_MaybeBuiltin, {"strcat"}},
+       TR::Prop({{1}}, {{0, ReturnValueIndex}})},
+      {{CDF_MaybeBuiltin, {"strdup"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+      {{CDF_MaybeBuiltin, {"strdupa"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+      {{CDF_MaybeBuiltin, {"wcsdup"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+
+      // Sinks
+      {{"system"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+      {{"popen"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+      {{"execl"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+      {{"execle"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+      {{"execlp"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+      {{"execvp"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+      {{"execvP"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+      {{"execve"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+      {{"dlopen"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+      {{CDF_MaybeBuiltin, {"malloc"}}, TR::Sink({{0}}, MsgTaintedBufferSize)},
+      {{CDF_MaybeBuiltin, {"calloc"}}, TR::Sink({{0}}, MsgTaintedBufferSize)},
+      {{CDF_MaybeBuiltin, {"alloca"}}, TR::Sink({{0}}, MsgTaintedBufferSize)},
+      {{CDF_MaybeBuiltin, {"memccpy"}}, TR::Sink({{3}}, MsgTaintedBufferSize)},
+      {{CDF_MaybeBuiltin, {"realloc"}}, TR::Sink({{1}}, MsgTaintedBufferSize)},
+      {{{"setproctitle"}}, TR::Sink({{0}, 1}, MsgUncontrolledFormatString)},
+      {{{"setproctitle_fast"}},
+       TR::Sink({{0}, 1}, MsgUncontrolledFormatString)},
+
+      // SinkProps
+      {{CDF_MaybeBuiltin, BI.getName(Builtin::BImemcpy)},
+       TR::SinkProp({{2}}, {{1, 2}}, {{0, ReturnValueIndex}},
+                    MsgTaintedBufferSize)},
+      {{CDF_MaybeBuiltin, {BI.getName(Builtin::BImemmove)}},
+       TR::SinkProp({{2}}, {{1, 2}}, {{0, ReturnValueIndex}},
+                    MsgTaintedBufferSize)},
+      {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrncpy)}},
+       TR::SinkProp({{2}}, {{1, 2}}, {{0, ReturnValueIndex}},
+                    MsgTaintedBufferSize)},
+      {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrndup)}},
+       TR::SinkProp({{1}}, {{0, 1}}, {{ReturnValueIndex}},
+                    MsgTaintedBufferSize)},
+      {{CDF_MaybeBuiltin, {"bcopy"}},
+       TR::SinkProp({{2}}, {{0, 2}}, {{1}}, MsgTaintedBufferSize)}};
 
   // `getenv` returns taint only in untrusted environments.
-  if (FData.FullName == "getenv") {
-    if (C.getAnalysisManager()
-            .getAnalyzerOptions()
-            .ShouldAssumeControlledEnvironment)
-      return {};
-    return {{}, {ReturnValueIndex}};
+  if (TR::UntrustedEnv(C)) {
+    // void setproctitle_init(int argc, char *argv[], char *envp[])
+    GlobalCRules.push_back(
+        {{{"setproctitle_init"}}, TR::Sink({{2}}, MsgCustomSink)});
+    GlobalCRules.push_back({{"getenv"}, TR::Source({{ReturnValueIndex}})});
   }
 
-  assert(FData.FDecl);
-
-  // Check if it's one of the memory setting/copying functions.
-  // This check is specialized but faster then calling isCLibraryFunction.
-  const FunctionDecl *FDecl = FData.FDecl;
-  unsigned BId = 0;
-  if ((BId = FDecl->getMemoryFunctionKind())) {
-    switch (BId) {
-    case Builtin::BImemcpy:
-    case Builtin::BImemmove:
-    case Builtin::BIstrncpy:
-    case Builtin::BIstrncat:
-      return {{1, 2}, {0, ReturnValueIndex}};
-    case Builtin::BIstrlcpy:
-    case Builtin::BIstrlcat:
-      return {{1, 2}, {0}};
-    case Builtin::BIstrndup:
-      return {{0, 1}, {ReturnValueIndex}};
-
-    default:
-      break;
-    }
-  }
+  StaticTaintRules.emplace(std::make_move_iterator(GlobalCRules.begin()),
+                           std::make_move_iterator(GlobalCRules.end()));
 
-  // Process all other functions which could be defined as builtins.
-  if (Rule.isNull()) {
-    const auto OneOf = [FDecl](const auto &... Name) {
-      // FIXME: use fold expression in C++17
-      using unused = int[];
-      bool ret = false;
-      static_cast<void>(unused{
-          0, (ret |= CheckerContext::isCLibraryFunction(FDecl, Name), 0)...});
-      return ret;
-    };
-    if (OneOf("snprintf"))
-      return {{1}, {0, ReturnValueIndex}, VariadicType::Src, 3};
-    if (OneOf("sprintf"))
-      return {{1}, {0, ReturnValueIndex}, VariadicType::Src, 2};
-    if (OneOf("strcpy", "stpcpy", "strcat"))
-      return {{1}, {0, ReturnValueIndex}};
-    if (OneOf("bcopy"))
-      return {{0, 2}, {1}};
-    if (OneOf("strdup", "strdupa", "wcsdup"))
-      return {{0}, {ReturnValueIndex}};
+  // User-provided taint configuration.
+  CheckerManager *Mgr = C.getAnalysisManager().getCheckerManager();
+  assert(Mgr);
+  GenericTaintRuleParser ConfigParser{*Mgr};
+  std::string Option{"Config"};
+  StringRef ConfigFile =
+      Mgr->getAnalyzerOptions().getCheckerStringOption(this, Option);
+  llvm::Optional<TaintConfiguration> Config =
+      getConfiguration<TaintConfiguration>(*Mgr, this, Option, ConfigFile);
+  if (!Config) {
+    // We don't have external taint config, no parsing required.
+    DynamicTaintRules = RuleLookupTy{};
+    return;
   }
 
-  // Skipping the following functions, since they might be used for cleansing or
-  // smart memory copy:
-  // - memccpy - copying until hitting a special character.
+  GenericTaintRuleParser::RulesContTy Rules{
+      ConfigParser.parseConfiguration(Option, std::move(Config.getValue()))};
 
-  auto It = findFunctionInConfig(CustomPropagations, FData);
-  if (It != CustomPropagations.end())
-    return It->second.second;
-  return {};
+  DynamicTaintRules.emplace(std::make_move_iterator(Rules.begin()),
+                            std::make_move_iterator(Rules.end()));
 }
 
 void GenericTaintChecker::checkPreCall(const CallEvent &Call,
                                        CheckerContext &C) const {
-  Optional<FunctionData> FData = FunctionData::create(Call, C);
-  if (!FData)
-    return;
-
-  // Check for taintedness related errors first: system call, uncontrolled
-  // format string, tainted buffer size.
-  if (checkPre(Call, *FData, C))
-    return;
-
-  // Marks the function's arguments and/or return value tainted if it present in
-  // the list.
-  if (addSourcesPre(Call, *FData, C))
-    return;
-
-  addFiltersPre(Call, *FData, C);
+  initTaintRules(C);
+
+  // FIXME: this should be much simpler.
+  if (const auto *Rule =
+          Call.isGlobalCFunction() ? StaticTaintRules->lookup(Call) : nullptr)
+    Rule->process(*this, Call, C);
+  else if (const auto *Rule = DynamicTaintRules->lookup(Call))
+    Rule->process(*this, Call, C);
+
+  // FIXME: These edge cases are to be eliminated from here eventually.
+  //
+  // Additional check that is not supported by CallDescription.
+  // TODO: Make CallDescription be able to match attributes such as printf-like
+  // arguments.
+  checkUncontrolledFormatString(Call, C);
+
+  // TODO: Modeling sockets should be done in a specific checker.
+  // Socket is a source, which taints the return value.
+  taintUnsafeSocketProtocol(Call, C);
 }
 
 void GenericTaintChecker::checkPostCall(const CallEvent &Call,
                                         CheckerContext &C) const {
   // Set the marked values as tainted. The return value only accessible from
   // checkPostStmt.
-  propagateFromPre(Call, C);
-}
-
-void GenericTaintChecker::printState(raw_ostream &Out, ProgramStateRef State,
-                                     const char *NL, const char *Sep) const {
-  printTaint(State, Out, NL, Sep);
-}
-
-bool GenericTaintChecker::addSourcesPre(const CallEvent &Call,
-                                        const FunctionData &FData,
-                                        CheckerContext &C) const {
-  // First, try generating a propagation rule for this function.
-  TaintPropagationRule Rule = TaintPropagationRule::getTaintPropagationRule(
-      this->CustomPropagations, FData, C);
-  if (!Rule.isNull()) {
-    ProgramStateRef State = Rule.process(Call, C);
-    if (State) {
-      C.addTransition(State);
-      return true;
-    }
-  }
-  return false;
-}
-
-bool GenericTaintChecker::addFiltersPre(const CallEvent &Call,
-                                        const FunctionData &FData,
-                                        CheckerContext &C) const {
-  auto It = findFunctionInConfig(CustomFilters, FData);
-  if (It == CustomFilters.end())
-    return false;
-
-  ProgramStateRef State = C.getState();
-  const auto &Value = It->second;
-  const ArgVector &Args = Value.second;
-  for (unsigned ArgNum : Args) {
-    if (ArgNum >= Call.getNumArgs())
-      continue;
-
-    const Expr *Arg = Call.getArgExpr(ArgNum);
-    Optional<SVal> V = getPointeeOf(C, Arg);
-    if (V)
-      State = removeTaint(State, *V);
-  }
-
-  if (State != C.getState()) {
-    C.addTransition(State);
-    return true;
-  }
-  return false;
-}
-
-bool GenericTaintChecker::propagateFromPre(const CallEvent &Call,
-                                           CheckerContext &C) {
   ProgramStateRef State = C.getState();
 
   // Depending on what was tainted at pre-visit, we determined a set of
@@ -616,9 +689,9 @@ bool GenericTaintChecker::propagateFromPre(const CallEvent &Call,
   // stored in the state as TaintArgsOnPostVisit set.
   TaintArgsOnPostVisitTy TaintArgs = State->get<TaintArgsOnPostVisit>();
   if (TaintArgs.isEmpty())
-    return false;
+    return;
 
-  for (unsigned ArgNum : TaintArgs) {
+  for (ArgIdxTy ArgNum : TaintArgs) {
     // Special handling for the tainted return value.
     if (ArgNum == ReturnValueIndex) {
       State = addTaint(State, Call.getReturnValue());
@@ -627,234 +700,147 @@ bool GenericTaintChecker::propagateFromPre(const CallEvent &Call,
 
     // The arguments are pointer arguments. The data they are pointing at is
     // tainted after the call.
-    if (Call.getNumArgs() < (ArgNum + 1))
-      return false;
-    const Expr *Arg = Call.getArgExpr(ArgNum);
-    Optional<SVal> V = getPointeeOf(C, Arg);
-    if (V)
+    if (auto V = getPointeeOf(C, Call.getArgSVal(ArgNum)))
       State = addTaint(State, *V);
   }
 
   // Clear up the taint info from the state.
   State = State->remove<TaintArgsOnPostVisit>();
-
-  if (State != C.getState()) {
-    C.addTransition(State);
-    return true;
-  }
-  return false;
-}
-
-bool GenericTaintChecker::checkPre(const CallEvent &Call,
-                                   const FunctionData &FData,
-                                   CheckerContext &C) const {
-  if (checkUncontrolledFormatString(Call, C))
-    return true;
-
-  if (checkSystemCall(Call, FData.Name, C))
-    return true;
-
-  if (checkTaintedBufferSize(Call, C))
-    return true;
-
-  return checkCustomSinks(Call, FData, C);
+  C.addTransition(State);
 }
 
-Optional<SVal> GenericTaintChecker::getPointeeOf(CheckerContext &C,
-                                                 const Expr *Arg) {
-  ProgramStateRef State = C.getState();
-  SVal AddrVal = C.getSVal(Arg->IgnoreParens());
-  if (AddrVal.isUnknownOrUndef())
-    return None;
-
-  Optional<Loc> AddrLoc = AddrVal.getAs<Loc>();
-  if (!AddrLoc)
-    return None;
-
-  QualType ArgTy = Arg->getType().getCanonicalType();
-  if (!ArgTy->isPointerType())
-    return State->getSVal(*AddrLoc);
-
-  QualType ValTy = ArgTy->getPointeeType();
-
-  // Do not dereference void pointers. Treat them as byte pointers instead.
-  // FIXME: we might want to consider more than just the first byte.
-  if (ValTy->isVoidType())
-    ValTy = C.getASTContext().CharTy;
-
-  return State->getSVal(*AddrLoc, ValTy);
+void GenericTaintChecker::printState(raw_ostream &Out, ProgramStateRef State,
+                                     const char *NL, const char *Sep) const {
+  printTaint(State, Out, NL, Sep);
 }
 
-ProgramStateRef
-GenericTaintChecker::TaintPropagationRule::process(const CallEvent &Call,
-                                                   CheckerContext &C) const {
+void GenericTaintRule::process(const GenericTaintChecker &Checker,
+                               const CallEvent &Call, CheckerContext &C) const {
   ProgramStateRef State = C.getState();
+  const ArgIdxTy CallNumArgs = fromArgumentCount(Call.getNumArgs());
 
-  // Check for taint in arguments.
-  bool IsTainted = true;
-  for (unsigned ArgNum : SrcArgs) {
-    if (ArgNum >= Call.getNumArgs())
-      continue;
-
-    if ((IsTainted =
-             isTaintedOrPointsToTainted(Call.getArgExpr(ArgNum), State, C)))
-      break;
-  }
-
-  // Check for taint in variadic arguments.
-  if (!IsTainted && VariadicType::Src == VarType) {
-    // Check if any of the arguments is tainted
-    for (unsigned i = VariadicIndex; i < Call.getNumArgs(); ++i) {
-      if ((IsTainted =
-               isTaintedOrPointsToTainted(Call.getArgExpr(i), State, C)))
-        break;
+  /// Iterate every call argument, and get their corresponding Expr and SVal.
+  const auto ForEachCallArg = [&C, &Call, CallNumArgs](auto &&Fun) {
+    for (ArgIdxTy I = ReturnValueIndex; I < CallNumArgs; ++I) {
+      const Expr *E = GetArgExpr(I, Call);
+      Fun(I, E, C.getSVal(E));
     }
-  }
+  };
 
-  if (PropagationFunc)
-    IsTainted = PropagationFunc(IsTainted, Call, C);
+  /// Check for taint sinks.
+  ForEachCallArg([this, &Checker, &C, &State](ArgIdxTy I, const Expr *E, SVal) {
+    if (SinkArgs.contains(I) && isTaintedOrPointsToTainted(E, State, C))
+      Checker.generateReportIfTainted(E, SinkMsg.getValueOr(MsgCustomSink), C);
+  });
+
+  /// Check for taint filters.
+  ForEachCallArg([this, &C, &State](ArgIdxTy I, const Expr *E, SVal S) {
+    if (FilterArgs.contains(I)) {
+      State = removeTaint(State, S);
+      if (auto P = getPointeeOf(C, S))
+        State = removeTaint(State, *P);
+    }
+  });
+
+  /// Check for taint propagation sources.
+  /// A rule is relevant if PropSrcArgs is empty, or if any of its signified
+  /// args are tainted in context of the current CallEvent.
+  bool IsMatching = PropSrcArgs.isEmpty();
+  ForEachCallArg(
+      [this, &C, &IsMatching, &State](ArgIdxTy I, const Expr *E, SVal) {
+        IsMatching = IsMatching || (PropSrcArgs.contains(I) &&
+                                    isTaintedOrPointsToTainted(E, State, C));
+      });
 
-  if (!IsTainted)
-    return State;
+  if (!IsMatching)
+    return;
 
-  // Mark the arguments which should be tainted after the function returns.
-  for (unsigned ArgNum : DstArgs) {
-    // Should mark the return value?
-    if (ArgNum == ReturnValueIndex) {
-      State = State->add<TaintArgsOnPostVisit>(ReturnValueIndex);
-      continue;
-    }
+  const auto WouldEscape = [](SVal V, QualType Ty) -> bool {
+    if (!V.getAs<Loc>())
+      return false;
 
-    if (ArgNum >= Call.getNumArgs())
-      continue;
+    const bool IsNonConstRef = Ty->isReferenceType() && !Ty.isConstQualified();
+    const bool IsNonConstPtr =
+        Ty->isPointerType() && !Ty->getPointeeType().isConstQualified();
 
-    // Mark the given argument.
-    State = State->add<TaintArgsOnPostVisit>(ArgNum);
-  }
+    return IsNonConstRef || IsNonConstPtr;
+  };
 
-  // Mark all variadic arguments tainted if present.
-  if (VariadicType::Dst == VarType) {
-    // For all pointer and references that were passed in:
-    //   If they are not pointing to const data, mark data as tainted.
-    //   TODO: So far we are just going one level down; ideally we'd need to
-    //         recurse here.
-    for (unsigned i = VariadicIndex; i < Call.getNumArgs(); ++i) {
-      const Expr *Arg = Call.getArgExpr(i);
-      // Process pointer argument.
-      const Type *ArgTy = Arg->getType().getTypePtr();
-      QualType PType = ArgTy->getPointeeType();
-      if ((!PType.isNull() && !PType.isConstQualified()) ||
-          (ArgTy->isReferenceType() && !Arg->getType().isConstQualified())) {
-        State = State->add<TaintArgsOnPostVisit>(i);
-      }
-    }
-  }
+  /// Propagate taint where it is necessary.
+  ForEachCallArg(
+      [this, &State, WouldEscape](ArgIdxTy I, const Expr *E, SVal V) {
+        if (PropDstArgs.contains(I))
+          State = State->add<TaintArgsOnPostVisit>(I);
+
+        // TODO: We should traverse all reachable memory regions via the
+        // escaping parameter. Instead of doing that we simply mark only the
+        // referred memory region as tainted.
+        if (WouldEscape(V, E->getType()))
+          State = State->add<TaintArgsOnPostVisit>(I);
+      });
 
-  return State;
+  C.addTransition(State);
 }
 
-// If argument 0(protocol domain) is network, the return value should get taint.
-bool GenericTaintChecker::TaintPropagationRule::postSocket(
-    bool /*IsTainted*/, const CallEvent &Call, CheckerContext &C) {
-  SourceLocation DomLoc = Call.getArgExpr(0)->getExprLoc();
-  StringRef DomName = C.getMacroNameOrSpelling(DomLoc);
-  // White list the internal communication protocols.
-  if (DomName.equals("AF_SYSTEM") || DomName.equals("AF_LOCAL") ||
-      DomName.equals("AF_UNIX") || DomName.equals("AF_RESERVED_36"))
-    return false;
-  return true;
+bool GenericTaintRule::UntrustedEnv(CheckerContext &C) {
+  return !C.getAnalysisManager()
+              .getAnalyzerOptions()
+              .ShouldAssumeControlledEnvironment;
 }
 
-bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) {
-  ProgramStateRef State = C.getState();
-  SVal Val = C.getSVal(E);
-
-  // stdin is a pointer, so it would be a region.
-  const MemRegion *MemReg = Val.getAsRegion();
+bool GenericTaintChecker::generateReportIfTainted(const Expr *E, StringRef Msg,
+                                                  CheckerContext &C) const {
+  assert(E);
+  Optional<SVal> TaintedSVal{getTaintedPointeeOrPointer(C, C.getSVal(E))};
 
-  // The region should be symbolic, we do not know it's value.
-  const auto *SymReg = dyn_cast_or_null<SymbolicRegion>(MemReg);
-  if (!SymReg)
+  if (!TaintedSVal)
     return false;
 
-  // Get it's symbol and find the declaration region it's pointing to.
-  const auto *Sm = dyn_cast<SymbolRegionValue>(SymReg->getSymbol());
-  if (!Sm)
-    return false;
-  const auto *DeclReg = dyn_cast_or_null<DeclRegion>(Sm->getRegion());
-  if (!DeclReg)
-    return false;
-
-  // This region corresponds to a declaration, find out if it's a global/extern
-  // variable named stdin with the proper type.
-  if (const auto *D = dyn_cast_or_null<VarDecl>(DeclReg->getDecl())) {
-    D = D->getCanonicalDecl();
-    if (D->getName().contains("stdin") && D->isExternC()) {
-      const auto *PtrTy = dyn_cast<PointerType>(D->getType().getTypePtr());
-      if (PtrTy && PtrTy->getPointeeType().getCanonicalType() ==
-                       C.getASTContext().getFILEType().getCanonicalType())
-        return true;
-    }
+  // Generate diagnostic.
+  if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+    auto report = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
+    report->addRange(E->getSourceRange());
+    report->addVisitor(std::make_unique<TaintBugVisitor>(*TaintedSVal));
+    C.emitReport(std::move(report));
+    return true;
   }
   return false;
 }
 
+/// TODO: remove checking for printf format attributes and socket whitelisting
+/// from GenericTaintChecker, and that means the following functions:
+/// getPrintfFormatArgumentNum,
+/// GenericTaintChecker::checkUncontrolledFormatString,
+/// GenericTaintChecker::taintUnsafeSocketProtocol
+
 static bool getPrintfFormatArgumentNum(const CallEvent &Call,
                                        const CheckerContext &C,
-                                       unsigned &ArgNum) {
+                                       ArgIdxTy &ArgNum) {
   // Find if the function contains a format string argument.
   // Handles: fprintf, printf, sprintf, snprintf, vfprintf, vprintf, vsprintf,
   // vsnprintf, syslog, custom annotated functions.
-  const FunctionDecl *FDecl = Call.getDecl()->getAsFunction();
+  const Decl *CallDecl = Call.getDecl();
+  if (!CallDecl)
+    return false;
+  const FunctionDecl *FDecl = CallDecl->getAsFunction();
   if (!FDecl)
     return false;
+
+  const ArgIdxTy CallNumArgs = fromArgumentCount(Call.getNumArgs());
+
   for (const auto *Format : FDecl->specific_attrs<FormatAttr>()) {
     ArgNum = Format->getFormatIdx() - 1;
-    if ((Format->getType()->getName() == "printf") &&
-        Call.getNumArgs() > ArgNum)
+    if ((Format->getType()->getName() == "printf") && CallNumArgs > ArgNum)
       return true;
   }
 
-  // Or if a function is named setproctitle (this is a heuristic).
-  if (C.getCalleeName(FDecl).contains("setproctitle")) {
-    ArgNum = 0;
-    return true;
-  }
-
-  return false;
-}
-
-bool GenericTaintChecker::generateReportIfTainted(const Expr *E, StringRef Msg,
-                                                  CheckerContext &C) const {
-  assert(E);
-
-  // Check for taint.
-  ProgramStateRef State = C.getState();
-  Optional<SVal> PointedToSVal = getPointeeOf(C, E);
-  SVal TaintedSVal;
-  if (PointedToSVal && isTainted(State, *PointedToSVal))
-    TaintedSVal = *PointedToSVal;
-  else if (isTainted(State, E, C.getLocationContext()))
-    TaintedSVal = C.getSVal(E);
-  else
-    return false;
-
-  // Generate diagnostic.
-  if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
-    initBugType();
-    auto report = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
-    report->addRange(E->getSourceRange());
-    report->addVisitor(std::make_unique<TaintBugVisitor>(TaintedSVal));
-    C.emitReport(std::move(report));
-    return true;
-  }
   return false;
 }
 
 bool GenericTaintChecker::checkUncontrolledFormatString(
     const CallEvent &Call, CheckerContext &C) const {
   // Check if the function contains a format string argument.
-  unsigned ArgNum = 0;
+  ArgIdxTy ArgNum = 0;
   if (!getPrintfFormatArgumentNum(Call, C, ArgNum))
     return false;
 
@@ -864,102 +850,32 @@ bool GenericTaintChecker::checkUncontrolledFormatString(
                                  MsgUncontrolledFormatString, C);
 }
 
-bool GenericTaintChecker::checkSystemCall(const CallEvent &Call, StringRef Name,
-                                          CheckerContext &C) const {
-  // TODO: It might make sense to run this check on demand. In some cases,
-  // we should check if the environment has been cleansed here. We also might
-  // need to know if the user was reset before these calls(seteuid).
-  unsigned ArgNum = llvm::StringSwitch<unsigned>(Name)
-                        .Case("system", 0)
-                        .Case("popen", 0)
-                        .Case("execl", 0)
-                        .Case("execle", 0)
-                        .Case("execlp", 0)
-                        .Case("execv", 0)
-                        .Case("execvp", 0)
-                        .Case("execvP", 0)
-                        .Case("execve", 0)
-                        .Case("dlopen", 0)
-                        .Default(InvalidArgIndex);
-
-  if (ArgNum == InvalidArgIndex || Call.getNumArgs() < (ArgNum + 1))
-    return false;
-
-  return generateReportIfTainted(Call.getArgExpr(ArgNum), MsgSanitizeSystemArgs,
-                                 C);
-}
-
-// TODO: Should this check be a part of the CString checker?
-// If yes, should taint be a global setting?
-bool GenericTaintChecker::checkTaintedBufferSize(const CallEvent &Call,
-                                                 CheckerContext &C) const {
-  const auto *FDecl = Call.getDecl()->getAsFunction();
-  // If the function has a buffer size argument, set ArgNum.
-  unsigned ArgNum = InvalidArgIndex;
-  unsigned BId = 0;
-  if ((BId = FDecl->getMemoryFunctionKind())) {
-    switch (BId) {
-    case Builtin::BImemcpy:
-    case Builtin::BImemmove:
-    case Builtin::BIstrncpy:
-      ArgNum = 2;
-      break;
-    case Builtin::BIstrndup:
-      ArgNum = 1;
-      break;
-    default:
-      break;
-    }
-  }
+void GenericTaintChecker::taintUnsafeSocketProtocol(const CallEvent &Call,
+                                                    CheckerContext &C) const {
+  if (Call.getNumArgs() < 1)
+    return;
+  const IdentifierInfo *ID = Call.getCalleeIdentifier();
+  if (!ID)
+    return;
+  if (!ID->getName().equals("socket"))
+    return;
 
-  if (ArgNum == InvalidArgIndex) {
-    using CCtx = CheckerContext;
-    if (CCtx::isCLibraryFunction(FDecl, "malloc") ||
-        CCtx::isCLibraryFunction(FDecl, "calloc") ||
-        CCtx::isCLibraryFunction(FDecl, "alloca"))
-      ArgNum = 0;
-    else if (CCtx::isCLibraryFunction(FDecl, "memccpy"))
-      ArgNum = 3;
-    else if (CCtx::isCLibraryFunction(FDecl, "realloc"))
-      ArgNum = 1;
-    else if (CCtx::isCLibraryFunction(FDecl, "bcopy"))
-      ArgNum = 2;
-  }
+  SourceLocation DomLoc = Call.getArgExpr(0)->getExprLoc();
+  StringRef DomName = C.getMacroNameOrSpelling(DomLoc);
+  // Allow internal communication protocols.
+  bool SafeProtocol = DomName.equals("AF_SYSTEM") ||
+                      DomName.equals("AF_LOCAL") || DomName.equals("AF_UNIX") ||
+                      DomName.equals("AF_RESERVED_36");
+  if (SafeProtocol)
+    return;
 
-  return ArgNum != InvalidArgIndex && Call.getNumArgs() > ArgNum &&
-         generateReportIfTainted(Call.getArgExpr(ArgNum), MsgTaintedBufferSize,
-                                 C);
+  C.addTransition(C.getState()->add<TaintArgsOnPostVisit>(ReturnValueIndex));
 }
 
-bool GenericTaintChecker::checkCustomSinks(const CallEvent &Call,
-                                           const FunctionData &FData,
-                                           CheckerContext &C) const {
-  auto It = findFunctionInConfig(CustomSinks, FData);
-  if (It == CustomSinks.end())
-    return false;
-
-  const auto &Value = It->second;
-  const GenericTaintChecker::ArgVector &Args = Value.second;
-  for (unsigned ArgNum : Args) {
-    if (ArgNum >= Call.getNumArgs())
-      continue;
-
-    if (generateReportIfTainted(Call.getArgExpr(ArgNum), MsgCustomSink, C))
-      return true;
-  }
-
-  return false;
-}
+/// Checker registration
 
 void ento::registerGenericTaintChecker(CheckerManager &Mgr) {
-  auto *Checker = Mgr.registerChecker<GenericTaintChecker>();
-  std::string Option{"Config"};
-  StringRef ConfigFile =
-      Mgr.getAnalyzerOptions().getCheckerStringOption(Checker, Option);
-  llvm::Optional<TaintConfig> Config =
-      getConfiguration<TaintConfig>(Mgr, Checker, Option, ConfigFile);
-  if (Config)
-    Checker->parseConfiguration(Mgr, Option, std::move(Config.getValue()));
+  Mgr.registerChecker<GenericTaintChecker>();
 }
 
 bool ento::shouldRegisterGenericTaintChecker(const CheckerManager &mgr) {


        


More information about the cfe-commits mailing list