Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
35266 views
//==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file defines a set of flow-insensitive security checks.9//10//===----------------------------------------------------------------------===//1112#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"13#include "clang/AST/StmtVisitor.h"14#include "clang/Analysis/AnalysisDeclContext.h"15#include "clang/Basic/TargetInfo.h"16#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"17#include "clang/StaticAnalyzer/Core/Checker.h"18#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"19#include "llvm/ADT/SmallString.h"20#include "llvm/ADT/StringSwitch.h"21#include "llvm/Support/raw_ostream.h"2223using namespace clang;24using namespace ento;2526static bool isArc4RandomAvailable(const ASTContext &Ctx) {27const llvm::Triple &T = Ctx.getTargetInfo().getTriple();28return T.getVendor() == llvm::Triple::Apple ||29T.isOSFreeBSD() ||30T.isOSNetBSD() ||31T.isOSOpenBSD() ||32T.isOSDragonFly();33}3435namespace {36struct ChecksFilter {37bool check_bcmp = false;38bool check_bcopy = false;39bool check_bzero = false;40bool check_gets = false;41bool check_getpw = false;42bool check_mktemp = false;43bool check_mkstemp = false;44bool check_strcpy = false;45bool check_DeprecatedOrUnsafeBufferHandling = false;46bool check_rand = false;47bool check_vfork = false;48bool check_FloatLoopCounter = false;49bool check_UncheckedReturn = false;50bool check_decodeValueOfObjCType = false;5152CheckerNameRef checkName_bcmp;53CheckerNameRef checkName_bcopy;54CheckerNameRef checkName_bzero;55CheckerNameRef checkName_gets;56CheckerNameRef checkName_getpw;57CheckerNameRef checkName_mktemp;58CheckerNameRef checkName_mkstemp;59CheckerNameRef checkName_strcpy;60CheckerNameRef checkName_DeprecatedOrUnsafeBufferHandling;61CheckerNameRef checkName_rand;62CheckerNameRef checkName_vfork;63CheckerNameRef checkName_FloatLoopCounter;64CheckerNameRef checkName_UncheckedReturn;65CheckerNameRef checkName_decodeValueOfObjCType;66};6768class WalkAST : public StmtVisitor<WalkAST> {69BugReporter &BR;70AnalysisDeclContext* AC;71enum { num_setids = 6 };72IdentifierInfo *II_setid[num_setids];7374const bool CheckRand;75const ChecksFilter &filter;7677public:78WalkAST(BugReporter &br, AnalysisDeclContext* ac,79const ChecksFilter &f)80: BR(br), AC(ac), II_setid(),81CheckRand(isArc4RandomAvailable(BR.getContext())),82filter(f) {}8384// Statement visitor methods.85void VisitCallExpr(CallExpr *CE);86void VisitObjCMessageExpr(ObjCMessageExpr *CE);87void VisitForStmt(ForStmt *S);88void VisitCompoundStmt (CompoundStmt *S);89void VisitStmt(Stmt *S) { VisitChildren(S); }9091void VisitChildren(Stmt *S);9293// Helpers.94bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD);9596typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *);97typedef void (WalkAST::*MsgCheck)(const ObjCMessageExpr *);9899// Checker-specific methods.100void checkLoopConditionForFloat(const ForStmt *FS);101void checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD);102void checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD);103void checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD);104void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD);105void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD);106void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD);107void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD);108void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD);109void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD);110void checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,111const FunctionDecl *FD);112void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD);113void checkCall_random(const CallExpr *CE, const FunctionDecl *FD);114void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD);115void checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME);116void checkUncheckedReturnValue(CallExpr *CE);117};118} // end anonymous namespace119120//===----------------------------------------------------------------------===//121// AST walking.122//===----------------------------------------------------------------------===//123124void WalkAST::VisitChildren(Stmt *S) {125for (Stmt *Child : S->children())126if (Child)127Visit(Child);128}129130void WalkAST::VisitCallExpr(CallExpr *CE) {131// Get the callee.132const FunctionDecl *FD = CE->getDirectCallee();133134if (!FD)135return;136137// Get the name of the callee. If it's a builtin, strip off the prefix.138IdentifierInfo *II = FD->getIdentifier();139if (!II) // if no identifier, not a simple C function140return;141StringRef Name = II->getName();142Name.consume_front("__builtin_");143144// Set the evaluation function by switching on the callee name.145FnCheck evalFunction =146llvm::StringSwitch<FnCheck>(Name)147.Case("bcmp", &WalkAST::checkCall_bcmp)148.Case("bcopy", &WalkAST::checkCall_bcopy)149.Case("bzero", &WalkAST::checkCall_bzero)150.Case("gets", &WalkAST::checkCall_gets)151.Case("getpw", &WalkAST::checkCall_getpw)152.Case("mktemp", &WalkAST::checkCall_mktemp)153.Case("mkstemp", &WalkAST::checkCall_mkstemp)154.Case("mkdtemp", &WalkAST::checkCall_mkstemp)155.Case("mkstemps", &WalkAST::checkCall_mkstemp)156.Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy)157.Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat)158.Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf",159"vscanf", "vwscanf", "vfscanf", "vfwscanf",160&WalkAST::checkDeprecatedOrUnsafeBufferHandling)161.Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf",162"snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove",163&WalkAST::checkDeprecatedOrUnsafeBufferHandling)164.Cases("strncpy", "strncat", "memset", "fprintf",165&WalkAST::checkDeprecatedOrUnsafeBufferHandling)166.Case("drand48", &WalkAST::checkCall_rand)167.Case("erand48", &WalkAST::checkCall_rand)168.Case("jrand48", &WalkAST::checkCall_rand)169.Case("lrand48", &WalkAST::checkCall_rand)170.Case("mrand48", &WalkAST::checkCall_rand)171.Case("nrand48", &WalkAST::checkCall_rand)172.Case("lcong48", &WalkAST::checkCall_rand)173.Case("rand", &WalkAST::checkCall_rand)174.Case("rand_r", &WalkAST::checkCall_rand)175.Case("random", &WalkAST::checkCall_random)176.Case("vfork", &WalkAST::checkCall_vfork)177.Default(nullptr);178179// If the callee isn't defined, it is not of security concern.180// Check and evaluate the call.181if (evalFunction)182(this->*evalFunction)(CE, FD);183184// Recurse and check children.185VisitChildren(CE);186}187188void WalkAST::VisitObjCMessageExpr(ObjCMessageExpr *ME) {189MsgCheck evalFunction =190llvm::StringSwitch<MsgCheck>(ME->getSelector().getAsString())191.Case("decodeValueOfObjCType:at:",192&WalkAST::checkMsg_decodeValueOfObjCType)193.Default(nullptr);194195if (evalFunction)196(this->*evalFunction)(ME);197198// Recurse and check children.199VisitChildren(ME);200}201202void WalkAST::VisitCompoundStmt(CompoundStmt *S) {203for (Stmt *Child : S->children())204if (Child) {205if (CallExpr *CE = dyn_cast<CallExpr>(Child))206checkUncheckedReturnValue(CE);207Visit(Child);208}209}210211void WalkAST::VisitForStmt(ForStmt *FS) {212checkLoopConditionForFloat(FS);213214// Recurse and check children.215VisitChildren(FS);216}217218//===----------------------------------------------------------------------===//219// Check: floating point variable used as loop counter.220// Implements: CERT security coding advisory FLP-30.221//===----------------------------------------------------------------------===//222223// Returns either 'x' or 'y', depending on which one of them is incremented224// in 'expr', or nullptr if none of them is incremented.225static const DeclRefExpr*226getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) {227expr = expr->IgnoreParenCasts();228229if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) {230if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||231B->getOpcode() == BO_Comma))232return nullptr;233234if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y))235return lhs;236237if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y))238return rhs;239240return nullptr;241}242243if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {244const NamedDecl *ND = DR->getDecl();245return ND == x || ND == y ? DR : nullptr;246}247248if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr))249return U->isIncrementDecrementOp()250? getIncrementedVar(U->getSubExpr(), x, y) : nullptr;251252return nullptr;253}254255/// CheckLoopConditionForFloat - This check looks for 'for' statements that256/// use a floating point variable as a loop counter.257/// CERT: FLP30-C, FLP30-CPP.258///259void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {260if (!filter.check_FloatLoopCounter)261return;262263// Does the loop have a condition?264const Expr *condition = FS->getCond();265266if (!condition)267return;268269// Does the loop have an increment?270const Expr *increment = FS->getInc();271272if (!increment)273return;274275// Strip away '()' and casts.276condition = condition->IgnoreParenCasts();277increment = increment->IgnoreParenCasts();278279// Is the loop condition a comparison?280const BinaryOperator *B = dyn_cast<BinaryOperator>(condition);281282if (!B)283return;284285// Is this a comparison?286if (!(B->isRelationalOp() || B->isEqualityOp()))287return;288289// Are we comparing variables?290const DeclRefExpr *drLHS =291dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts());292const DeclRefExpr *drRHS =293dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts());294295// Does at least one of the variables have a floating point type?296drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : nullptr;297drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : nullptr;298299if (!drLHS && !drRHS)300return;301302const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : nullptr;303const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : nullptr;304305if (!vdLHS && !vdRHS)306return;307308// Does either variable appear in increment?309const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS);310if (!drInc)311return;312313const VarDecl *vdInc = cast<VarDecl>(drInc->getDecl());314assert(vdInc && (vdInc == vdLHS || vdInc == vdRHS));315316// Emit the error. First figure out which DeclRefExpr in the condition317// referenced the compared variable.318const DeclRefExpr *drCond = vdLHS == vdInc ? drLHS : drRHS;319320SmallVector<SourceRange, 2> ranges;321SmallString<256> sbuf;322llvm::raw_svector_ostream os(sbuf);323324os << "Variable '" << drCond->getDecl()->getName()325<< "' with floating point type '" << drCond->getType()326<< "' should not be used as a loop counter";327328ranges.push_back(drCond->getSourceRange());329ranges.push_back(drInc->getSourceRange());330331const char *bugType = "Floating point variable used as loop counter";332333PathDiagnosticLocation FSLoc =334PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC);335BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,336bugType, "Security", os.str(),337FSLoc, ranges);338}339340//===----------------------------------------------------------------------===//341// Check: Any use of bcmp.342// CWE-477: Use of Obsolete Functions343// bcmp was deprecated in POSIX.1-2008344//===----------------------------------------------------------------------===//345346void WalkAST::checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD) {347if (!filter.check_bcmp)348return;349350const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();351if (!FPT)352return;353354// Verify that the function takes three arguments.355if (FPT->getNumParams() != 3)356return;357358for (int i = 0; i < 2; i++) {359// Verify the first and second argument type is void*.360const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();361if (!PT)362return;363364if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)365return;366}367368// Verify the third argument type is integer.369if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())370return;371372// Issue a warning.373PathDiagnosticLocation CELoc =374PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);375BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp,376"Use of deprecated function in call to 'bcmp()'",377"Security",378"The bcmp() function is obsoleted by memcmp().",379CELoc, CE->getCallee()->getSourceRange());380}381382//===----------------------------------------------------------------------===//383// Check: Any use of bcopy.384// CWE-477: Use of Obsolete Functions385// bcopy was deprecated in POSIX.1-2008386//===----------------------------------------------------------------------===//387388void WalkAST::checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD) {389if (!filter.check_bcopy)390return;391392const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();393if (!FPT)394return;395396// Verify that the function takes three arguments.397if (FPT->getNumParams() != 3)398return;399400for (int i = 0; i < 2; i++) {401// Verify the first and second argument type is void*.402const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();403if (!PT)404return;405406if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)407return;408}409410// Verify the third argument type is integer.411if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())412return;413414// Issue a warning.415PathDiagnosticLocation CELoc =416PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);417BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy,418"Use of deprecated function in call to 'bcopy()'",419"Security",420"The bcopy() function is obsoleted by memcpy() "421"or memmove().",422CELoc, CE->getCallee()->getSourceRange());423}424425//===----------------------------------------------------------------------===//426// Check: Any use of bzero.427// CWE-477: Use of Obsolete Functions428// bzero was deprecated in POSIX.1-2008429//===----------------------------------------------------------------------===//430431void WalkAST::checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD) {432if (!filter.check_bzero)433return;434435const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();436if (!FPT)437return;438439// Verify that the function takes two arguments.440if (FPT->getNumParams() != 2)441return;442443// Verify the first argument type is void*.444const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();445if (!PT)446return;447448if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)449return;450451// Verify the second argument type is integer.452if (!FPT->getParamType(1)->isIntegralOrUnscopedEnumerationType())453return;454455// Issue a warning.456PathDiagnosticLocation CELoc =457PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);458BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero,459"Use of deprecated function in call to 'bzero()'",460"Security",461"The bzero() function is obsoleted by memset().",462CELoc, CE->getCallee()->getSourceRange());463}464465466//===----------------------------------------------------------------------===//467// Check: Any use of 'gets' is insecure. Most man pages literally says this.468//469// Implements (part of): 300-BSI (buildsecurityin.us-cert.gov)470// CWE-242: Use of Inherently Dangerous Function471//===----------------------------------------------------------------------===//472473void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) {474if (!filter.check_gets)475return;476477const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();478if (!FPT)479return;480481// Verify that the function takes a single argument.482if (FPT->getNumParams() != 1)483return;484485// Is the argument a 'char*'?486const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();487if (!PT)488return;489490if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)491return;492493// Issue a warning.494PathDiagnosticLocation CELoc =495PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);496BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,497"Potential buffer overflow in call to 'gets'",498"Security",499"Call to function 'gets' is extremely insecure as it can "500"always result in a buffer overflow",501CELoc, CE->getCallee()->getSourceRange());502}503504//===----------------------------------------------------------------------===//505// Check: Any use of 'getpwd' is insecure.506// CWE-477: Use of Obsolete Functions507//===----------------------------------------------------------------------===//508509void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) {510if (!filter.check_getpw)511return;512513const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();514if (!FPT)515return;516517// Verify that the function takes two arguments.518if (FPT->getNumParams() != 2)519return;520521// Verify the first argument type is integer.522if (!FPT->getParamType(0)->isIntegralOrUnscopedEnumerationType())523return;524525// Verify the second argument type is char*.526const PointerType *PT = FPT->getParamType(1)->getAs<PointerType>();527if (!PT)528return;529530if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)531return;532533// Issue a warning.534PathDiagnosticLocation CELoc =535PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);536BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,537"Potential buffer overflow in call to 'getpw'",538"Security",539"The getpw() function is dangerous as it may overflow the "540"provided buffer. It is obsoleted by getpwuid().",541CELoc, CE->getCallee()->getSourceRange());542}543544//===----------------------------------------------------------------------===//545// Check: Any use of 'mktemp' is insecure. It is obsoleted by mkstemp().546// CWE-377: Insecure Temporary File547//===----------------------------------------------------------------------===//548549void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {550if (!filter.check_mktemp) {551// Fall back to the security check of looking for enough 'X's in the552// format string, since that is a less severe warning.553checkCall_mkstemp(CE, FD);554return;555}556557const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();558if(!FPT)559return;560561// Verify that the function takes a single argument.562if (FPT->getNumParams() != 1)563return;564565// Verify that the argument is Pointer Type.566const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();567if (!PT)568return;569570// Verify that the argument is a 'char*'.571if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)572return;573574// Issue a warning.575PathDiagnosticLocation CELoc =576PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);577BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,578"Potential insecure temporary file in call 'mktemp'",579"Security",580"Call to function 'mktemp' is insecure as it always "581"creates or uses insecure temporary file. Use 'mkstemp' "582"instead",583CELoc, CE->getCallee()->getSourceRange());584}585586//===----------------------------------------------------------------------===//587// Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's.588//===----------------------------------------------------------------------===//589590void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {591if (!filter.check_mkstemp)592return;593594StringRef Name = FD->getIdentifier()->getName();595std::pair<signed, signed> ArgSuffix =596llvm::StringSwitch<std::pair<signed, signed> >(Name)597.Case("mktemp", std::make_pair(0,-1))598.Case("mkstemp", std::make_pair(0,-1))599.Case("mkdtemp", std::make_pair(0,-1))600.Case("mkstemps", std::make_pair(0,1))601.Default(std::make_pair(-1, -1));602603assert(ArgSuffix.first >= 0 && "Unsupported function");604605// Check if the number of arguments is consistent with out expectations.606unsigned numArgs = CE->getNumArgs();607if ((signed) numArgs <= ArgSuffix.first)608return;609610const StringLiteral *strArg =611dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first)612->IgnoreParenImpCasts());613614// Currently we only handle string literals. It is possible to do better,615// either by looking at references to const variables, or by doing real616// flow analysis.617if (!strArg || strArg->getCharByteWidth() != 1)618return;619620// Count the number of X's, taking into account a possible cutoff suffix.621StringRef str = strArg->getString();622unsigned numX = 0;623unsigned n = str.size();624625// Take into account the suffix.626unsigned suffix = 0;627if (ArgSuffix.second >= 0) {628const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second);629Expr::EvalResult EVResult;630if (!suffixEx->EvaluateAsInt(EVResult, BR.getContext()))631return;632llvm::APSInt Result = EVResult.Val.getInt();633// FIXME: Issue a warning.634if (Result.isNegative())635return;636suffix = (unsigned) Result.getZExtValue();637n = (n > suffix) ? n - suffix : 0;638}639640for (unsigned i = 0; i < n; ++i)641if (str[i] == 'X') ++numX;642643if (numX >= 6)644return;645646// Issue a warning.647PathDiagnosticLocation CELoc =648PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);649SmallString<512> buf;650llvm::raw_svector_ostream out(buf);651out << "Call to '" << Name << "' should have at least 6 'X's in the"652" format string to be secure (" << numX << " 'X'";653if (numX != 1)654out << 's';655out << " seen";656if (suffix) {657out << ", " << suffix << " character";658if (suffix > 1)659out << 's';660out << " used as a suffix";661}662out << ')';663BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,664"Insecure temporary file creation", "Security",665out.str(), CELoc, strArg->getSourceRange());666}667668//===----------------------------------------------------------------------===//669// Check: Any use of 'strcpy' is insecure.670//671// CWE-119: Improper Restriction of Operations within672// the Bounds of a Memory Buffer673//===----------------------------------------------------------------------===//674675void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {676if (!filter.check_strcpy)677return;678679if (!checkCall_strCommon(CE, FD))680return;681682const auto *Target = CE->getArg(0)->IgnoreImpCasts(),683*Source = CE->getArg(1)->IgnoreImpCasts();684685if (const auto *Array = dyn_cast<ConstantArrayType>(Target->getType())) {686uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8;687if (const auto *String = dyn_cast<StringLiteral>(Source)) {688if (ArraySize >= String->getLength() + 1)689return;690}691}692693// Issue a warning.694PathDiagnosticLocation CELoc =695PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);696BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,697"Potential insecure memory buffer bounds restriction in "698"call 'strcpy'",699"Security",700"Call to function 'strcpy' is insecure as it does not "701"provide bounding of the memory buffer. Replace "702"unbounded copy functions with analogous functions that "703"support length arguments such as 'strlcpy'. CWE-119.",704CELoc, CE->getCallee()->getSourceRange());705}706707//===----------------------------------------------------------------------===//708// Check: Any use of 'strcat' is insecure.709//710// CWE-119: Improper Restriction of Operations within711// the Bounds of a Memory Buffer712//===----------------------------------------------------------------------===//713714void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {715if (!filter.check_strcpy)716return;717718if (!checkCall_strCommon(CE, FD))719return;720721// Issue a warning.722PathDiagnosticLocation CELoc =723PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);724BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,725"Potential insecure memory buffer bounds restriction in "726"call 'strcat'",727"Security",728"Call to function 'strcat' is insecure as it does not "729"provide bounding of the memory buffer. Replace "730"unbounded copy functions with analogous functions that "731"support length arguments such as 'strlcat'. CWE-119.",732CELoc, CE->getCallee()->getSourceRange());733}734735//===----------------------------------------------------------------------===//736// Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',737// 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',738// 'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf',739// 'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset',740// 'fprintf' is deprecated since C11.741//742// Use of 'sprintf', 'fprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',743// 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',744// 'swscanf', 'vsscanf', 'vswscanf' without buffer limitations745// is insecure.746//747// CWE-119: Improper Restriction of Operations within748// the Bounds of a Memory Buffer749//===----------------------------------------------------------------------===//750751void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,752const FunctionDecl *FD) {753if (!filter.check_DeprecatedOrUnsafeBufferHandling)754return;755756if (!BR.getContext().getLangOpts().C11)757return;758759// Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size760// restrictions).761enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 };762763StringRef Name = FD->getIdentifier()->getName();764Name.consume_front("__builtin_");765766int ArgIndex =767llvm::StringSwitch<int>(Name)768.Cases("scanf", "wscanf", "vscanf", "vwscanf", 0)769.Cases("fscanf", "fwscanf", "vfscanf", "vfwscanf", "sscanf",770"swscanf", "vsscanf", "vswscanf", 1)771.Cases("sprintf", "vsprintf", "fprintf", 1)772.Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy",773"memmove", "memset", "strncpy", "strncat", DEPR_ONLY)774.Default(UNKNOWN_CALL);775776assert(ArgIndex != UNKNOWN_CALL && "Unsupported function");777bool BoundsProvided = ArgIndex == DEPR_ONLY;778779if (!BoundsProvided) {780// Currently we only handle (not wide) string literals. It is possible to do781// better, either by looking at references to const variables, or by doing782// real flow analysis.783auto FormatString =784dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts());785if (FormatString && !FormatString->getString().contains("%s") &&786!FormatString->getString().contains("%["))787BoundsProvided = true;788}789790SmallString<128> Buf1;791SmallString<512> Buf2;792llvm::raw_svector_ostream Out1(Buf1);793llvm::raw_svector_ostream Out2(Buf2);794795Out1 << "Potential insecure memory buffer bounds restriction in call '"796<< Name << "'";797Out2 << "Call to function '" << Name798<< "' is insecure as it does not provide ";799800if (!BoundsProvided) {801Out2 << "bounding of the memory buffer or ";802}803804Out2 << "security checks introduced "805"in the C11 standard. Replace with analogous functions that "806"support length arguments or provides boundary checks such as '"807<< Name << "_s' in case of C11";808809PathDiagnosticLocation CELoc =810PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);811BR.EmitBasicReport(AC->getDecl(),812filter.checkName_DeprecatedOrUnsafeBufferHandling,813Out1.str(), "Security", Out2.str(), CELoc,814CE->getCallee()->getSourceRange());815}816817//===----------------------------------------------------------------------===//818// Common check for str* functions with no bounds parameters.819//===----------------------------------------------------------------------===//820821bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) {822const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();823if (!FPT)824return false;825826// Verify the function takes two arguments, three in the _chk version.827int numArgs = FPT->getNumParams();828if (numArgs != 2 && numArgs != 3)829return false;830831// Verify the type for both arguments.832for (int i = 0; i < 2; i++) {833// Verify that the arguments are pointers.834const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();835if (!PT)836return false;837838// Verify that the argument is a 'char*'.839if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)840return false;841}842843return true;844}845846//===----------------------------------------------------------------------===//847// Check: Linear congruent random number generators should not be used,848// i.e. rand(), random().849//850// E. Bach, "Efficient prediction of Marsaglia-Zaman random number generators,"851// in IEEE Transactions on Information Theory, vol. 44, no. 3, pp. 1253-1257,852// May 1998, https://doi.org/10.1109/18.669305853//854// CWE-338: Use of cryptographically weak prng855//===----------------------------------------------------------------------===//856857void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {858if (!filter.check_rand || !CheckRand)859return;860861const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();862if (!FTP)863return;864865if (FTP->getNumParams() == 1) {866// Is the argument an 'unsigned short *'?867// (Actually any integer type is allowed.)868const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>();869if (!PT)870return;871872if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType())873return;874} else if (FTP->getNumParams() != 0)875return;876877// Issue a warning.878SmallString<256> buf1;879llvm::raw_svector_ostream os1(buf1);880os1 << '\'' << *FD << "' is a poor random number generator";881882SmallString<256> buf2;883llvm::raw_svector_ostream os2(buf2);884os2 << "Function '" << *FD885<< "' is obsolete because it implements a poor random number generator."886<< " Use 'arc4random' instead";887888PathDiagnosticLocation CELoc =889PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);890BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),891"Security", os2.str(), CELoc,892CE->getCallee()->getSourceRange());893}894895// See justification for rand().896void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) {897if (!CheckRand || !filter.check_rand)898return;899900const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();901if (!FTP)902return;903904// Verify that the function takes no argument.905if (FTP->getNumParams() != 0)906return;907908// Issue a warning.909PathDiagnosticLocation CELoc =910PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);911BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,912"'random' is not a secure random number generator",913"Security",914"The 'random' function produces a sequence of values that "915"an adversary may be able to predict. Use 'arc4random' "916"instead", CELoc, CE->getCallee()->getSourceRange());917}918919//===----------------------------------------------------------------------===//920// Check: 'vfork' should not be used.921// POS33-C: Do not use vfork().922//===----------------------------------------------------------------------===//923924void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {925if (!filter.check_vfork)926return;927928// All calls to vfork() are insecure, issue a warning.929PathDiagnosticLocation CELoc =930PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);931BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,932"Potential insecure implementation-specific behavior in "933"call 'vfork'",934"Security",935"Call to function 'vfork' is insecure as it can lead to "936"denial of service situations in the parent process. "937"Replace calls to vfork with calls to the safer "938"'posix_spawn' function",939CELoc, CE->getCallee()->getSourceRange());940}941942//===----------------------------------------------------------------------===//943// Check: '-decodeValueOfObjCType:at:' should not be used.944// It is deprecated in favor of '-decodeValueOfObjCType:at:size:' due to945// likelihood of buffer overflows.946//===----------------------------------------------------------------------===//947948void WalkAST::checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME) {949if (!filter.check_decodeValueOfObjCType)950return;951952// Check availability of the secure alternative:953// iOS 11+, macOS 10.13+, tvOS 11+, and watchOS 4.0+954// FIXME: We probably shouldn't register the check if it's not available.955const TargetInfo &TI = AC->getASTContext().getTargetInfo();956const llvm::Triple &T = TI.getTriple();957const VersionTuple &VT = TI.getPlatformMinVersion();958switch (T.getOS()) {959case llvm::Triple::IOS:960if (VT < VersionTuple(11, 0))961return;962break;963case llvm::Triple::MacOSX:964if (VT < VersionTuple(10, 13))965return;966break;967case llvm::Triple::WatchOS:968if (VT < VersionTuple(4, 0))969return;970break;971case llvm::Triple::TvOS:972if (VT < VersionTuple(11, 0))973return;974break;975case llvm::Triple::XROS:976break;977default:978return;979}980981PathDiagnosticLocation MELoc =982PathDiagnosticLocation::createBegin(ME, BR.getSourceManager(), AC);983BR.EmitBasicReport(984AC->getDecl(), filter.checkName_decodeValueOfObjCType,985"Potential buffer overflow in '-decodeValueOfObjCType:at:'", "Security",986"Deprecated method '-decodeValueOfObjCType:at:' is insecure "987"as it can lead to potential buffer overflows. Use the safer "988"'-decodeValueOfObjCType:at:size:' method.",989MELoc, ME->getSourceRange());990}991992//===----------------------------------------------------------------------===//993// Check: The caller should always verify that the privileges994// were dropped successfully.995//996// Some library functions, like setuid() and setgid(), should always be used997// with a check of the return value to verify that the function completed998// successfully. If the drop fails, the software will continue to run999// with the raised privileges, which might provide additional access1000// to unprivileged users.1001//1002// (Note that this check predates __attribute__((warn_unused_result)).1003// Do we still need it now that we have a compiler warning for this?1004// Are these standard functions already annotated this way?)1005//===----------------------------------------------------------------------===//10061007void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {1008if (!filter.check_UncheckedReturn)1009return;10101011const FunctionDecl *FD = CE->getDirectCallee();1012if (!FD)1013return;10141015if (II_setid[0] == nullptr) {1016static const char * const identifiers[num_setids] = {1017"setuid", "setgid", "seteuid", "setegid",1018"setreuid", "setregid"1019};10201021for (size_t i = 0; i < num_setids; i++)1022II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);1023}10241025const IdentifierInfo *id = FD->getIdentifier();1026size_t identifierid;10271028for (identifierid = 0; identifierid < num_setids; identifierid++)1029if (id == II_setid[identifierid])1030break;10311032if (identifierid >= num_setids)1033return;10341035const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();1036if (!FTP)1037return;10381039// Verify that the function takes one or two arguments (depending on1040// the function).1041if (FTP->getNumParams() != (identifierid < 4 ? 1 : 2))1042return;10431044// The arguments must be integers.1045for (unsigned i = 0; i < FTP->getNumParams(); i++)1046if (!FTP->getParamType(i)->isIntegralOrUnscopedEnumerationType())1047return;10481049// Issue a warning.1050SmallString<256> buf1;1051llvm::raw_svector_ostream os1(buf1);1052os1 << "Return value is not checked in call to '" << *FD << '\'';10531054SmallString<256> buf2;1055llvm::raw_svector_ostream os2(buf2);1056os2 << "The return value from the call to '" << *FD1057<< "' is not checked. If an error occurs in '" << *FD1058<< "', the following code may execute with unexpected privileges";10591060PathDiagnosticLocation CELoc =1061PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);1062BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),1063"Security", os2.str(), CELoc,1064CE->getCallee()->getSourceRange());1065}10661067//===----------------------------------------------------------------------===//1068// SecuritySyntaxChecker1069//===----------------------------------------------------------------------===//10701071namespace {1072class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {1073public:1074ChecksFilter filter;10751076void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,1077BugReporter &BR) const {1078WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);1079walker.Visit(D->getBody());1080}1081};1082}10831084void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {1085mgr.registerChecker<SecuritySyntaxChecker>();1086}10871088bool ento::shouldRegisterSecuritySyntaxChecker(const CheckerManager &mgr) {1089return true;1090}10911092#define REGISTER_CHECKER(name) \1093void ento::register##name(CheckerManager &mgr) { \1094SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \1095checker->filter.check_##name = true; \1096checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \1097} \1098\1099bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }11001101REGISTER_CHECKER(bcmp)1102REGISTER_CHECKER(bcopy)1103REGISTER_CHECKER(bzero)1104REGISTER_CHECKER(gets)1105REGISTER_CHECKER(getpw)1106REGISTER_CHECKER(mkstemp)1107REGISTER_CHECKER(mktemp)1108REGISTER_CHECKER(strcpy)1109REGISTER_CHECKER(rand)1110REGISTER_CHECKER(vfork)1111REGISTER_CHECKER(FloatLoopCounter)1112REGISTER_CHECKER(UncheckedReturn)1113REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling)1114REGISTER_CHECKER(decodeValueOfObjCType)111511161117