Index: lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
+++ lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
@@ -122,6 +122,11 @@
   bool checkTaintedBufferSize(const CallExpr *CE, const FunctionDecl *FDecl,
                               CheckerContext &C) const;
 
+  /// Check if tainted data is used as a custom sink's parameter.
+  static const char MsgCustomSink[];
+  bool checkCustomSinks(const CallExpr *CE, StringRef Name,
+                        CheckerContext &C) const;
+
   /// Generate a report if the expression is tainted or points to tainted data.
   bool generateReportIfTainted(const Expr *E, const char Msg[],
                                CheckerContext &C) const;
@@ -231,6 +236,9 @@
     "(CERT/STR31-C. Guarantee that storage for strings has sufficient space "
     "for character data and the null terminator)";
 
+const char GenericTaintChecker::MsgCustomSink[] =
+    "Untrusted data is passed to a user defined sink";
+
 GenericTaintChecker::NameRuleMap GenericTaintChecker::CustomPropagations;
 
 GenericTaintChecker::NameArgMap GenericTaintChecker::CustomFilters;
@@ -257,7 +265,7 @@
   static void mapping(IO &IO, TaintConfig::Propagation &Propagation) {
     IO.mapRequired("Name", Propagation.Name);
     IO.mapOptional("SrcArgs", Propagation.SrcArgs);
-    IO.mapRequired("DstArgs", Propagation.DstArgs);
+    IO.mapOptional("DstArgs", Propagation.DstArgs);
     IO.mapOptional("VarType", Propagation.VarType,
                    GenericTaintChecker::VariadicType::None);
     IO.mapOptional("VarIndex", Propagation.VarIndex,
@@ -426,6 +434,10 @@
   // or smart memory copy:
   // - memccpy - copying until hitting a special character.
 
+  auto It = CustomPropagations.find(Name);
+  if (It != CustomPropagations.end())
+    return It->getValue();
+
   return TaintPropagationRule();
 }
 
@@ -538,6 +550,9 @@
   if (checkTaintedBufferSize(CE, FDecl, C))
     return true;
 
+  if (checkCustomSinks(CE, Name, C))
+    return true;
+
   return false;
 }
 
@@ -574,8 +589,12 @@
   // Check for taint in arguments.
   bool IsTainted = true;
   for (unsigned ArgNum : SrcArgs) {
-    if (ArgNum >= CE->getNumArgs())
-      return State;
+    if (ArgNum >= CE->getNumArgs()) {
+      StringRef Name = C.getCalleeName(C.getCalleeDecl(CE));
+      llvm::errs() << "Skip out of bound SrcArg: " << Name << ":" << ArgNum
+                   << '\n';
+      continue;
+    }
     if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(ArgNum), State, C)))
       break;
   }
@@ -603,8 +622,14 @@
       continue;
     }
 
+    if (ArgNum >= CE->getNumArgs()) {
+      StringRef Name = C.getCalleeName(C.getCalleeDecl(CE));
+      llvm::errs() << "Skip out of bound DstArg: " << Name << ":" << ArgNum
+                   << '\n';
+      continue;
+    }
+
     // Mark the given argument.
-    assert(ArgNum < CE->getNumArgs());
     State = State->add<TaintArgsOnPostVisit>(ArgNum);
   }
 
@@ -758,9 +783,9 @@
                         .Case("execvP", 0)
                         .Case("execve", 0)
                         .Case("dlopen", 0)
-                        .Default(UINT_MAX);
+                        .Default(InvalidArgIndex);
 
-  if (ArgNum == UINT_MAX || CE->getNumArgs() < (ArgNum + 1))
+  if (ArgNum == InvalidArgIndex || CE->getNumArgs() < (ArgNum + 1))
     return false;
 
   return generateReportIfTainted(CE->getArg(ArgNum), MsgSanitizeSystemArgs, C);
@@ -805,6 +830,27 @@
          generateReportIfTainted(CE->getArg(ArgNum), MsgTaintedBufferSize, C);
 }
 
