diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp | 1036 |
1 files changed, 0 insertions, 1036 deletions
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp deleted file mode 100644 index 3f1c213a5647..000000000000 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ /dev/null @@ -1,1036 +0,0 @@ -//==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines a set of flow-insensitive security checks. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" -#include "clang/AST/StmtVisitor.h" -#include "clang/Analysis/AnalysisDeclContext.h" -#include "clang/Basic/TargetInfo.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/StringSwitch.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; -using namespace ento; - -static bool isArc4RandomAvailable(const ASTContext &Ctx) { - const llvm::Triple &T = Ctx.getTargetInfo().getTriple(); - return T.getVendor() == llvm::Triple::Apple || - T.getOS() == llvm::Triple::CloudABI || - T.isOSFreeBSD() || - T.isOSNetBSD() || - T.isOSOpenBSD() || - T.isOSDragonFly(); -} - -namespace { -struct ChecksFilter { - DefaultBool check_bcmp; - DefaultBool check_bcopy; - DefaultBool check_bzero; - DefaultBool check_gets; - DefaultBool check_getpw; - DefaultBool check_mktemp; - DefaultBool check_mkstemp; - DefaultBool check_strcpy; - DefaultBool check_DeprecatedOrUnsafeBufferHandling; - DefaultBool check_rand; - DefaultBool check_vfork; - DefaultBool check_FloatLoopCounter; - DefaultBool check_UncheckedReturn; - - CheckName checkName_bcmp; - CheckName checkName_bcopy; - CheckName checkName_bzero; - CheckName checkName_gets; - CheckName checkName_getpw; - CheckName checkName_mktemp; - CheckName checkName_mkstemp; - CheckName checkName_strcpy; - CheckName checkName_DeprecatedOrUnsafeBufferHandling; - CheckName checkName_rand; - CheckName checkName_vfork; - CheckName checkName_FloatLoopCounter; - CheckName checkName_UncheckedReturn; -}; - -class WalkAST : public StmtVisitor<WalkAST> { - BugReporter &BR; - AnalysisDeclContext* AC; - enum { num_setids = 6 }; - IdentifierInfo *II_setid[num_setids]; - - const bool CheckRand; - const ChecksFilter &filter; - -public: - WalkAST(BugReporter &br, AnalysisDeclContext* ac, - const ChecksFilter &f) - : BR(br), AC(ac), II_setid(), - CheckRand(isArc4RandomAvailable(BR.getContext())), - filter(f) {} - - // Statement visitor methods. - void VisitCallExpr(CallExpr *CE); - void VisitForStmt(ForStmt *S); - void VisitCompoundStmt (CompoundStmt *S); - void VisitStmt(Stmt *S) { VisitChildren(S); } - - void VisitChildren(Stmt *S); - - // Helpers. - bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD); - - typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *); - - // Checker-specific methods. - void checkLoopConditionForFloat(const ForStmt *FS); - void checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD); - void checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD); - void checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD); - void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD); - void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD); - void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD); - void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD); - void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD); - void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD); - void checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, - const FunctionDecl *FD); - void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD); - void checkCall_random(const CallExpr *CE, const FunctionDecl *FD); - void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD); - void checkUncheckedReturnValue(CallExpr *CE); -}; -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// AST walking. -//===----------------------------------------------------------------------===// - -void WalkAST::VisitChildren(Stmt *S) { - for (Stmt *Child : S->children()) - if (Child) - Visit(Child); -} - -void WalkAST::VisitCallExpr(CallExpr *CE) { - // Get the callee. - const FunctionDecl *FD = CE->getDirectCallee(); - - if (!FD) - return; - - // Get the name of the callee. If it's a builtin, strip off the prefix. - IdentifierInfo *II = FD->getIdentifier(); - if (!II) // if no identifier, not a simple C function - return; - StringRef Name = II->getName(); - if (Name.startswith("__builtin_")) - Name = Name.substr(10); - - // Set the evaluation function by switching on the callee name. - FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) - .Case("bcmp", &WalkAST::checkCall_bcmp) - .Case("bcopy", &WalkAST::checkCall_bcopy) - .Case("bzero", &WalkAST::checkCall_bzero) - .Case("gets", &WalkAST::checkCall_gets) - .Case("getpw", &WalkAST::checkCall_getpw) - .Case("mktemp", &WalkAST::checkCall_mktemp) - .Case("mkstemp", &WalkAST::checkCall_mkstemp) - .Case("mkdtemp", &WalkAST::checkCall_mkstemp) - .Case("mkstemps", &WalkAST::checkCall_mkstemp) - .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy) - .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat) - .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf", - "vscanf", "vwscanf", "vfscanf", "vfwscanf", - &WalkAST::checkDeprecatedOrUnsafeBufferHandling) - .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf", - "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove", - &WalkAST::checkDeprecatedOrUnsafeBufferHandling) - .Cases("strncpy", "strncat", "memset", - &WalkAST::checkDeprecatedOrUnsafeBufferHandling) - .Case("drand48", &WalkAST::checkCall_rand) - .Case("erand48", &WalkAST::checkCall_rand) - .Case("jrand48", &WalkAST::checkCall_rand) - .Case("lrand48", &WalkAST::checkCall_rand) - .Case("mrand48", &WalkAST::checkCall_rand) - .Case("nrand48", &WalkAST::checkCall_rand) - .Case("lcong48", &WalkAST::checkCall_rand) - .Case("rand", &WalkAST::checkCall_rand) - .Case("rand_r", &WalkAST::checkCall_rand) - .Case("random", &WalkAST::checkCall_random) - .Case("vfork", &WalkAST::checkCall_vfork) - .Default(nullptr); - - // If the callee isn't defined, it is not of security concern. - // Check and evaluate the call. - if (evalFunction) - (this->*evalFunction)(CE, FD); - - // Recurse and check children. - VisitChildren(CE); -} - -void WalkAST::VisitCompoundStmt(CompoundStmt *S) { - for (Stmt *Child : S->children()) - if (Child) { - if (CallExpr *CE = dyn_cast<CallExpr>(Child)) - checkUncheckedReturnValue(CE); - Visit(Child); - } -} - -void WalkAST::VisitForStmt(ForStmt *FS) { - checkLoopConditionForFloat(FS); - - // Recurse and check children. - VisitChildren(FS); -} - -//===----------------------------------------------------------------------===// -// Check: floating point variable used as loop counter. -// Originally: <rdar://problem/6336718> -// Implements: CERT security coding advisory FLP-30. -//===----------------------------------------------------------------------===// - -static const DeclRefExpr* -getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) { - expr = expr->IgnoreParenCasts(); - - if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) { - if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() || - B->getOpcode() == BO_Comma)) - return nullptr; - - if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y)) - return lhs; - - if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y)) - return rhs; - - return nullptr; - } - - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) { - const NamedDecl *ND = DR->getDecl(); - return ND == x || ND == y ? DR : nullptr; - } - - if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr)) - return U->isIncrementDecrementOp() - ? getIncrementedVar(U->getSubExpr(), x, y) : nullptr; - - return nullptr; -} - -/// CheckLoopConditionForFloat - This check looks for 'for' statements that -/// use a floating point variable as a loop counter. -/// CERT: FLP30-C, FLP30-CPP. -/// -void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) { - if (!filter.check_FloatLoopCounter) - return; - - // Does the loop have a condition? - const Expr *condition = FS->getCond(); - - if (!condition) - return; - - // Does the loop have an increment? - const Expr *increment = FS->getInc(); - - if (!increment) - return; - - // Strip away '()' and casts. - condition = condition->IgnoreParenCasts(); - increment = increment->IgnoreParenCasts(); - - // Is the loop condition a comparison? - const BinaryOperator *B = dyn_cast<BinaryOperator>(condition); - - if (!B) - return; - - // Is this a comparison? - if (!(B->isRelationalOp() || B->isEqualityOp())) - return; - - // Are we comparing variables? - const DeclRefExpr *drLHS = - dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts()); - const DeclRefExpr *drRHS = - dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts()); - - // Does at least one of the variables have a floating point type? - drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : nullptr; - drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : nullptr; - - if (!drLHS && !drRHS) - return; - - const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : nullptr; - const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : nullptr; - - if (!vdLHS && !vdRHS) - return; - - // Does either variable appear in increment? - const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS); - - if (!drInc) - return; - - // Emit the error. First figure out which DeclRefExpr in the condition - // referenced the compared variable. - assert(drInc->getDecl()); - const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS; - - SmallVector<SourceRange, 2> ranges; - SmallString<256> sbuf; - llvm::raw_svector_ostream os(sbuf); - - os << "Variable '" << drCond->getDecl()->getName() - << "' with floating point type '" << drCond->getType().getAsString() - << "' should not be used as a loop counter"; - - ranges.push_back(drCond->getSourceRange()); - ranges.push_back(drInc->getSourceRange()); - - const char *bugType = "Floating point variable used as loop counter"; - - PathDiagnosticLocation FSLoc = - PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter, - bugType, "Security", os.str(), - FSLoc, ranges); -} - -//===----------------------------------------------------------------------===// -// Check: Any use of bcmp. -// CWE-477: Use of Obsolete Functions -// bcmp was deprecated in POSIX.1-2008 -//===----------------------------------------------------------------------===// - -void WalkAST::checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD) { - if (!filter.check_bcmp) - return; - - const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); - if (!FPT) - return; - - // Verify that the function takes three arguments. - if (FPT->getNumParams() != 3) - return; - - for (int i = 0; i < 2; i++) { - // Verify the first and second argument type is void*. - const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>(); - if (!PT) - return; - - if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy) - return; - } - - // Verify the third argument type is integer. - if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType()) - return; - - // Issue a warning. - PathDiagnosticLocation CELoc = - PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp, - "Use of deprecated function in call to 'bcmp()'", - "Security", - "The bcmp() function is obsoleted by memcmp().", - CELoc, CE->getCallee()->getSourceRange()); -} - -//===----------------------------------------------------------------------===// -// Check: Any use of bcopy. -// CWE-477: Use of Obsolete Functions -// bcopy was deprecated in POSIX.1-2008 -//===----------------------------------------------------------------------===// - -void WalkAST::checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD) { - if (!filter.check_bcopy) - return; - - const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); - if (!FPT) - return; - - // Verify that the function takes three arguments. - if (FPT->getNumParams() != 3) - return; - - for (int i = 0; i < 2; i++) { - // Verify the first and second argument type is void*. - const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>(); - if (!PT) - return; - - if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy) - return; - } - - // Verify the third argument type is integer. - if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType()) - return; - - // Issue a warning. - PathDiagnosticLocation CELoc = - PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy, - "Use of deprecated function in call to 'bcopy()'", - "Security", - "The bcopy() function is obsoleted by memcpy() " - "or memmove().", - CELoc, CE->getCallee()->getSourceRange()); -} - -//===----------------------------------------------------------------------===// -// Check: Any use of bzero. -// CWE-477: Use of Obsolete Functions -// bzero was deprecated in POSIX.1-2008 -//===----------------------------------------------------------------------===// - -void WalkAST::checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD) { - if (!filter.check_bzero) - return; - - const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); - if (!FPT) - return; - - // Verify that the function takes two arguments. - if (FPT->getNumParams() != 2) - return; - - // Verify the first argument type is void*. - const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>(); - if (!PT) - return; - - if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy) - return; - - // Verify the second argument type is integer. - if (!FPT->getParamType(1)->isIntegralOrUnscopedEnumerationType()) - return; - - // Issue a warning. - PathDiagnosticLocation CELoc = - PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero, - "Use of deprecated function in call to 'bzero()'", - "Security", - "The bzero() function is obsoleted by memset().", - CELoc, CE->getCallee()->getSourceRange()); -} - - -//===----------------------------------------------------------------------===// -// Check: Any use of 'gets' is insecure. -// Originally: <rdar://problem/6335715> -// Implements (part of): 300-BSI (buildsecurityin.us-cert.gov) -// CWE-242: Use of Inherently Dangerous Function -//===----------------------------------------------------------------------===// - -void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) { - if (!filter.check_gets) - return; - - const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); - if (!FPT) - return; - - // Verify that the function takes a single argument. - if (FPT->getNumParams() != 1) - return; - - // Is the argument a 'char*'? - const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>(); - if (!PT) - return; - - if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) - return; - - // Issue a warning. - PathDiagnosticLocation CELoc = - PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets, - "Potential buffer overflow in call to 'gets'", - "Security", - "Call to function 'gets' is extremely insecure as it can " - "always result in a buffer overflow", - CELoc, CE->getCallee()->getSourceRange()); -} - -//===----------------------------------------------------------------------===// -// Check: Any use of 'getpwd' is insecure. -// CWE-477: Use of Obsolete Functions -//===----------------------------------------------------------------------===// - -void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { - if (!filter.check_getpw) - return; - - const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); - if (!FPT) - return; - - // Verify that the function takes two arguments. - if (FPT->getNumParams() != 2) - return; - - // Verify the first argument type is integer. - if (!FPT->getParamType(0)->isIntegralOrUnscopedEnumerationType()) - return; - - // Verify the second argument type is char*. - const PointerType *PT = FPT->getParamType(1)->getAs<PointerType>(); - if (!PT) - return; - - if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) - return; - - // Issue a warning. - PathDiagnosticLocation CELoc = - PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw, - "Potential buffer overflow in call to 'getpw'", - "Security", - "The getpw() function is dangerous as it may overflow the " - "provided buffer. It is obsoleted by getpwuid().", - CELoc, CE->getCallee()->getSourceRange()); -} - -//===----------------------------------------------------------------------===// -// Check: Any use of 'mktemp' is insecure. It is obsoleted by mkstemp(). -// CWE-377: Insecure Temporary File -//===----------------------------------------------------------------------===// - -void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { - if (!filter.check_mktemp) { - // Fall back to the security check of looking for enough 'X's in the - // format string, since that is a less severe warning. - checkCall_mkstemp(CE, FD); - return; - } - - const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); - if(!FPT) - return; - - // Verify that the function takes a single argument. - if (FPT->getNumParams() != 1) - return; - - // Verify that the argument is Pointer Type. - const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>(); - if (!PT) - return; - - // Verify that the argument is a 'char*'. - if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) - return; - - // Issue a warning. - PathDiagnosticLocation CELoc = - PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp, - "Potential insecure temporary file in call 'mktemp'", - "Security", - "Call to function 'mktemp' is insecure as it always " - "creates or uses insecure temporary file. Use 'mkstemp' " - "instead", - CELoc, CE->getCallee()->getSourceRange()); -} - -//===----------------------------------------------------------------------===// -// Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's. -//===----------------------------------------------------------------------===// - -void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) { - if (!filter.check_mkstemp) - return; - - StringRef Name = FD->getIdentifier()->getName(); - std::pair<signed, signed> ArgSuffix = - llvm::StringSwitch<std::pair<signed, signed> >(Name) - .Case("mktemp", std::make_pair(0,-1)) - .Case("mkstemp", std::make_pair(0,-1)) - .Case("mkdtemp", std::make_pair(0,-1)) - .Case("mkstemps", std::make_pair(0,1)) - .Default(std::make_pair(-1, -1)); - - assert(ArgSuffix.first >= 0 && "Unsupported function"); - - // Check if the number of arguments is consistent with out expectations. - unsigned numArgs = CE->getNumArgs(); - if ((signed) numArgs <= ArgSuffix.first) - return; - - const StringLiteral *strArg = - dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first) - ->IgnoreParenImpCasts()); - - // Currently we only handle string literals. It is possible to do better, - // either by looking at references to const variables, or by doing real - // flow analysis. - if (!strArg || strArg->getCharByteWidth() != 1) - return; - - // Count the number of X's, taking into account a possible cutoff suffix. - StringRef str = strArg->getString(); - unsigned numX = 0; - unsigned n = str.size(); - - // Take into account the suffix. - unsigned suffix = 0; - if (ArgSuffix.second >= 0) { - const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second); - Expr::EvalResult EVResult; - if (!suffixEx->EvaluateAsInt(EVResult, BR.getContext())) - return; - llvm::APSInt Result = EVResult.Val.getInt(); - // FIXME: Issue a warning. - if (Result.isNegative()) - return; - suffix = (unsigned) Result.getZExtValue(); - n = (n > suffix) ? n - suffix : 0; - } - - for (unsigned i = 0; i < n; ++i) - if (str[i] == 'X') ++numX; - - if (numX >= 6) - return; - - // Issue a warning. - PathDiagnosticLocation CELoc = - PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - SmallString<512> buf; - llvm::raw_svector_ostream out(buf); - out << "Call to '" << Name << "' should have at least 6 'X's in the" - " format string to be secure (" << numX << " 'X'"; - if (numX != 1) - out << 's'; - out << " seen"; - if (suffix) { - out << ", " << suffix << " character"; - if (suffix > 1) - out << 's'; - out << " used as a suffix"; - } - out << ')'; - BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp, - "Insecure temporary file creation", "Security", - out.str(), CELoc, strArg->getSourceRange()); -} - -//===----------------------------------------------------------------------===// -// Check: Any use of 'strcpy' is insecure. -// -// CWE-119: Improper Restriction of Operations within -// the Bounds of a Memory Buffer -//===----------------------------------------------------------------------===// - -void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) { - if (!filter.check_strcpy) - return; - - if (!checkCall_strCommon(CE, FD)) - return; - - const auto *Target = CE->getArg(0)->IgnoreImpCasts(), - *Source = CE->getArg(1)->IgnoreImpCasts(); - - if (const auto *Array = dyn_cast<ConstantArrayType>(Target->getType())) { - uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8; - if (const auto *String = dyn_cast<StringLiteral>(Source)) { - if (ArraySize >= String->getLength() + 1) - return; - } - } - - // Issue a warning. - PathDiagnosticLocation CELoc = - PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy, - "Potential insecure memory buffer bounds restriction in " - "call 'strcpy'", - "Security", - "Call to function 'strcpy' is insecure as it does not " - "provide bounding of the memory buffer. Replace " - "unbounded copy functions with analogous functions that " - "support length arguments such as 'strlcpy'. CWE-119.", - CELoc, CE->getCallee()->getSourceRange()); -} - -//===----------------------------------------------------------------------===// -// Check: Any use of 'strcat' is insecure. -// -// CWE-119: Improper Restriction of Operations within -// the Bounds of a Memory Buffer -//===----------------------------------------------------------------------===// - -void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) { - if (!filter.check_strcpy) - return; - - if (!checkCall_strCommon(CE, FD)) - return; - - // Issue a warning. - PathDiagnosticLocation CELoc = - PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy, - "Potential insecure memory buffer bounds restriction in " - "call 'strcat'", - "Security", - "Call to function 'strcat' is insecure as it does not " - "provide bounding of the memory buffer. Replace " - "unbounded copy functions with analogous functions that " - "support length arguments such as 'strlcat'. CWE-119.", - CELoc, CE->getCallee()->getSourceRange()); -} - -//===----------------------------------------------------------------------===// -// Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf', -// 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf', -// 'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf', -// 'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset' -// is deprecated since C11. -// -// Use of 'sprintf', 'vsprintf', 'scanf', 'wscanf','fscanf', -// 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf', -// 'swscanf', 'vsscanf', 'vswscanf' without buffer limitations -// is insecure. -// -// CWE-119: Improper Restriction of Operations within -// the Bounds of a Memory Buffer -//===----------------------------------------------------------------------===// - -void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, - const FunctionDecl *FD) { - if (!filter.check_DeprecatedOrUnsafeBufferHandling) - return; - - if (!BR.getContext().getLangOpts().C11) - return; - - // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size - // restrictions). - enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 }; - - StringRef Name = FD->getIdentifier()->getName(); - if (Name.startswith("__builtin_")) - Name = Name.substr(10); - - int ArgIndex = - llvm::StringSwitch<int>(Name) - .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0) - .Cases("sprintf", "vsprintf", "fscanf", "fwscanf", "vfscanf", - "vfwscanf", "sscanf", "swscanf", "vsscanf", "vswscanf", 1) - .Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy", - "memmove", "memset", "strncpy", "strncat", DEPR_ONLY) - .Default(UNKNOWN_CALL); - - assert(ArgIndex != UNKNOWN_CALL && "Unsupported function"); - bool BoundsProvided = ArgIndex == DEPR_ONLY; - - if (!BoundsProvided) { - // Currently we only handle (not wide) string literals. It is possible to do - // better, either by looking at references to const variables, or by doing - // real flow analysis. - auto FormatString = - dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts()); - if (FormatString && - FormatString->getString().find("%s") == StringRef::npos && - FormatString->getString().find("%[") == StringRef::npos) - BoundsProvided = true; - } - - SmallString<128> Buf1; - SmallString<512> Buf2; - llvm::raw_svector_ostream Out1(Buf1); - llvm::raw_svector_ostream Out2(Buf2); - - Out1 << "Potential insecure memory buffer bounds restriction in call '" - << Name << "'"; - Out2 << "Call to function '" << Name - << "' is insecure as it does not provide "; - - if (!BoundsProvided) { - Out2 << "bounding of the memory buffer or "; - } - - Out2 << "security checks introduced " - "in the C11 standard. Replace with analogous functions that " - "support length arguments or provides boundary checks such as '" - << Name << "_s' in case of C11"; - - PathDiagnosticLocation CELoc = - PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), - filter.checkName_DeprecatedOrUnsafeBufferHandling, - Out1.str(), "Security", Out2.str(), CELoc, - CE->getCallee()->getSourceRange()); -} - -//===----------------------------------------------------------------------===// -// Common check for str* functions with no bounds parameters. -//===----------------------------------------------------------------------===// - -bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) { - const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); - if (!FPT) - return false; - - // Verify the function takes two arguments, three in the _chk version. - int numArgs = FPT->getNumParams(); - if (numArgs != 2 && numArgs != 3) - return false; - - // Verify the type for both arguments. - for (int i = 0; i < 2; i++) { - // Verify that the arguments are pointers. - const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>(); - if (!PT) - return false; - - // Verify that the argument is a 'char*'. - if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) - return false; - } - - return true; -} - -//===----------------------------------------------------------------------===// -// Check: Linear congruent random number generators should not be used -// Originally: <rdar://problem/63371000> -// CWE-338: Use of cryptographically weak prng -//===----------------------------------------------------------------------===// - -void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) { - if (!filter.check_rand || !CheckRand) - return; - - const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>(); - if (!FTP) - return; - - if (FTP->getNumParams() == 1) { - // Is the argument an 'unsigned short *'? - // (Actually any integer type is allowed.) - const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>(); - if (!PT) - return; - - if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType()) - return; - } else if (FTP->getNumParams() != 0) - return; - - // Issue a warning. - SmallString<256> buf1; - llvm::raw_svector_ostream os1(buf1); - os1 << '\'' << *FD << "' is a poor random number generator"; - - SmallString<256> buf2; - llvm::raw_svector_ostream os2(buf2); - os2 << "Function '" << *FD - << "' is obsolete because it implements a poor random number generator." - << " Use 'arc4random' instead"; - - PathDiagnosticLocation CELoc = - PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(), - "Security", os2.str(), CELoc, - CE->getCallee()->getSourceRange()); -} - -//===----------------------------------------------------------------------===// -// Check: 'random' should not be used -// Originally: <rdar://problem/63371000> -//===----------------------------------------------------------------------===// - -void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) { - if (!CheckRand || !filter.check_rand) - return; - - const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>(); - if (!FTP) - return; - - // Verify that the function takes no argument. - if (FTP->getNumParams() != 0) - return; - - // Issue a warning. - PathDiagnosticLocation CELoc = - PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, - "'random' is not a secure random number generator", - "Security", - "The 'random' function produces a sequence of values that " - "an adversary may be able to predict. Use 'arc4random' " - "instead", CELoc, CE->getCallee()->getSourceRange()); -} - -//===----------------------------------------------------------------------===// -// Check: 'vfork' should not be used. -// POS33-C: Do not use vfork(). -//===----------------------------------------------------------------------===// - -void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) { - if (!filter.check_vfork) - return; - - // All calls to vfork() are insecure, issue a warning. - PathDiagnosticLocation CELoc = - PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork, - "Potential insecure implementation-specific behavior in " - "call 'vfork'", - "Security", - "Call to function 'vfork' is insecure as it can lead to " - "denial of service situations in the parent process. " - "Replace calls to vfork with calls to the safer " - "'posix_spawn' function", - CELoc, CE->getCallee()->getSourceRange()); -} - -//===----------------------------------------------------------------------===// -// Check: Should check whether privileges are dropped successfully. -// Originally: <rdar://problem/6337132> -//===----------------------------------------------------------------------===// - -void WalkAST::checkUncheckedReturnValue(CallExpr *CE) { - if (!filter.check_UncheckedReturn) - return; - - const FunctionDecl *FD = CE->getDirectCallee(); - if (!FD) - return; - - if (II_setid[0] == nullptr) { - static const char * const identifiers[num_setids] = { - "setuid", "setgid", "seteuid", "setegid", - "setreuid", "setregid" - }; - - for (size_t i = 0; i < num_setids; i++) - II_setid[i] = &BR.getContext().Idents.get(identifiers[i]); - } - - const IdentifierInfo *id = FD->getIdentifier(); - size_t identifierid; - - for (identifierid = 0; identifierid < num_setids; identifierid++) - if (id == II_setid[identifierid]) - break; - - if (identifierid >= num_setids) - return; - - const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>(); - if (!FTP) - return; - - // Verify that the function takes one or two arguments (depending on - // the function). - if (FTP->getNumParams() != (identifierid < 4 ? 1 : 2)) - return; - - // The arguments must be integers. - for (unsigned i = 0; i < FTP->getNumParams(); i++) - if (!FTP->getParamType(i)->isIntegralOrUnscopedEnumerationType()) - return; - - // Issue a warning. - SmallString<256> buf1; - llvm::raw_svector_ostream os1(buf1); - os1 << "Return value is not checked in call to '" << *FD << '\''; - - SmallString<256> buf2; - llvm::raw_svector_ostream os2(buf2); - os2 << "The return value from the call to '" << *FD - << "' is not checked. If an error occurs in '" << *FD - << "', the following code may execute with unexpected privileges"; - - PathDiagnosticLocation CELoc = - PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(), - "Security", os2.str(), CELoc, - CE->getCallee()->getSourceRange()); -} - -//===----------------------------------------------------------------------===// -// SecuritySyntaxChecker -//===----------------------------------------------------------------------===// - -namespace { -class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> { -public: - ChecksFilter filter; - - void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, - BugReporter &BR) const { - WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter); - walker.Visit(D->getBody()); - } -}; -} - -void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) { - mgr.registerChecker<SecuritySyntaxChecker>(); -} - -bool ento::shouldRegisterSecuritySyntaxChecker(const LangOptions &LO) { - return true; -} - -#define REGISTER_CHECKER(name) \ - void ento::register##name(CheckerManager &mgr) { \ - SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ - checker->filter.check_##name = true; \ - checker->filter.checkName_##name = mgr.getCurrentCheckName(); \ - } \ - \ - bool ento::shouldRegister##name(const LangOptions &LO) { \ - return true; \ - } - -REGISTER_CHECKER(bcmp) -REGISTER_CHECKER(bcopy) -REGISTER_CHECKER(bzero) -REGISTER_CHECKER(gets) -REGISTER_CHECKER(getpw) -REGISTER_CHECKER(mkstemp) -REGISTER_CHECKER(mktemp) -REGISTER_CHECKER(strcpy) -REGISTER_CHECKER(rand) -REGISTER_CHECKER(vfork) -REGISTER_CHECKER(FloatLoopCounter) -REGISTER_CHECKER(UncheckedReturn) -REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling) |