Index: clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -132,6 +132,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; @@ -175,7 +180,8 @@ /// Get the propagation rule for a given function. static TaintPropagationRule - getTaintPropagationRule(const FunctionDecl *FDecl, StringRef Name, + getTaintPropagationRule(const GenericTaintChecker *Checker, + const FunctionDecl *FDecl, StringRef Name, CheckerContext &C); void addSrcArg(unsigned A) { SrcArgs.push_back(A); } @@ -240,6 +246,10 @@ "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)"; + +const char GenericTaintChecker::MsgCustomSink[] = + "Untrusted data is passed to a user defined sink"; +; } // end of anonymous namespace using TaintConfig = GenericTaintChecker::TaintConfiguration; @@ -330,7 +340,8 @@ GenericTaintChecker::TaintPropagationRule GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule( - const FunctionDecl *FDecl, StringRef Name, CheckerContext &C) { + const GenericTaintChecker *Checker, const FunctionDecl *FDecl, + StringRef Name, 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. @@ -424,6 +435,10 @@ // or smart memory copy: // - memccpy - copying until hitting a special character. + auto It = Checker->CustomPropagations.find(Name); + if (It != Checker->CustomPropagations.end()) + return It->getValue(); + return TaintPropagationRule(); } @@ -464,7 +479,7 @@ // First, try generating a propagation rule for this function. TaintPropagationRule Rule = - TaintPropagationRule::getTaintPropagationRule(FDecl, Name, C); + TaintPropagationRule::getTaintPropagationRule(this, FDecl, Name, C); if (!Rule.isNull()) { State = Rule.process(CE, C); if (!State) @@ -536,6 +551,9 @@ if (checkTaintedBufferSize(CE, FDecl, C)) return true; + if (checkCustomSinks(CE, Name, C)) + return true; + return false; } @@ -572,8 +590,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; } @@ -601,8 +623,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(ArgNum); } @@ -756,9 +784,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); @@ -803,6 +831,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) { auto *Checker = Mgr.registerChecker(); std::string Option{"Config"}; Index: clang/test/Analysis/taint-generic.c =================================================================== --- clang/test/Analysis/taint-generic.c +++ clang/test/Analysis/taint-generic.c @@ -338,3 +338,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}} +}