+bool GenericTaintChecker::checkCustomSinks(const CallExpr *CE, StringRef Name,
+                                           CheckerContext &C) const {
+  auto It = CustomSinks.find(Name);
+  if (It == CustomSinks.end())
+    return false;
+
+  const GenericTaintChecker::ArgVector& Args = It->getValue();
+  for (unsigned ArgNum : Args) {
+    if (ArgNum >= CE->getNumArgs()) {
+      llvm::errs() << "Skip out of bound sink Arg: " << Name << ":" << ArgNum
+                   << '\n';
+      continue;
+    }
+
+    if (generateReportIfTainted(CE->getArg(ArgNum), MsgCustomSink, C))
+      return true;
+  }
+
+  return false;
+}
+
 void ento::registerGenericTaintChecker(CheckerManager &mgr) {
   const auto *Checker = mgr.registerChecker<GenericTaintChecker>();
   StringRef ConfigFile =
Index: test/Analysis/Inputs/taint-generic-config.yaml
===================================================================
--- /dev/null
+++ test/Analysis/Inputs/taint-generic-config.yaml
@@ -0,0 +1,51 @@
+# A list of source/propagation function
+Propagations:
+  # int x = mySource1(); // x is tainted
+  - Name:     mySource1
+    DstArgs:  [4294967294] # Index for return value
+
+  # int x;
+  # mySource2(&x); // x is tainted
+  - Name:     mySource2
+    DstArgs:  [0]
+
+  # int x, y;
+  # myScanf("%d %d", &x, &y); // x and y are tainted
+  - Name:     myScanf
+    VarType:  Dst
+    VarIndex: 1
+
+  # int x; // x is tainted
+  # int y;
+  # myPropagator(x, &y); // y is tainted
+  - Name:     myPropagator
+    SrcArgs:  [0]
+    DstArgs:  [1]
+
+  # const unsigned size = 100;
+  # char buf[size];
+  # int x, y;
+  # int n = mySprintf(buf, size, "%d %d", x, y); // If size, x or y is tainted
+  # // the return value and the buf will be tainted
+  - Name:     mySnprintf
+    SrcArgs:  [1]
+    DstArgs:  [0, 4294967294]
+    VarType:  Src
+    VarIndex: 3
+
+# A list of filter functions
+Filters:
+  # int x; // x is tainted
+  # myFilter(&x); // x is not tainted anymore
+  - Name: myFilter
+    Args: [0]
+
+# A list of sink functions
+Sinks:
+  # int x, y; // x and y are tainted
+  # mySink(x, 0, 1); // It will warn
+  # mySink(0, 1, y); // It will warn
+  # mySink(0, x, 1); // It won't warn
+  - Name: mySink
+    Args: [0, 2]
+
Index: test/Analysis/taint-generic.c
===================================================================
--- test/Analysis/taint-generic.c
+++ test/Analysis/taint-generic.c
@@ -1,5 +1,5 @@
-// RUN: %clang_analyze_cc1  -analyzer-checker=alpha.security.taint,core,alpha.security.ArrayBoundV2 -Wno-format-security -verify %s
-// RUN: %clang_analyze_cc1  -DFILE_IS_STRUCT -analyzer-checker=alpha.security.taint,core,alpha.security.ArrayBoundV2 -Wno-format-security -verify %s
+// RUN: %clang_analyze_cc1  -analyzer-checker=alpha.security.taint,core,alpha.security.ArrayBoundV2 -analyzer-config alpha.security.taint.TaintPropagation:Config=%S/Inputs/taint-generic-config.yaml -Wno-format-security -verify %s
+// RUN: %clang_analyze_cc1  -DFILE_IS_STRUCT -analyzer-checker=alpha.security.taint,core,alpha.security.ArrayBoundV2 -analyzer-config alpha.security.taint.TaintPropagation:Config=%S/Inputs/taint-generic-config.yaml -Wno-format-security -verify %s
 
 int scanf(const char *restrict format, ...);
 char *gets(char *str);
@@ -295,3 +295,43 @@
   if (i < rhs)
     *(volatile int *) 0; // no-warning
 }
+
+
+// Test configuration
+int mySource1();
+void mySource2(int*);
+void myScanf(const char*, ...);
+int myPropagator(int, int*);
+int mySnprintf(char*, size_t, const char*, ...);
+void mySink(int, int, int);
+
+void testConfigurationSources1() {
+  int x = mySource1();
+  Buffer[x] = 1; // expected-warning {{Out of bound memory access }}
+}
+
+void testConfigurationSources2() {
+  int x;
+  mySource2(&x);
+  Buffer[x] = 1; // expected-warning {{Out of bound memory access }}
+}
+
+void testConfigurationSources3() {
+  int x, y;
+  myScanf("%d %d", &x, &y);
+  Buffer[y] = 1; // expected-warning {{Out of bound memory access }}
+}
+
+void testConfigurationPropagation() {
+  int x = mySource1();
+  int y;
+  myPropagator(x, &y);
+  Buffer[y] = 1; // expected-warning {{Out of bound memory access }}
+}
+
+void testConfigurationSinks() {
+  int x = mySource1();
+  mySink(x, 1, 2); // expected-warning {{Untrusted data is passed to a user defined sink}}
+  mySink(1, x, 2); // no-warning
+  mySink(1, 2, x); // expected-warning {{Untrusted data is passed to a user defined sink}}
+}