xref: /src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp (revision 7a6dacaca14b62ca4b74406814becb87a3fefac0)
14c8b2481SRoman Divacky //==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==//
24c8b2481SRoman Divacky //
322989816SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
422989816SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
522989816SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64c8b2481SRoman Divacky //
74c8b2481SRoman Divacky //===----------------------------------------------------------------------===//
84c8b2481SRoman Divacky //
94c8b2481SRoman Divacky //  This file defines a set of flow-insensitive security checks.
104c8b2481SRoman Divacky //
114c8b2481SRoman Divacky //===----------------------------------------------------------------------===//
124c8b2481SRoman Divacky 
13676fbe81SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
1436981b17SDimitry Andric #include "clang/AST/StmtVisitor.h"
15461a67faSDimitry Andric #include "clang/Analysis/AnalysisDeclContext.h"
1636981b17SDimitry Andric #include "clang/Basic/TargetInfo.h"
17bca07a45SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
18809500fcSDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
1936981b17SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
20dbe13110SDimitry Andric #include "llvm/ADT/SmallString.h"
2101af97d3SDimitry Andric #include "llvm/ADT/StringSwitch.h"
2236981b17SDimitry Andric #include "llvm/Support/raw_ostream.h"
234c8b2481SRoman Divacky 
244c8b2481SRoman Divacky using namespace clang;
25bca07a45SDimitry Andric using namespace ento;
264c8b2481SRoman Divacky 
isArc4RandomAvailable(const ASTContext & Ctx)27ee791ddeSRoman Divacky static bool isArc4RandomAvailable(const ASTContext &Ctx) {
2836981b17SDimitry Andric   const llvm::Triple &T = Ctx.getTargetInfo().getTriple();
29ee791ddeSRoman Divacky   return T.getVendor() == llvm::Triple::Apple ||
30676fbe81SDimitry Andric          T.isOSFreeBSD() ||
31676fbe81SDimitry Andric          T.isOSNetBSD() ||
32676fbe81SDimitry Andric          T.isOSOpenBSD() ||
33676fbe81SDimitry Andric          T.isOSDragonFly();
34ee791ddeSRoman Divacky }
35ee791ddeSRoman Divacky 
364c8b2481SRoman Divacky namespace {
37dbe13110SDimitry Andric struct ChecksFilter {
38145449b1SDimitry Andric   bool check_bcmp = false;
39145449b1SDimitry Andric   bool check_bcopy = false;
40145449b1SDimitry Andric   bool check_bzero = false;
41145449b1SDimitry Andric   bool check_gets = false;
42145449b1SDimitry Andric   bool check_getpw = false;
43145449b1SDimitry Andric   bool check_mktemp = false;
44145449b1SDimitry Andric   bool check_mkstemp = false;
45145449b1SDimitry Andric   bool check_strcpy = false;
46145449b1SDimitry Andric   bool check_DeprecatedOrUnsafeBufferHandling = false;
47145449b1SDimitry Andric   bool check_rand = false;
48145449b1SDimitry Andric   bool check_vfork = false;
49145449b1SDimitry Andric   bool check_FloatLoopCounter = false;
50145449b1SDimitry Andric   bool check_UncheckedReturn = false;
51145449b1SDimitry Andric   bool check_decodeValueOfObjCType = false;
529f4dbff6SDimitry Andric 
53519fc96cSDimitry Andric   CheckerNameRef checkName_bcmp;
54519fc96cSDimitry Andric   CheckerNameRef checkName_bcopy;
55519fc96cSDimitry Andric   CheckerNameRef checkName_bzero;
56519fc96cSDimitry Andric   CheckerNameRef checkName_gets;
57519fc96cSDimitry Andric   CheckerNameRef checkName_getpw;
58519fc96cSDimitry Andric   CheckerNameRef checkName_mktemp;
59519fc96cSDimitry Andric   CheckerNameRef checkName_mkstemp;
60519fc96cSDimitry Andric   CheckerNameRef checkName_strcpy;
61519fc96cSDimitry Andric   CheckerNameRef checkName_DeprecatedOrUnsafeBufferHandling;
62519fc96cSDimitry Andric   CheckerNameRef checkName_rand;
63519fc96cSDimitry Andric   CheckerNameRef checkName_vfork;
64519fc96cSDimitry Andric   CheckerNameRef checkName_FloatLoopCounter;
65519fc96cSDimitry Andric   CheckerNameRef checkName_UncheckedReturn;
66706b4fc4SDimitry Andric   CheckerNameRef checkName_decodeValueOfObjCType;
67dbe13110SDimitry Andric };
68dbe13110SDimitry Andric 
691569ce68SRoman Divacky class WalkAST : public StmtVisitor<WalkAST> {
704c8b2481SRoman Divacky   BugReporter &BR;
71dbe13110SDimitry Andric   AnalysisDeclContext* AC;
724c8b2481SRoman Divacky   enum { num_setids = 6 };
734c8b2481SRoman Divacky   IdentifierInfo *II_setid[num_setids];
744c8b2481SRoman Divacky 
75ee791ddeSRoman Divacky   const bool CheckRand;
76dbe13110SDimitry Andric   const ChecksFilter &filter;
77ee791ddeSRoman Divacky 
784c8b2481SRoman Divacky public:
WalkAST(BugReporter & br,AnalysisDeclContext * ac,const ChecksFilter & f)79dbe13110SDimitry Andric   WalkAST(BugReporter &br, AnalysisDeclContext* ac,
80dbe13110SDimitry Andric           const ChecksFilter &f)
8136981b17SDimitry Andric   : BR(br), AC(ac), II_setid(),
82dbe13110SDimitry Andric     CheckRand(isArc4RandomAvailable(BR.getContext())),
83dbe13110SDimitry Andric     filter(f) {}
844c8b2481SRoman Divacky 
854c8b2481SRoman Divacky   // Statement visitor methods.
864c8b2481SRoman Divacky   void VisitCallExpr(CallExpr *CE);
87706b4fc4SDimitry Andric   void VisitObjCMessageExpr(ObjCMessageExpr *CE);
884c8b2481SRoman Divacky   void VisitForStmt(ForStmt *S);
894c8b2481SRoman Divacky   void VisitCompoundStmt (CompoundStmt *S);
VisitStmt(Stmt * S)904c8b2481SRoman Divacky   void VisitStmt(Stmt *S) { VisitChildren(S); }
914c8b2481SRoman Divacky 
924c8b2481SRoman Divacky   void VisitChildren(Stmt *S);
934c8b2481SRoman Divacky 
944c8b2481SRoman Divacky   // Helpers.
9501af97d3SDimitry Andric   bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD);
9601af97d3SDimitry Andric 
9745b53394SDimitry Andric   typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *);
98706b4fc4SDimitry Andric   typedef void (WalkAST::*MsgCheck)(const ObjCMessageExpr *);
994c8b2481SRoman Divacky 
1004c8b2481SRoman Divacky   // Checker-specific methods.
10101af97d3SDimitry Andric   void checkLoopConditionForFloat(const ForStmt *FS);
10248675466SDimitry Andric   void checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD);
10348675466SDimitry Andric   void checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD);
10448675466SDimitry Andric   void checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD);
10501af97d3SDimitry Andric   void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD);
10601af97d3SDimitry Andric   void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD);
10701af97d3SDimitry Andric   void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD);
108dbe13110SDimitry Andric   void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD);
10901af97d3SDimitry Andric   void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD);
11001af97d3SDimitry Andric   void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD);
11122989816SDimitry Andric   void checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
11222989816SDimitry Andric                                              const FunctionDecl *FD);
11301af97d3SDimitry Andric   void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD);
11401af97d3SDimitry Andric   void checkCall_random(const CallExpr *CE, const FunctionDecl *FD);
11536981b17SDimitry Andric   void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD);
116706b4fc4SDimitry Andric   void checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME);
11701af97d3SDimitry Andric   void checkUncheckedReturnValue(CallExpr *CE);
1184c8b2481SRoman Divacky };
1194c8b2481SRoman Divacky } // end anonymous namespace
1204c8b2481SRoman Divacky 
1214c8b2481SRoman Divacky //===----------------------------------------------------------------------===//
1224c8b2481SRoman Divacky // AST walking.
1234c8b2481SRoman Divacky //===----------------------------------------------------------------------===//
1244c8b2481SRoman Divacky 
VisitChildren(Stmt * S)1254c8b2481SRoman Divacky void WalkAST::VisitChildren(Stmt *S) {
126c192b3dcSDimitry Andric   for (Stmt *Child : S->children())
127c192b3dcSDimitry Andric     if (Child)
128c192b3dcSDimitry Andric       Visit(Child);
1294c8b2481SRoman Divacky }
1304c8b2481SRoman Divacky 
VisitCallExpr(CallExpr * CE)1314c8b2481SRoman Divacky void WalkAST::VisitCallExpr(CallExpr *CE) {
13201af97d3SDimitry Andric   // Get the callee.
13301af97d3SDimitry Andric   const FunctionDecl *FD = CE->getDirectCallee();
13401af97d3SDimitry Andric 
13501af97d3SDimitry Andric   if (!FD)
13601af97d3SDimitry Andric     return;
13701af97d3SDimitry Andric 
13801af97d3SDimitry Andric   // Get the name of the callee. If it's a builtin, strip off the prefix.
13901af97d3SDimitry Andric   IdentifierInfo *II = FD->getIdentifier();
14001af97d3SDimitry Andric   if (!II)   // if no identifier, not a simple C function
14101af97d3SDimitry Andric     return;
14236981b17SDimitry Andric   StringRef Name = II->getName();
14377dbea07SDimitry Andric   Name.consume_front("__builtin_");
14401af97d3SDimitry Andric 
14501af97d3SDimitry Andric   // Set the evaluation function by switching on the callee name.
146b1c73532SDimitry Andric   FnCheck evalFunction =
147b1c73532SDimitry Andric       llvm::StringSwitch<FnCheck>(Name)
14848675466SDimitry Andric           .Case("bcmp", &WalkAST::checkCall_bcmp)
14948675466SDimitry Andric           .Case("bcopy", &WalkAST::checkCall_bcopy)
15048675466SDimitry Andric           .Case("bzero", &WalkAST::checkCall_bzero)
15101af97d3SDimitry Andric           .Case("gets", &WalkAST::checkCall_gets)
15201af97d3SDimitry Andric           .Case("getpw", &WalkAST::checkCall_getpw)
15301af97d3SDimitry Andric           .Case("mktemp", &WalkAST::checkCall_mktemp)
154dbe13110SDimitry Andric           .Case("mkstemp", &WalkAST::checkCall_mkstemp)
155dbe13110SDimitry Andric           .Case("mkdtemp", &WalkAST::checkCall_mkstemp)
156dbe13110SDimitry Andric           .Case("mkstemps", &WalkAST::checkCall_mkstemp)
15701af97d3SDimitry Andric           .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy)
15801af97d3SDimitry Andric           .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat)
15922989816SDimitry Andric           .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf",
16022989816SDimitry Andric                  "vscanf", "vwscanf", "vfscanf", "vfwscanf",
16122989816SDimitry Andric                  &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
16222989816SDimitry Andric           .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf",
16322989816SDimitry Andric                  "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove",
16422989816SDimitry Andric                  &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
165b1c73532SDimitry Andric           .Cases("strncpy", "strncat", "memset", "fprintf",
16622989816SDimitry Andric                  &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
16701af97d3SDimitry Andric           .Case("drand48", &WalkAST::checkCall_rand)
16801af97d3SDimitry Andric           .Case("erand48", &WalkAST::checkCall_rand)
16901af97d3SDimitry Andric           .Case("jrand48", &WalkAST::checkCall_rand)
17001af97d3SDimitry Andric           .Case("lrand48", &WalkAST::checkCall_rand)
17101af97d3SDimitry Andric           .Case("mrand48", &WalkAST::checkCall_rand)
17201af97d3SDimitry Andric           .Case("nrand48", &WalkAST::checkCall_rand)
17301af97d3SDimitry Andric           .Case("lcong48", &WalkAST::checkCall_rand)
17401af97d3SDimitry Andric           .Case("rand", &WalkAST::checkCall_rand)
17501af97d3SDimitry Andric           .Case("rand_r", &WalkAST::checkCall_rand)
17601af97d3SDimitry Andric           .Case("random", &WalkAST::checkCall_random)
17736981b17SDimitry Andric           .Case("vfork", &WalkAST::checkCall_vfork)
1789f4dbff6SDimitry Andric           .Default(nullptr);
17901af97d3SDimitry Andric 
18001af97d3SDimitry Andric   // If the callee isn't defined, it is not of security concern.
18101af97d3SDimitry Andric   // Check and evaluate the call.
18201af97d3SDimitry Andric   if (evalFunction)
18301af97d3SDimitry Andric     (this->*evalFunction)(CE, FD);
1844c8b2481SRoman Divacky 
1854c8b2481SRoman Divacky   // Recurse and check children.
1864c8b2481SRoman Divacky   VisitChildren(CE);
1874c8b2481SRoman Divacky }
1884c8b2481SRoman Divacky 
VisitObjCMessageExpr(ObjCMessageExpr * ME)189706b4fc4SDimitry Andric void WalkAST::VisitObjCMessageExpr(ObjCMessageExpr *ME) {
190706b4fc4SDimitry Andric   MsgCheck evalFunction =
191706b4fc4SDimitry Andric       llvm::StringSwitch<MsgCheck>(ME->getSelector().getAsString())
192706b4fc4SDimitry Andric           .Case("decodeValueOfObjCType:at:",
193706b4fc4SDimitry Andric                 &WalkAST::checkMsg_decodeValueOfObjCType)
194706b4fc4SDimitry Andric           .Default(nullptr);
195706b4fc4SDimitry Andric 
196706b4fc4SDimitry Andric   if (evalFunction)
197706b4fc4SDimitry Andric     (this->*evalFunction)(ME);
198706b4fc4SDimitry Andric 
199706b4fc4SDimitry Andric   // Recurse and check children.
200706b4fc4SDimitry Andric   VisitChildren(ME);
201706b4fc4SDimitry Andric }
202706b4fc4SDimitry Andric 
VisitCompoundStmt(CompoundStmt * S)2034c8b2481SRoman Divacky void WalkAST::VisitCompoundStmt(CompoundStmt *S) {
204c192b3dcSDimitry Andric   for (Stmt *Child : S->children())
205c192b3dcSDimitry Andric     if (Child) {
206c192b3dcSDimitry Andric       if (CallExpr *CE = dyn_cast<CallExpr>(Child))
20701af97d3SDimitry Andric         checkUncheckedReturnValue(CE);
208c192b3dcSDimitry Andric       Visit(Child);
2094c8b2481SRoman Divacky     }
2104c8b2481SRoman Divacky }
2114c8b2481SRoman Divacky 
VisitForStmt(ForStmt * FS)2124c8b2481SRoman Divacky void WalkAST::VisitForStmt(ForStmt *FS) {
21301af97d3SDimitry Andric   checkLoopConditionForFloat(FS);
2144c8b2481SRoman Divacky 
2154c8b2481SRoman Divacky   // Recurse and check children.
2164c8b2481SRoman Divacky   VisitChildren(FS);
2174c8b2481SRoman Divacky }
2184c8b2481SRoman Divacky 
2194c8b2481SRoman Divacky //===----------------------------------------------------------------------===//
220676fbe81SDimitry Andric // Check: floating point variable used as loop counter.
2214c8b2481SRoman Divacky // Implements: CERT security coding advisory FLP-30.
2224c8b2481SRoman Divacky //===----------------------------------------------------------------------===//
2234c8b2481SRoman Divacky 
224519fc96cSDimitry Andric // Returns either 'x' or 'y', depending on which one of them is incremented
225519fc96cSDimitry Andric // in 'expr', or nullptr if none of them is incremented.
2264c8b2481SRoman Divacky static const DeclRefExpr*
getIncrementedVar(const Expr * expr,const VarDecl * x,const VarDecl * y)22701af97d3SDimitry Andric getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) {
2284c8b2481SRoman Divacky   expr = expr->IgnoreParenCasts();
2294c8b2481SRoman Divacky 
2304c8b2481SRoman Divacky   if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) {
2314c8b2481SRoman Divacky     if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
2323d1dcd9bSDimitry Andric           B->getOpcode() == BO_Comma))
2339f4dbff6SDimitry Andric       return nullptr;
2344c8b2481SRoman Divacky 
23501af97d3SDimitry Andric     if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y))
2364c8b2481SRoman Divacky       return lhs;
2374c8b2481SRoman Divacky 
23801af97d3SDimitry Andric     if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y))
2394c8b2481SRoman Divacky       return rhs;
2404c8b2481SRoman Divacky 
2419f4dbff6SDimitry Andric     return nullptr;
2424c8b2481SRoman Divacky   }
2434c8b2481SRoman Divacky 
2444c8b2481SRoman Divacky   if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {
2454c8b2481SRoman Divacky     const NamedDecl *ND = DR->getDecl();
2469f4dbff6SDimitry Andric     return ND == x || ND == y ? DR : nullptr;
2474c8b2481SRoman Divacky   }
2484c8b2481SRoman Divacky 
2494c8b2481SRoman Divacky   if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr))
2504c8b2481SRoman Divacky     return U->isIncrementDecrementOp()
2519f4dbff6SDimitry Andric       ? getIncrementedVar(U->getSubExpr(), x, y) : nullptr;
2524c8b2481SRoman Divacky 
2539f4dbff6SDimitry Andric   return nullptr;
2544c8b2481SRoman Divacky }
2554c8b2481SRoman Divacky 
2564c8b2481SRoman Divacky /// CheckLoopConditionForFloat - This check looks for 'for' statements that
2574c8b2481SRoman Divacky ///  use a floating point variable as a loop counter.
2584c8b2481SRoman Divacky ///  CERT: FLP30-C, FLP30-CPP.
2594c8b2481SRoman Divacky ///
checkLoopConditionForFloat(const ForStmt * FS)26001af97d3SDimitry Andric void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
261dbe13110SDimitry Andric   if (!filter.check_FloatLoopCounter)
262dbe13110SDimitry Andric     return;
263dbe13110SDimitry Andric 
2644c8b2481SRoman Divacky   // Does the loop have a condition?
2654c8b2481SRoman Divacky   const Expr *condition = FS->getCond();
2664c8b2481SRoman Divacky 
2674c8b2481SRoman Divacky   if (!condition)
2684c8b2481SRoman Divacky     return;
2694c8b2481SRoman Divacky 
2704c8b2481SRoman Divacky   // Does the loop have an increment?
2714c8b2481SRoman Divacky   const Expr *increment = FS->getInc();
2724c8b2481SRoman Divacky 
2734c8b2481SRoman Divacky   if (!increment)
2744c8b2481SRoman Divacky     return;
2754c8b2481SRoman Divacky 
2764c8b2481SRoman Divacky   // Strip away '()' and casts.
2774c8b2481SRoman Divacky   condition = condition->IgnoreParenCasts();
2784c8b2481SRoman Divacky   increment = increment->IgnoreParenCasts();
2794c8b2481SRoman Divacky 
2804c8b2481SRoman Divacky   // Is the loop condition a comparison?
2814c8b2481SRoman Divacky   const BinaryOperator *B = dyn_cast<BinaryOperator>(condition);
2824c8b2481SRoman Divacky 
2834c8b2481SRoman Divacky   if (!B)
2844c8b2481SRoman Divacky     return;
2854c8b2481SRoman Divacky 
2864c8b2481SRoman Divacky   // Is this a comparison?
2874c8b2481SRoman Divacky   if (!(B->isRelationalOp() || B->isEqualityOp()))
2884c8b2481SRoman Divacky     return;
2894c8b2481SRoman Divacky 
2904c8b2481SRoman Divacky   // Are we comparing variables?
291bca07a45SDimitry Andric   const DeclRefExpr *drLHS =
292bca07a45SDimitry Andric     dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts());
293bca07a45SDimitry Andric   const DeclRefExpr *drRHS =
294bca07a45SDimitry Andric     dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts());
2954c8b2481SRoman Divacky 
2964c8b2481SRoman Divacky   // Does at least one of the variables have a floating point type?
2979f4dbff6SDimitry Andric   drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : nullptr;
2989f4dbff6SDimitry Andric   drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : nullptr;
2994c8b2481SRoman Divacky 
3004c8b2481SRoman Divacky   if (!drLHS && !drRHS)
3014c8b2481SRoman Divacky     return;
3024c8b2481SRoman Divacky 
3039f4dbff6SDimitry Andric   const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : nullptr;
3049f4dbff6SDimitry Andric   const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : nullptr;
3054c8b2481SRoman Divacky 
3064c8b2481SRoman Divacky   if (!vdLHS && !vdRHS)
3074c8b2481SRoman Divacky     return;
3084c8b2481SRoman Divacky 
3094c8b2481SRoman Divacky   // Does either variable appear in increment?
31001af97d3SDimitry Andric   const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS);
3114c8b2481SRoman Divacky   if (!drInc)
3124c8b2481SRoman Divacky     return;
3134c8b2481SRoman Divacky 
314519fc96cSDimitry Andric   const VarDecl *vdInc = cast<VarDecl>(drInc->getDecl());
315519fc96cSDimitry Andric   assert(vdInc && (vdInc == vdLHS || vdInc == vdRHS));
316519fc96cSDimitry Andric 
3174c8b2481SRoman Divacky   // Emit the error.  First figure out which DeclRefExpr in the condition
3184c8b2481SRoman Divacky   // referenced the compared variable.
319519fc96cSDimitry Andric   const DeclRefExpr *drCond = vdLHS == vdInc ? drLHS : drRHS;
3204c8b2481SRoman Divacky 
32136981b17SDimitry Andric   SmallVector<SourceRange, 2> ranges;
322dbe13110SDimitry Andric   SmallString<256> sbuf;
32311d2b2d2SRoman Divacky   llvm::raw_svector_ostream os(sbuf);
3244c8b2481SRoman Divacky 
3253d1dcd9bSDimitry Andric   os << "Variable '" << drCond->getDecl()->getName()
326145449b1SDimitry Andric      << "' with floating point type '" << drCond->getType()
3274c8b2481SRoman Divacky      << "' should not be used as a loop counter";
3284c8b2481SRoman Divacky 
3294c8b2481SRoman Divacky   ranges.push_back(drCond->getSourceRange());
3304c8b2481SRoman Divacky   ranges.push_back(drInc->getSourceRange());
3314c8b2481SRoman Divacky 
3324c8b2481SRoman Divacky   const char *bugType = "Floating point variable used as loop counter";
33336981b17SDimitry Andric 
33436981b17SDimitry Andric   PathDiagnosticLocation FSLoc =
33536981b17SDimitry Andric     PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC);
3369f4dbff6SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
337dbe13110SDimitry Andric                      bugType, "Security", os.str(),
338bfef3995SDimitry Andric                      FSLoc, ranges);
3394c8b2481SRoman Divacky }
3404c8b2481SRoman Divacky 
3414c8b2481SRoman Divacky //===----------------------------------------------------------------------===//
34248675466SDimitry Andric // Check: Any use of bcmp.
34348675466SDimitry Andric // CWE-477: Use of Obsolete Functions
34448675466SDimitry Andric // bcmp was deprecated in POSIX.1-2008
34548675466SDimitry Andric //===----------------------------------------------------------------------===//
34648675466SDimitry Andric 
checkCall_bcmp(const CallExpr * CE,const FunctionDecl * FD)34748675466SDimitry Andric void WalkAST::checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD) {
34848675466SDimitry Andric   if (!filter.check_bcmp)
34948675466SDimitry Andric     return;
35048675466SDimitry Andric 
35148675466SDimitry Andric   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
35248675466SDimitry Andric   if (!FPT)
35348675466SDimitry Andric     return;
35448675466SDimitry Andric 
35548675466SDimitry Andric   // Verify that the function takes three arguments.
35648675466SDimitry Andric   if (FPT->getNumParams() != 3)
35748675466SDimitry Andric     return;
35848675466SDimitry Andric 
35948675466SDimitry Andric   for (int i = 0; i < 2; i++) {
36048675466SDimitry Andric     // Verify the first and second argument type is void*.
36148675466SDimitry Andric     const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
36248675466SDimitry Andric     if (!PT)
36348675466SDimitry Andric       return;
36448675466SDimitry Andric 
36548675466SDimitry Andric     if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
36648675466SDimitry Andric       return;
36748675466SDimitry Andric   }
36848675466SDimitry Andric 
36948675466SDimitry Andric   // Verify the third argument type is integer.
37048675466SDimitry Andric   if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
37148675466SDimitry Andric     return;
37248675466SDimitry Andric 
37348675466SDimitry Andric   // Issue a warning.
37448675466SDimitry Andric   PathDiagnosticLocation CELoc =
37548675466SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
37648675466SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp,
37748675466SDimitry Andric                      "Use of deprecated function in call to 'bcmp()'",
37848675466SDimitry Andric                      "Security",
37948675466SDimitry Andric                      "The bcmp() function is obsoleted by memcmp().",
38048675466SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
38148675466SDimitry Andric }
38248675466SDimitry Andric 
38348675466SDimitry Andric //===----------------------------------------------------------------------===//
38448675466SDimitry Andric // Check: Any use of bcopy.
38548675466SDimitry Andric // CWE-477: Use of Obsolete Functions
38648675466SDimitry Andric // bcopy was deprecated in POSIX.1-2008
38748675466SDimitry Andric //===----------------------------------------------------------------------===//
38848675466SDimitry Andric 
checkCall_bcopy(const CallExpr * CE,const FunctionDecl * FD)38948675466SDimitry Andric void WalkAST::checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD) {
39048675466SDimitry Andric   if (!filter.check_bcopy)
39148675466SDimitry Andric     return;
39248675466SDimitry Andric 
39348675466SDimitry Andric   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
39448675466SDimitry Andric   if (!FPT)
39548675466SDimitry Andric     return;
39648675466SDimitry Andric 
39748675466SDimitry Andric   // Verify that the function takes three arguments.
39848675466SDimitry Andric   if (FPT->getNumParams() != 3)
39948675466SDimitry Andric     return;
40048675466SDimitry Andric 
40148675466SDimitry Andric   for (int i = 0; i < 2; i++) {
40248675466SDimitry Andric     // Verify the first and second argument type is void*.
40348675466SDimitry Andric     const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
40448675466SDimitry Andric     if (!PT)
40548675466SDimitry Andric       return;
40648675466SDimitry Andric 
40748675466SDimitry Andric     if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
40848675466SDimitry Andric       return;
40948675466SDimitry Andric   }
41048675466SDimitry Andric 
41148675466SDimitry Andric   // Verify the third argument type is integer.
41248675466SDimitry Andric   if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
41348675466SDimitry Andric     return;
41448675466SDimitry Andric 
41548675466SDimitry Andric   // Issue a warning.
41648675466SDimitry Andric   PathDiagnosticLocation CELoc =
41748675466SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
41848675466SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy,
41948675466SDimitry Andric                      "Use of deprecated function in call to 'bcopy()'",
42048675466SDimitry Andric                      "Security",
42148675466SDimitry Andric                      "The bcopy() function is obsoleted by memcpy() "
42248675466SDimitry Andric                      "or memmove().",
42348675466SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
42448675466SDimitry Andric }
42548675466SDimitry Andric 
42648675466SDimitry Andric //===----------------------------------------------------------------------===//
42748675466SDimitry Andric // Check: Any use of bzero.
42848675466SDimitry Andric // CWE-477: Use of Obsolete Functions
42948675466SDimitry Andric // bzero was deprecated in POSIX.1-2008
43048675466SDimitry Andric //===----------------------------------------------------------------------===//
43148675466SDimitry Andric 
checkCall_bzero(const CallExpr * CE,const FunctionDecl * FD)43248675466SDimitry Andric void WalkAST::checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD) {
43348675466SDimitry Andric   if (!filter.check_bzero)
43448675466SDimitry Andric     return;
43548675466SDimitry Andric 
43648675466SDimitry Andric   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
43748675466SDimitry Andric   if (!FPT)
43848675466SDimitry Andric     return;
43948675466SDimitry Andric 
44048675466SDimitry Andric   // Verify that the function takes two arguments.
44148675466SDimitry Andric   if (FPT->getNumParams() != 2)
44248675466SDimitry Andric     return;
44348675466SDimitry Andric 
44448675466SDimitry Andric   // Verify the first argument type is void*.
44548675466SDimitry Andric   const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
44648675466SDimitry Andric   if (!PT)
44748675466SDimitry Andric     return;
44848675466SDimitry Andric 
44948675466SDimitry Andric   if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
45048675466SDimitry Andric     return;
45148675466SDimitry Andric 
45248675466SDimitry Andric   // Verify the second argument type is integer.
45348675466SDimitry Andric   if (!FPT->getParamType(1)->isIntegralOrUnscopedEnumerationType())
45448675466SDimitry Andric     return;
45548675466SDimitry Andric 
45648675466SDimitry Andric   // Issue a warning.
45748675466SDimitry Andric   PathDiagnosticLocation CELoc =
45848675466SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
45948675466SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero,
46048675466SDimitry Andric                      "Use of deprecated function in call to 'bzero()'",
46148675466SDimitry Andric                      "Security",
46248675466SDimitry Andric                      "The bzero() function is obsoleted by memset().",
46348675466SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
46448675466SDimitry Andric }
46548675466SDimitry Andric 
46648675466SDimitry Andric 
46748675466SDimitry Andric //===----------------------------------------------------------------------===//
468b1c73532SDimitry Andric // Check: Any use of 'gets' is insecure. Most man pages literally says this.
469b1c73532SDimitry Andric //
4704c8b2481SRoman Divacky // Implements (part of): 300-BSI (buildsecurityin.us-cert.gov)
471b3d5a323SRoman Divacky // CWE-242: Use of Inherently Dangerous Function
4724c8b2481SRoman Divacky //===----------------------------------------------------------------------===//
4734c8b2481SRoman Divacky 
checkCall_gets(const CallExpr * CE,const FunctionDecl * FD)47401af97d3SDimitry Andric void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) {
475dbe13110SDimitry Andric   if (!filter.check_gets)
476dbe13110SDimitry Andric     return;
477dbe13110SDimitry Andric 
478bfef3995SDimitry Andric   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
479b3d5a323SRoman Divacky   if (!FPT)
4804c8b2481SRoman Divacky     return;
4814c8b2481SRoman Divacky 
4824c8b2481SRoman Divacky   // Verify that the function takes a single argument.
4839f4dbff6SDimitry Andric   if (FPT->getNumParams() != 1)
4844c8b2481SRoman Divacky     return;
4854c8b2481SRoman Divacky 
4864c8b2481SRoman Divacky   // Is the argument a 'char*'?
4879f4dbff6SDimitry Andric   const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
4884c8b2481SRoman Divacky   if (!PT)
4894c8b2481SRoman Divacky     return;
4904c8b2481SRoman Divacky 
4914c8b2481SRoman Divacky   if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
4924c8b2481SRoman Divacky     return;
4934c8b2481SRoman Divacky 
4944c8b2481SRoman Divacky   // Issue a warning.
49536981b17SDimitry Andric   PathDiagnosticLocation CELoc =
49636981b17SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
4979f4dbff6SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
498dbe13110SDimitry Andric                      "Potential buffer overflow in call to 'gets'",
4994c8b2481SRoman Divacky                      "Security",
5004c8b2481SRoman Divacky                      "Call to function 'gets' is extremely insecure as it can "
5014c8b2481SRoman Divacky                      "always result in a buffer overflow",
502bfef3995SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
5034c8b2481SRoman Divacky }
5044c8b2481SRoman Divacky 
5054c8b2481SRoman Divacky //===----------------------------------------------------------------------===//
506b3d5a323SRoman Divacky // Check: Any use of 'getpwd' is insecure.
507b3d5a323SRoman Divacky // CWE-477: Use of Obsolete Functions
508b3d5a323SRoman Divacky //===----------------------------------------------------------------------===//
509b3d5a323SRoman Divacky 
checkCall_getpw(const CallExpr * CE,const FunctionDecl * FD)51001af97d3SDimitry Andric void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) {
511dbe13110SDimitry Andric   if (!filter.check_getpw)
512dbe13110SDimitry Andric     return;
513dbe13110SDimitry Andric 
514bfef3995SDimitry Andric   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
515b3d5a323SRoman Divacky   if (!FPT)
516b3d5a323SRoman Divacky     return;
517b3d5a323SRoman Divacky 
518b3d5a323SRoman Divacky   // Verify that the function takes two arguments.
5199f4dbff6SDimitry Andric   if (FPT->getNumParams() != 2)
520b3d5a323SRoman Divacky     return;
521b3d5a323SRoman Divacky 
522b3d5a323SRoman Divacky   // Verify the first argument type is integer.
5239f4dbff6SDimitry Andric   if (!FPT->getParamType(0)->isIntegralOrUnscopedEnumerationType())
524b3d5a323SRoman Divacky     return;
525b3d5a323SRoman Divacky 
526b3d5a323SRoman Divacky   // Verify the second argument type is char*.
5279f4dbff6SDimitry Andric   const PointerType *PT = FPT->getParamType(1)->getAs<PointerType>();
528b3d5a323SRoman Divacky   if (!PT)
529b3d5a323SRoman Divacky     return;
530b3d5a323SRoman Divacky 
531b3d5a323SRoman Divacky   if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
532b3d5a323SRoman Divacky     return;
533b3d5a323SRoman Divacky 
534b3d5a323SRoman Divacky   // Issue a warning.
53536981b17SDimitry Andric   PathDiagnosticLocation CELoc =
53636981b17SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
5379f4dbff6SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
538dbe13110SDimitry Andric                      "Potential buffer overflow in call to 'getpw'",
539b3d5a323SRoman Divacky                      "Security",
540b3d5a323SRoman Divacky                      "The getpw() function is dangerous as it may overflow the "
541b3d5a323SRoman Divacky                      "provided buffer. It is obsoleted by getpwuid().",
542bfef3995SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
543b3d5a323SRoman Divacky }
544b3d5a323SRoman Divacky 
545b3d5a323SRoman Divacky //===----------------------------------------------------------------------===//
54634d02d0bSRoman Divacky // Check: Any use of 'mktemp' is insecure.  It is obsoleted by mkstemp().
54734d02d0bSRoman Divacky // CWE-377: Insecure Temporary File
54834d02d0bSRoman Divacky //===----------------------------------------------------------------------===//
54934d02d0bSRoman Divacky 
checkCall_mktemp(const CallExpr * CE,const FunctionDecl * FD)55001af97d3SDimitry Andric void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {
551dbe13110SDimitry Andric   if (!filter.check_mktemp) {
552dbe13110SDimitry Andric     // Fall back to the security check of looking for enough 'X's in the
553dbe13110SDimitry Andric     // format string, since that is a less severe warning.
554dbe13110SDimitry Andric     checkCall_mkstemp(CE, FD);
555dbe13110SDimitry Andric     return;
556dbe13110SDimitry Andric   }
557dbe13110SDimitry Andric 
558bfef3995SDimitry Andric   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
55934d02d0bSRoman Divacky   if(!FPT)
56034d02d0bSRoman Divacky     return;
56134d02d0bSRoman Divacky 
56201af97d3SDimitry Andric   // Verify that the function takes a single argument.
5639f4dbff6SDimitry Andric   if (FPT->getNumParams() != 1)
56434d02d0bSRoman Divacky     return;
56534d02d0bSRoman Divacky 
56634d02d0bSRoman Divacky   // Verify that the argument is Pointer Type.
5679f4dbff6SDimitry Andric   const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
56834d02d0bSRoman Divacky   if (!PT)
56934d02d0bSRoman Divacky     return;
57034d02d0bSRoman Divacky 
57134d02d0bSRoman Divacky   // Verify that the argument is a 'char*'.
57234d02d0bSRoman Divacky   if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
57334d02d0bSRoman Divacky     return;
57434d02d0bSRoman Divacky 
5759f4dbff6SDimitry Andric   // Issue a warning.
57636981b17SDimitry Andric   PathDiagnosticLocation CELoc =
57736981b17SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
5789f4dbff6SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
579dbe13110SDimitry Andric                      "Potential insecure temporary file in call 'mktemp'",
58034d02d0bSRoman Divacky                      "Security",
58134d02d0bSRoman Divacky                      "Call to function 'mktemp' is insecure as it always "
582dbe13110SDimitry Andric                      "creates or uses insecure temporary file.  Use 'mkstemp' "
583dbe13110SDimitry Andric                      "instead",
584bfef3995SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
58534d02d0bSRoman Divacky }
58634d02d0bSRoman Divacky 
587dbe13110SDimitry Andric //===----------------------------------------------------------------------===//
588dbe13110SDimitry Andric // Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's.
589dbe13110SDimitry Andric //===----------------------------------------------------------------------===//
590dbe13110SDimitry Andric 
checkCall_mkstemp(const CallExpr * CE,const FunctionDecl * FD)591dbe13110SDimitry Andric void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {
592dbe13110SDimitry Andric   if (!filter.check_mkstemp)
593dbe13110SDimitry Andric     return;
594dbe13110SDimitry Andric 
595dbe13110SDimitry Andric   StringRef Name = FD->getIdentifier()->getName();
596dbe13110SDimitry Andric   std::pair<signed, signed> ArgSuffix =
597dbe13110SDimitry Andric     llvm::StringSwitch<std::pair<signed, signed> >(Name)
598dbe13110SDimitry Andric       .Case("mktemp", std::make_pair(0,-1))
599dbe13110SDimitry Andric       .Case("mkstemp", std::make_pair(0,-1))
600dbe13110SDimitry Andric       .Case("mkdtemp", std::make_pair(0,-1))
601dbe13110SDimitry Andric       .Case("mkstemps", std::make_pair(0,1))
602dbe13110SDimitry Andric       .Default(std::make_pair(-1, -1));
603dbe13110SDimitry Andric 
604dbe13110SDimitry Andric   assert(ArgSuffix.first >= 0 && "Unsupported function");
605dbe13110SDimitry Andric 
606dbe13110SDimitry Andric   // Check if the number of arguments is consistent with out expectations.
607dbe13110SDimitry Andric   unsigned numArgs = CE->getNumArgs();
608dbe13110SDimitry Andric   if ((signed) numArgs <= ArgSuffix.first)
609dbe13110SDimitry Andric     return;
610dbe13110SDimitry Andric 
611dbe13110SDimitry Andric   const StringLiteral *strArg =
612dbe13110SDimitry Andric     dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first)
613dbe13110SDimitry Andric                               ->IgnoreParenImpCasts());
614dbe13110SDimitry Andric 
615dbe13110SDimitry Andric   // Currently we only handle string literals.  It is possible to do better,
616dbe13110SDimitry Andric   // either by looking at references to const variables, or by doing real
617dbe13110SDimitry Andric   // flow analysis.
618dbe13110SDimitry Andric   if (!strArg || strArg->getCharByteWidth() != 1)
619dbe13110SDimitry Andric     return;
620dbe13110SDimitry Andric 
621dbe13110SDimitry Andric   // Count the number of X's, taking into account a possible cutoff suffix.
622dbe13110SDimitry Andric   StringRef str = strArg->getString();
623dbe13110SDimitry Andric   unsigned numX = 0;
624dbe13110SDimitry Andric   unsigned n = str.size();
625dbe13110SDimitry Andric 
626dbe13110SDimitry Andric   // Take into account the suffix.
627dbe13110SDimitry Andric   unsigned suffix = 0;
628dbe13110SDimitry Andric   if (ArgSuffix.second >= 0) {
629dbe13110SDimitry Andric     const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second);
630676fbe81SDimitry Andric     Expr::EvalResult EVResult;
631676fbe81SDimitry Andric     if (!suffixEx->EvaluateAsInt(EVResult, BR.getContext()))
632dbe13110SDimitry Andric       return;
633676fbe81SDimitry Andric     llvm::APSInt Result = EVResult.Val.getInt();
634dbe13110SDimitry Andric     // FIXME: Issue a warning.
635dbe13110SDimitry Andric     if (Result.isNegative())
636dbe13110SDimitry Andric       return;
637dbe13110SDimitry Andric     suffix = (unsigned) Result.getZExtValue();
638dbe13110SDimitry Andric     n = (n > suffix) ? n - suffix : 0;
639dbe13110SDimitry Andric   }
640dbe13110SDimitry Andric 
641dbe13110SDimitry Andric   for (unsigned i = 0; i < n; ++i)
642dbe13110SDimitry Andric     if (str[i] == 'X') ++numX;
643dbe13110SDimitry Andric 
644dbe13110SDimitry Andric   if (numX >= 6)
645dbe13110SDimitry Andric     return;
646dbe13110SDimitry Andric 
647dbe13110SDimitry Andric   // Issue a warning.
648dbe13110SDimitry Andric   PathDiagnosticLocation CELoc =
649dbe13110SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
650dbe13110SDimitry Andric   SmallString<512> buf;
651dbe13110SDimitry Andric   llvm::raw_svector_ostream out(buf);
652dbe13110SDimitry Andric   out << "Call to '" << Name << "' should have at least 6 'X's in the"
653dbe13110SDimitry Andric     " format string to be secure (" << numX << " 'X'";
654dbe13110SDimitry Andric   if (numX != 1)
655dbe13110SDimitry Andric     out << 's';
656dbe13110SDimitry Andric   out << " seen";
657dbe13110SDimitry Andric   if (suffix) {
658dbe13110SDimitry Andric     out << ", " << suffix << " character";
659dbe13110SDimitry Andric     if (suffix > 1)
660dbe13110SDimitry Andric       out << 's';
661dbe13110SDimitry Andric     out << " used as a suffix";
662dbe13110SDimitry Andric   }
663dbe13110SDimitry Andric   out << ')';
6649f4dbff6SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
665dbe13110SDimitry Andric                      "Insecure temporary file creation", "Security",
666bfef3995SDimitry Andric                      out.str(), CELoc, strArg->getSourceRange());
667dbe13110SDimitry Andric }
668dbe13110SDimitry Andric 
66934d02d0bSRoman Divacky //===----------------------------------------------------------------------===//
67001af97d3SDimitry Andric // Check: Any use of 'strcpy' is insecure.
67101af97d3SDimitry Andric //
67201af97d3SDimitry Andric // CWE-119: Improper Restriction of Operations within
67301af97d3SDimitry Andric // the Bounds of a Memory Buffer
67401af97d3SDimitry Andric //===----------------------------------------------------------------------===//
67522989816SDimitry Andric 
checkCall_strcpy(const CallExpr * CE,const FunctionDecl * FD)67601af97d3SDimitry Andric void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
677dbe13110SDimitry Andric   if (!filter.check_strcpy)
678dbe13110SDimitry Andric     return;
679dbe13110SDimitry Andric 
68001af97d3SDimitry Andric   if (!checkCall_strCommon(CE, FD))
68101af97d3SDimitry Andric     return;
68201af97d3SDimitry Andric 
68348675466SDimitry Andric   const auto *Target = CE->getArg(0)->IgnoreImpCasts(),
68448675466SDimitry Andric              *Source = CE->getArg(1)->IgnoreImpCasts();
685676fbe81SDimitry Andric 
686676fbe81SDimitry Andric   if (const auto *Array = dyn_cast<ConstantArrayType>(Target->getType())) {
68748675466SDimitry Andric     uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8;
68848675466SDimitry Andric     if (const auto *String = dyn_cast<StringLiteral>(Source)) {
68948675466SDimitry Andric       if (ArraySize >= String->getLength() + 1)
69048675466SDimitry Andric         return;
69148675466SDimitry Andric     }
69248675466SDimitry Andric   }
69348675466SDimitry Andric 
69401af97d3SDimitry Andric   // Issue a warning.
69536981b17SDimitry Andric   PathDiagnosticLocation CELoc =
69636981b17SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
6979f4dbff6SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
698dbe13110SDimitry Andric                      "Potential insecure memory buffer bounds restriction in "
69901af97d3SDimitry Andric                      "call 'strcpy'",
70001af97d3SDimitry Andric                      "Security",
70101af97d3SDimitry Andric                      "Call to function 'strcpy' is insecure as it does not "
70201af97d3SDimitry Andric                      "provide bounding of the memory buffer. Replace "
70301af97d3SDimitry Andric                      "unbounded copy functions with analogous functions that "
704dbe13110SDimitry Andric                      "support length arguments such as 'strlcpy'. CWE-119.",
705bfef3995SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
70601af97d3SDimitry Andric }
70701af97d3SDimitry Andric 
70801af97d3SDimitry Andric //===----------------------------------------------------------------------===//
70901af97d3SDimitry Andric // Check: Any use of 'strcat' is insecure.
71001af97d3SDimitry Andric //
71101af97d3SDimitry Andric // CWE-119: Improper Restriction of Operations within
71201af97d3SDimitry Andric // the Bounds of a Memory Buffer
71301af97d3SDimitry Andric //===----------------------------------------------------------------------===//
71422989816SDimitry Andric 
checkCall_strcat(const CallExpr * CE,const FunctionDecl * FD)71501af97d3SDimitry Andric void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
716dbe13110SDimitry Andric   if (!filter.check_strcpy)
717dbe13110SDimitry Andric     return;
718dbe13110SDimitry Andric 
71901af97d3SDimitry Andric   if (!checkCall_strCommon(CE, FD))
72001af97d3SDimitry Andric     return;
72101af97d3SDimitry Andric 
72201af97d3SDimitry Andric   // Issue a warning.
72336981b17SDimitry Andric   PathDiagnosticLocation CELoc =
72436981b17SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
7259f4dbff6SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
726dbe13110SDimitry Andric                      "Potential insecure memory buffer bounds restriction in "
72701af97d3SDimitry Andric                      "call 'strcat'",
72801af97d3SDimitry Andric                      "Security",
72901af97d3SDimitry Andric                      "Call to function 'strcat' is insecure as it does not "
73001af97d3SDimitry Andric                      "provide bounding of the memory buffer. Replace "
73101af97d3SDimitry Andric                      "unbounded copy functions with analogous functions that "
732dbe13110SDimitry Andric                      "support length arguments such as 'strlcat'. CWE-119.",
733bfef3995SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
73401af97d3SDimitry Andric }
73501af97d3SDimitry Andric 
73601af97d3SDimitry Andric //===----------------------------------------------------------------------===//
73722989816SDimitry Andric // Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',
73822989816SDimitry Andric //        'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
73922989816SDimitry Andric //        'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf',
740b1c73532SDimitry Andric //        'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset',
741b1c73532SDimitry Andric //        'fprintf' is deprecated since C11.
74222989816SDimitry Andric //
743b1c73532SDimitry Andric //        Use of 'sprintf', 'fprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',
74422989816SDimitry Andric //        'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
74522989816SDimitry Andric //        'swscanf', 'vsscanf', 'vswscanf' without buffer limitations
74622989816SDimitry Andric //        is insecure.
74722989816SDimitry Andric //
74822989816SDimitry Andric // CWE-119: Improper Restriction of Operations within
74922989816SDimitry Andric // the Bounds of a Memory Buffer
75022989816SDimitry Andric //===----------------------------------------------------------------------===//
75122989816SDimitry Andric 
checkDeprecatedOrUnsafeBufferHandling(const CallExpr * CE,const FunctionDecl * FD)75222989816SDimitry Andric void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
75322989816SDimitry Andric                                                     const FunctionDecl *FD) {
75422989816SDimitry Andric   if (!filter.check_DeprecatedOrUnsafeBufferHandling)
75522989816SDimitry Andric     return;
75622989816SDimitry Andric 
75722989816SDimitry Andric   if (!BR.getContext().getLangOpts().C11)
75822989816SDimitry Andric     return;
75922989816SDimitry Andric 
76022989816SDimitry Andric   // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size
76122989816SDimitry Andric   // restrictions).
76222989816SDimitry Andric   enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 };
76322989816SDimitry Andric 
76422989816SDimitry Andric   StringRef Name = FD->getIdentifier()->getName();
76577dbea07SDimitry Andric   Name.consume_front("__builtin_");
76622989816SDimitry Andric 
76722989816SDimitry Andric   int ArgIndex =
76822989816SDimitry Andric       llvm::StringSwitch<int>(Name)
76922989816SDimitry Andric           .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0)
770b1c73532SDimitry Andric           .Cases("fscanf", "fwscanf", "vfscanf", "vfwscanf", "sscanf",
771b1c73532SDimitry Andric                  "swscanf", "vsscanf", "vswscanf", 1)
772b1c73532SDimitry Andric           .Cases("sprintf", "vsprintf", "fprintf", 1)
77322989816SDimitry Andric           .Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy",
77422989816SDimitry Andric                  "memmove", "memset", "strncpy", "strncat", DEPR_ONLY)
77522989816SDimitry Andric           .Default(UNKNOWN_CALL);
77622989816SDimitry Andric 
77722989816SDimitry Andric   assert(ArgIndex != UNKNOWN_CALL && "Unsupported function");
77822989816SDimitry Andric   bool BoundsProvided = ArgIndex == DEPR_ONLY;
77922989816SDimitry Andric 
78022989816SDimitry Andric   if (!BoundsProvided) {
78122989816SDimitry Andric     // Currently we only handle (not wide) string literals. It is possible to do
78222989816SDimitry Andric     // better, either by looking at references to const variables, or by doing
78322989816SDimitry Andric     // real flow analysis.
78422989816SDimitry Andric     auto FormatString =
78522989816SDimitry Andric         dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts());
786c0981da4SDimitry Andric     if (FormatString && !FormatString->getString().contains("%s") &&
787c0981da4SDimitry Andric         !FormatString->getString().contains("%["))
78822989816SDimitry Andric       BoundsProvided = true;
78922989816SDimitry Andric   }
79022989816SDimitry Andric 
79122989816SDimitry Andric   SmallString<128> Buf1;
79222989816SDimitry Andric   SmallString<512> Buf2;
79322989816SDimitry Andric   llvm::raw_svector_ostream Out1(Buf1);
79422989816SDimitry Andric   llvm::raw_svector_ostream Out2(Buf2);
79522989816SDimitry Andric 
79622989816SDimitry Andric   Out1 << "Potential insecure memory buffer bounds restriction in call '"
79722989816SDimitry Andric        << Name << "'";
79822989816SDimitry Andric   Out2 << "Call to function '" << Name
79922989816SDimitry Andric        << "' is insecure as it does not provide ";
80022989816SDimitry Andric 
80122989816SDimitry Andric   if (!BoundsProvided) {
80222989816SDimitry Andric     Out2 << "bounding of the memory buffer or ";
80322989816SDimitry Andric   }
80422989816SDimitry Andric 
80522989816SDimitry Andric   Out2 << "security checks introduced "
80622989816SDimitry Andric           "in the C11 standard. Replace with analogous functions that "
80722989816SDimitry Andric           "support length arguments or provides boundary checks such as '"
80822989816SDimitry Andric        << Name << "_s' in case of C11";
80922989816SDimitry Andric 
81022989816SDimitry Andric   PathDiagnosticLocation CELoc =
81122989816SDimitry Andric       PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
81222989816SDimitry Andric   BR.EmitBasicReport(AC->getDecl(),
81322989816SDimitry Andric                      filter.checkName_DeprecatedOrUnsafeBufferHandling,
81422989816SDimitry Andric                      Out1.str(), "Security", Out2.str(), CELoc,
81522989816SDimitry Andric                      CE->getCallee()->getSourceRange());
81622989816SDimitry Andric }
81722989816SDimitry Andric 
81822989816SDimitry Andric //===----------------------------------------------------------------------===//
81901af97d3SDimitry Andric // Common check for str* functions with no bounds parameters.
82001af97d3SDimitry Andric //===----------------------------------------------------------------------===//
82122989816SDimitry Andric 
checkCall_strCommon(const CallExpr * CE,const FunctionDecl * FD)82201af97d3SDimitry Andric bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) {
823bfef3995SDimitry Andric   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
82401af97d3SDimitry Andric   if (!FPT)
82501af97d3SDimitry Andric     return false;
82601af97d3SDimitry Andric 
82701af97d3SDimitry Andric   // Verify the function takes two arguments, three in the _chk version.
8289f4dbff6SDimitry Andric   int numArgs = FPT->getNumParams();
82901af97d3SDimitry Andric   if (numArgs != 2 && numArgs != 3)
83001af97d3SDimitry Andric     return false;
83101af97d3SDimitry Andric 
83201af97d3SDimitry Andric   // Verify the type for both arguments.
83301af97d3SDimitry Andric   for (int i = 0; i < 2; i++) {
83401af97d3SDimitry Andric     // Verify that the arguments are pointers.
8359f4dbff6SDimitry Andric     const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
83601af97d3SDimitry Andric     if (!PT)
83701af97d3SDimitry Andric       return false;
83801af97d3SDimitry Andric 
83901af97d3SDimitry Andric     // Verify that the argument is a 'char*'.
84001af97d3SDimitry Andric     if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
84101af97d3SDimitry Andric       return false;
84201af97d3SDimitry Andric   }
84301af97d3SDimitry Andric 
84401af97d3SDimitry Andric   return true;
84501af97d3SDimitry Andric }
84601af97d3SDimitry Andric 
84701af97d3SDimitry Andric //===----------------------------------------------------------------------===//
848b1c73532SDimitry Andric // Check: Linear congruent random number generators should not be used,
849b1c73532SDimitry Andric // i.e. rand(), random().
850b1c73532SDimitry Andric //
851b1c73532SDimitry Andric // E. Bach, "Efficient prediction of Marsaglia-Zaman random number generators,"
852b1c73532SDimitry Andric // in IEEE Transactions on Information Theory, vol. 44, no. 3, pp. 1253-1257,
853b1c73532SDimitry Andric // May 1998, https://doi.org/10.1109/18.669305
854b1c73532SDimitry Andric //
8554c8b2481SRoman Divacky // CWE-338: Use of cryptographically weak prng
8564c8b2481SRoman Divacky //===----------------------------------------------------------------------===//
8574c8b2481SRoman Divacky 
checkCall_rand(const CallExpr * CE,const FunctionDecl * FD)85801af97d3SDimitry Andric void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
859dbe13110SDimitry Andric   if (!filter.check_rand || !CheckRand)
8604c8b2481SRoman Divacky     return;
8614c8b2481SRoman Divacky 
862bfef3995SDimitry Andric   const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
8634c8b2481SRoman Divacky   if (!FTP)
8644c8b2481SRoman Divacky     return;
8654c8b2481SRoman Divacky 
8669f4dbff6SDimitry Andric   if (FTP->getNumParams() == 1) {
8674c8b2481SRoman Divacky     // Is the argument an 'unsigned short *'?
8684c8b2481SRoman Divacky     // (Actually any integer type is allowed.)
8699f4dbff6SDimitry Andric     const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>();
8704c8b2481SRoman Divacky     if (!PT)
8714c8b2481SRoman Divacky       return;
8724c8b2481SRoman Divacky 
8736a037251SDimitry Andric     if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType())
8744c8b2481SRoman Divacky       return;
8759f4dbff6SDimitry Andric   } else if (FTP->getNumParams() != 0)
8764c8b2481SRoman Divacky     return;
8774c8b2481SRoman Divacky 
8784c8b2481SRoman Divacky   // Issue a warning.
879dbe13110SDimitry Andric   SmallString<256> buf1;
88011d2b2d2SRoman Divacky   llvm::raw_svector_ostream os1(buf1);
88136981b17SDimitry Andric   os1 << '\'' << *FD << "' is a poor random number generator";
8824c8b2481SRoman Divacky 
883dbe13110SDimitry Andric   SmallString<256> buf2;
88411d2b2d2SRoman Divacky   llvm::raw_svector_ostream os2(buf2);
88536981b17SDimitry Andric   os2 << "Function '" << *FD
8864c8b2481SRoman Divacky       << "' is obsolete because it implements a poor random number generator."
8874c8b2481SRoman Divacky       << "  Use 'arc4random' instead";
8884c8b2481SRoman Divacky 
88936981b17SDimitry Andric   PathDiagnosticLocation CELoc =
89036981b17SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
8919f4dbff6SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
8929f4dbff6SDimitry Andric                      "Security", os2.str(), CELoc,
8939f4dbff6SDimitry Andric                      CE->getCallee()->getSourceRange());
8944c8b2481SRoman Divacky }
8954c8b2481SRoman Divacky 
896b1c73532SDimitry Andric // See justification for rand().
checkCall_random(const CallExpr * CE,const FunctionDecl * FD)89701af97d3SDimitry Andric void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) {
898dbe13110SDimitry Andric   if (!CheckRand || !filter.check_rand)
8994c8b2481SRoman Divacky     return;
9004c8b2481SRoman Divacky 
901bfef3995SDimitry Andric   const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
9024c8b2481SRoman Divacky   if (!FTP)
9034c8b2481SRoman Divacky     return;
9044c8b2481SRoman Divacky 
9054c8b2481SRoman Divacky   // Verify that the function takes no argument.
9069f4dbff6SDimitry Andric   if (FTP->getNumParams() != 0)
9074c8b2481SRoman Divacky     return;
9084c8b2481SRoman Divacky 
9094c8b2481SRoman Divacky   // Issue a warning.
91036981b17SDimitry Andric   PathDiagnosticLocation CELoc =
91136981b17SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
9129f4dbff6SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
913dbe13110SDimitry Andric                      "'random' is not a secure random number generator",
9144c8b2481SRoman Divacky                      "Security",
9154c8b2481SRoman Divacky                      "The 'random' function produces a sequence of values that "
9164c8b2481SRoman Divacky                      "an adversary may be able to predict.  Use 'arc4random' "
917bfef3995SDimitry Andric                      "instead", CELoc, CE->getCallee()->getSourceRange());
91836981b17SDimitry Andric }
91936981b17SDimitry Andric 
92036981b17SDimitry Andric //===----------------------------------------------------------------------===//
92136981b17SDimitry Andric // Check: 'vfork' should not be used.
92236981b17SDimitry Andric // POS33-C: Do not use vfork().
92336981b17SDimitry Andric //===----------------------------------------------------------------------===//
92436981b17SDimitry Andric 
checkCall_vfork(const CallExpr * CE,const FunctionDecl * FD)92536981b17SDimitry Andric void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
926dbe13110SDimitry Andric   if (!filter.check_vfork)
927dbe13110SDimitry Andric     return;
928dbe13110SDimitry Andric 
92936981b17SDimitry Andric   // All calls to vfork() are insecure, issue a warning.
93036981b17SDimitry Andric   PathDiagnosticLocation CELoc =
93136981b17SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
9329f4dbff6SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
933dbe13110SDimitry Andric                      "Potential insecure implementation-specific behavior in "
93436981b17SDimitry Andric                      "call 'vfork'",
93536981b17SDimitry Andric                      "Security",
93636981b17SDimitry Andric                      "Call to function 'vfork' is insecure as it can lead to "
93736981b17SDimitry Andric                      "denial of service situations in the parent process. "
93836981b17SDimitry Andric                      "Replace calls to vfork with calls to the safer "
93936981b17SDimitry Andric                      "'posix_spawn' function",
940bfef3995SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
9414c8b2481SRoman Divacky }
9424c8b2481SRoman Divacky 
9434c8b2481SRoman Divacky //===----------------------------------------------------------------------===//
944706b4fc4SDimitry Andric // Check: '-decodeValueOfObjCType:at:' should not be used.
945706b4fc4SDimitry Andric // It is deprecated in favor of '-decodeValueOfObjCType:at:size:' due to
946706b4fc4SDimitry Andric // likelihood of buffer overflows.
947706b4fc4SDimitry Andric //===----------------------------------------------------------------------===//
948706b4fc4SDimitry Andric 
checkMsg_decodeValueOfObjCType(const ObjCMessageExpr * ME)949706b4fc4SDimitry Andric void WalkAST::checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME) {
950706b4fc4SDimitry Andric   if (!filter.check_decodeValueOfObjCType)
951706b4fc4SDimitry Andric     return;
952706b4fc4SDimitry Andric 
953706b4fc4SDimitry Andric   // Check availability of the secure alternative:
954706b4fc4SDimitry Andric   // iOS 11+, macOS 10.13+, tvOS 11+, and watchOS 4.0+
955706b4fc4SDimitry Andric   // FIXME: We probably shouldn't register the check if it's not available.
956706b4fc4SDimitry Andric   const TargetInfo &TI = AC->getASTContext().getTargetInfo();
957706b4fc4SDimitry Andric   const llvm::Triple &T = TI.getTriple();
958706b4fc4SDimitry Andric   const VersionTuple &VT = TI.getPlatformMinVersion();
959706b4fc4SDimitry Andric   switch (T.getOS()) {
960706b4fc4SDimitry Andric   case llvm::Triple::IOS:
961706b4fc4SDimitry Andric     if (VT < VersionTuple(11, 0))
962706b4fc4SDimitry Andric       return;
963706b4fc4SDimitry Andric     break;
964706b4fc4SDimitry Andric   case llvm::Triple::MacOSX:
965706b4fc4SDimitry Andric     if (VT < VersionTuple(10, 13))
966706b4fc4SDimitry Andric       return;
967706b4fc4SDimitry Andric     break;
968706b4fc4SDimitry Andric   case llvm::Triple::WatchOS:
969706b4fc4SDimitry Andric     if (VT < VersionTuple(4, 0))
970706b4fc4SDimitry Andric       return;
971706b4fc4SDimitry Andric     break;
972706b4fc4SDimitry Andric   case llvm::Triple::TvOS:
973706b4fc4SDimitry Andric     if (VT < VersionTuple(11, 0))
974706b4fc4SDimitry Andric       return;
975706b4fc4SDimitry Andric     break;
9764df029ccSDimitry Andric   case llvm::Triple::XROS:
9774df029ccSDimitry Andric     break;
978706b4fc4SDimitry Andric   default:
979706b4fc4SDimitry Andric     return;
980706b4fc4SDimitry Andric   }
981706b4fc4SDimitry Andric 
982706b4fc4SDimitry Andric   PathDiagnosticLocation MELoc =
983706b4fc4SDimitry Andric       PathDiagnosticLocation::createBegin(ME, BR.getSourceManager(), AC);
984706b4fc4SDimitry Andric   BR.EmitBasicReport(
985706b4fc4SDimitry Andric       AC->getDecl(), filter.checkName_decodeValueOfObjCType,
986706b4fc4SDimitry Andric       "Potential buffer overflow in '-decodeValueOfObjCType:at:'", "Security",
987706b4fc4SDimitry Andric       "Deprecated method '-decodeValueOfObjCType:at:' is insecure "
988706b4fc4SDimitry Andric       "as it can lead to potential buffer overflows. Use the safer "
989706b4fc4SDimitry Andric       "'-decodeValueOfObjCType:at:size:' method.",
990706b4fc4SDimitry Andric       MELoc, ME->getSourceRange());
991706b4fc4SDimitry Andric }
992706b4fc4SDimitry Andric 
993706b4fc4SDimitry Andric //===----------------------------------------------------------------------===//
994b1c73532SDimitry Andric // Check: The caller should always verify that the privileges
995b1c73532SDimitry Andric // were dropped successfully.
996b1c73532SDimitry Andric //
997b1c73532SDimitry Andric // Some library functions, like setuid() and setgid(), should always be used
998b1c73532SDimitry Andric // with a check of the return value to verify that the function completed
999b1c73532SDimitry Andric // successfully.  If the drop fails, the software will continue to run
1000b1c73532SDimitry Andric // with the raised privileges, which might provide additional access
1001b1c73532SDimitry Andric // to unprivileged users.
1002b1c73532SDimitry Andric //
1003b1c73532SDimitry Andric // (Note that this check predates __attribute__((warn_unused_result)).
1004b1c73532SDimitry Andric // Do we still need it now that we have a compiler warning for this?
1005b1c73532SDimitry Andric // Are these standard functions already annotated this way?)
10064c8b2481SRoman Divacky //===----------------------------------------------------------------------===//
10074c8b2481SRoman Divacky 
checkUncheckedReturnValue(CallExpr * CE)100801af97d3SDimitry Andric void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
1009dbe13110SDimitry Andric   if (!filter.check_UncheckedReturn)
1010dbe13110SDimitry Andric     return;
1011dbe13110SDimitry Andric 
10124c8b2481SRoman Divacky   const FunctionDecl *FD = CE->getDirectCallee();
10134c8b2481SRoman Divacky   if (!FD)
10144c8b2481SRoman Divacky     return;
10154c8b2481SRoman Divacky 
10169f4dbff6SDimitry Andric   if (II_setid[0] == nullptr) {
10174c8b2481SRoman Divacky     static const char * const identifiers[num_setids] = {
10184c8b2481SRoman Divacky       "setuid", "setgid", "seteuid", "setegid",
10194c8b2481SRoman Divacky       "setreuid", "setregid"
10204c8b2481SRoman Divacky     };
10214c8b2481SRoman Divacky 
10224c8b2481SRoman Divacky     for (size_t i = 0; i < num_setids; i++)
10234c8b2481SRoman Divacky       II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
10244c8b2481SRoman Divacky   }
10254c8b2481SRoman Divacky 
10264c8b2481SRoman Divacky   const IdentifierInfo *id = FD->getIdentifier();
10274c8b2481SRoman Divacky   size_t identifierid;
10284c8b2481SRoman Divacky 
10294c8b2481SRoman Divacky   for (identifierid = 0; identifierid < num_setids; identifierid++)
10304c8b2481SRoman Divacky     if (id == II_setid[identifierid])
10314c8b2481SRoman Divacky       break;
10324c8b2481SRoman Divacky 
10334c8b2481SRoman Divacky   if (identifierid >= num_setids)
10344c8b2481SRoman Divacky     return;
10354c8b2481SRoman Divacky 
1036bfef3995SDimitry Andric   const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
10374c8b2481SRoman Divacky   if (!FTP)
10384c8b2481SRoman Divacky     return;
10394c8b2481SRoman Divacky 
10404c8b2481SRoman Divacky   // Verify that the function takes one or two arguments (depending on
10414c8b2481SRoman Divacky   //   the function).
10429f4dbff6SDimitry Andric   if (FTP->getNumParams() != (identifierid < 4 ? 1 : 2))
10434c8b2481SRoman Divacky     return;
10444c8b2481SRoman Divacky 
10454c8b2481SRoman Divacky   // The arguments must be integers.
10469f4dbff6SDimitry Andric   for (unsigned i = 0; i < FTP->getNumParams(); i++)
10479f4dbff6SDimitry Andric     if (!FTP->getParamType(i)->isIntegralOrUnscopedEnumerationType())
10484c8b2481SRoman Divacky       return;
10494c8b2481SRoman Divacky 
10504c8b2481SRoman Divacky   // Issue a warning.
1051dbe13110SDimitry Andric   SmallString<256> buf1;
105211d2b2d2SRoman Divacky   llvm::raw_svector_ostream os1(buf1);
105336981b17SDimitry Andric   os1 << "Return value is not checked in call to '" << *FD << '\'';
10544c8b2481SRoman Divacky 
1055dbe13110SDimitry Andric   SmallString<256> buf2;
105611d2b2d2SRoman Divacky   llvm::raw_svector_ostream os2(buf2);
105736981b17SDimitry Andric   os2 << "The return value from the call to '" << *FD
105836981b17SDimitry Andric       << "' is not checked.  If an error occurs in '" << *FD
10594c8b2481SRoman Divacky       << "', the following code may execute with unexpected privileges";
10604c8b2481SRoman Divacky 
106136981b17SDimitry Andric   PathDiagnosticLocation CELoc =
106236981b17SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
10639f4dbff6SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
10649f4dbff6SDimitry Andric                      "Security", os2.str(), CELoc,
10659f4dbff6SDimitry Andric                      CE->getCallee()->getSourceRange());
10664c8b2481SRoman Divacky }
10674c8b2481SRoman Divacky 
10684c8b2481SRoman Divacky //===----------------------------------------------------------------------===//
1069bca07a45SDimitry Andric // SecuritySyntaxChecker
10704c8b2481SRoman Divacky //===----------------------------------------------------------------------===//
10714c8b2481SRoman Divacky 
1072bca07a45SDimitry Andric namespace {
107301af97d3SDimitry Andric class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {
1074bca07a45SDimitry Andric public:
1075dbe13110SDimitry Andric   ChecksFilter filter;
1076dbe13110SDimitry Andric 
checkASTCodeBody(const Decl * D,AnalysisManager & mgr,BugReporter & BR) const1077bca07a45SDimitry Andric   void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
1078bca07a45SDimitry Andric                         BugReporter &BR) const {
1079dbe13110SDimitry Andric     WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);
10804c8b2481SRoman Divacky     walker.Visit(D->getBody());
10814c8b2481SRoman Divacky   }
1082bca07a45SDimitry Andric };
1083bca07a45SDimitry Andric }
1084bca07a45SDimitry Andric 
registerSecuritySyntaxChecker(CheckerManager & mgr)108522989816SDimitry Andric void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {
108622989816SDimitry Andric   mgr.registerChecker<SecuritySyntaxChecker>();
108722989816SDimitry Andric }
108822989816SDimitry Andric 
shouldRegisterSecuritySyntaxChecker(const CheckerManager & mgr)1089cfca06d7SDimitry Andric bool ento::shouldRegisterSecuritySyntaxChecker(const CheckerManager &mgr) {
109022989816SDimitry Andric   return true;
109122989816SDimitry Andric }
109222989816SDimitry Andric 
1093dbe13110SDimitry Andric #define REGISTER_CHECKER(name)                                                 \
1094dbe13110SDimitry Andric   void ento::register##name(CheckerManager &mgr) {                             \
109522989816SDimitry Andric     SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>();  \
10969f4dbff6SDimitry Andric     checker->filter.check_##name = true;                                       \
1097519fc96cSDimitry Andric     checker->filter.checkName_##name = mgr.getCurrentCheckerName();            \
109822989816SDimitry Andric   }                                                                            \
109922989816SDimitry Andric                                                                                \
1100cfca06d7SDimitry Andric   bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
1101dbe13110SDimitry Andric 
110248675466SDimitry Andric REGISTER_CHECKER(bcmp)
110348675466SDimitry Andric REGISTER_CHECKER(bcopy)
110448675466SDimitry Andric REGISTER_CHECKER(bzero)
1105dbe13110SDimitry Andric REGISTER_CHECKER(gets)
1106dbe13110SDimitry Andric REGISTER_CHECKER(getpw)
1107dbe13110SDimitry Andric REGISTER_CHECKER(mkstemp)
1108dbe13110SDimitry Andric REGISTER_CHECKER(mktemp)
1109dbe13110SDimitry Andric REGISTER_CHECKER(strcpy)
1110dbe13110SDimitry Andric REGISTER_CHECKER(rand)
1111dbe13110SDimitry Andric REGISTER_CHECKER(vfork)
1112dbe13110SDimitry Andric REGISTER_CHECKER(FloatLoopCounter)
1113dbe13110SDimitry Andric REGISTER_CHECKER(UncheckedReturn)
111422989816SDimitry Andric REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling)
1115706b4fc4SDimitry Andric REGISTER_CHECKER(decodeValueOfObjCType)
1116