1218887Sdim//==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==//
2218887Sdim//
3218887Sdim//                     The LLVM Compiler Infrastructure
4218887Sdim//
5218887Sdim// This file is distributed under the University of Illinois Open Source
6218887Sdim// License. See LICENSE.TXT for details.
7218887Sdim//
8218887Sdim//===----------------------------------------------------------------------===//
9218887Sdim//
10218887Sdim//  This file defines a set of flow-insensitive security checks.
11218887Sdim//
12218887Sdim//===----------------------------------------------------------------------===//
13218887Sdim
14218887Sdim#include "ClangSACheckers.h"
15249423Sdim#include "clang/AST/StmtVisitor.h"
16226633Sdim#include "clang/Analysis/AnalysisContext.h"
17226633Sdim#include "clang/Basic/TargetInfo.h"
18249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
19221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
20226633Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
21234353Sdim#include "llvm/ADT/SmallString.h"
22226633Sdim#include "llvm/ADT/StringSwitch.h"
23218887Sdim#include "llvm/Support/raw_ostream.h"
24218887Sdim
25218887Sdimusing namespace clang;
26218887Sdimusing namespace ento;
27218887Sdim
28218887Sdimstatic bool isArc4RandomAvailable(const ASTContext &Ctx) {
29226633Sdim  const llvm::Triple &T = Ctx.getTargetInfo().getTriple();
30218887Sdim  return T.getVendor() == llvm::Triple::Apple ||
31218887Sdim         T.getOS() == llvm::Triple::FreeBSD ||
32218887Sdim         T.getOS() == llvm::Triple::NetBSD ||
33218887Sdim         T.getOS() == llvm::Triple::OpenBSD ||
34239462Sdim         T.getOS() == llvm::Triple::Bitrig ||
35218887Sdim         T.getOS() == llvm::Triple::DragonFly;
36218887Sdim}
37218887Sdim
38218887Sdimnamespace {
39234353Sdimstruct ChecksFilter {
40234353Sdim  DefaultBool check_gets;
41234353Sdim  DefaultBool check_getpw;
42234353Sdim  DefaultBool check_mktemp;
43234353Sdim  DefaultBool check_mkstemp;
44234353Sdim  DefaultBool check_strcpy;
45234353Sdim  DefaultBool check_rand;
46234353Sdim  DefaultBool check_vfork;
47234353Sdim  DefaultBool check_FloatLoopCounter;
48234353Sdim  DefaultBool check_UncheckedReturn;
49234353Sdim};
50234353Sdim
51218887Sdimclass WalkAST : public StmtVisitor<WalkAST> {
52218887Sdim  BugReporter &BR;
53234353Sdim  AnalysisDeclContext* AC;
54218887Sdim  enum { num_setids = 6 };
55218887Sdim  IdentifierInfo *II_setid[num_setids];
56218887Sdim
57218887Sdim  const bool CheckRand;
58234353Sdim  const ChecksFilter &filter;
59218887Sdim
60218887Sdimpublic:
61234353Sdim  WalkAST(BugReporter &br, AnalysisDeclContext* ac,
62234353Sdim          const ChecksFilter &f)
63226633Sdim  : BR(br), AC(ac), II_setid(),
64234353Sdim    CheckRand(isArc4RandomAvailable(BR.getContext())),
65234353Sdim    filter(f) {}
66218887Sdim
67218887Sdim  // Statement visitor methods.
68218887Sdim  void VisitCallExpr(CallExpr *CE);
69218887Sdim  void VisitForStmt(ForStmt *S);
70218887Sdim  void VisitCompoundStmt (CompoundStmt *S);
71218887Sdim  void VisitStmt(Stmt *S) { VisitChildren(S); }
72218887Sdim
73218887Sdim  void VisitChildren(Stmt *S);
74218887Sdim
75218887Sdim  // Helpers.
76221345Sdim  bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD);
77218887Sdim
78221345Sdim  typedef void (WalkAST::*FnCheck)(const CallExpr *,
79221345Sdim				   const FunctionDecl *);
80221345Sdim
81218887Sdim  // Checker-specific methods.
82221345Sdim  void checkLoopConditionForFloat(const ForStmt *FS);
83221345Sdim  void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD);
84221345Sdim  void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD);
85221345Sdim  void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD);
86234353Sdim  void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD);
87221345Sdim  void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD);
88221345Sdim  void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD);
89221345Sdim  void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD);
90221345Sdim  void checkCall_random(const CallExpr *CE, const FunctionDecl *FD);
91226633Sdim  void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD);
92221345Sdim  void checkUncheckedReturnValue(CallExpr *CE);
93218887Sdim};
94218887Sdim} // end anonymous namespace
95218887Sdim
96218887Sdim//===----------------------------------------------------------------------===//
97218887Sdim// AST walking.
98218887Sdim//===----------------------------------------------------------------------===//
99218887Sdim
100218887Sdimvoid WalkAST::VisitChildren(Stmt *S) {
101218887Sdim  for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I)
102218887Sdim    if (Stmt *child = *I)
103218887Sdim      Visit(child);
104218887Sdim}
105218887Sdim
106218887Sdimvoid WalkAST::VisitCallExpr(CallExpr *CE) {
107221345Sdim  // Get the callee.
108221345Sdim  const FunctionDecl *FD = CE->getDirectCallee();
109218887Sdim
110221345Sdim  if (!FD)
111221345Sdim    return;
112221345Sdim
113221345Sdim  // Get the name of the callee. If it's a builtin, strip off the prefix.
114221345Sdim  IdentifierInfo *II = FD->getIdentifier();
115221345Sdim  if (!II)   // if no identifier, not a simple C function
116221345Sdim    return;
117226633Sdim  StringRef Name = II->getName();
118221345Sdim  if (Name.startswith("__builtin_"))
119221345Sdim    Name = Name.substr(10);
120221345Sdim
121221345Sdim  // Set the evaluation function by switching on the callee name.
122221345Sdim  FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
123221345Sdim    .Case("gets", &WalkAST::checkCall_gets)
124221345Sdim    .Case("getpw", &WalkAST::checkCall_getpw)
125221345Sdim    .Case("mktemp", &WalkAST::checkCall_mktemp)
126234353Sdim    .Case("mkstemp", &WalkAST::checkCall_mkstemp)
127234353Sdim    .Case("mkdtemp", &WalkAST::checkCall_mkstemp)
128234353Sdim    .Case("mkstemps", &WalkAST::checkCall_mkstemp)
129221345Sdim    .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy)
130221345Sdim    .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat)
131221345Sdim    .Case("drand48", &WalkAST::checkCall_rand)
132221345Sdim    .Case("erand48", &WalkAST::checkCall_rand)
133221345Sdim    .Case("jrand48", &WalkAST::checkCall_rand)
134221345Sdim    .Case("lrand48", &WalkAST::checkCall_rand)
135221345Sdim    .Case("mrand48", &WalkAST::checkCall_rand)
136221345Sdim    .Case("nrand48", &WalkAST::checkCall_rand)
137221345Sdim    .Case("lcong48", &WalkAST::checkCall_rand)
138221345Sdim    .Case("rand", &WalkAST::checkCall_rand)
139221345Sdim    .Case("rand_r", &WalkAST::checkCall_rand)
140221345Sdim    .Case("random", &WalkAST::checkCall_random)
141226633Sdim    .Case("vfork", &WalkAST::checkCall_vfork)
142221345Sdim    .Default(NULL);
143221345Sdim
144221345Sdim  // If the callee isn't defined, it is not of security concern.
145221345Sdim  // Check and evaluate the call.
146221345Sdim  if (evalFunction)
147221345Sdim    (this->*evalFunction)(CE, FD);
148221345Sdim
149218887Sdim  // Recurse and check children.
150218887Sdim  VisitChildren(CE);
151218887Sdim}
152218887Sdim
153218887Sdimvoid WalkAST::VisitCompoundStmt(CompoundStmt *S) {
154218887Sdim  for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I)
155218887Sdim    if (Stmt *child = *I) {
156218887Sdim      if (CallExpr *CE = dyn_cast<CallExpr>(child))
157221345Sdim        checkUncheckedReturnValue(CE);
158218887Sdim      Visit(child);
159218887Sdim    }
160218887Sdim}
161218887Sdim
162218887Sdimvoid WalkAST::VisitForStmt(ForStmt *FS) {
163221345Sdim  checkLoopConditionForFloat(FS);
164218887Sdim
165218887Sdim  // Recurse and check children.
166218887Sdim  VisitChildren(FS);
167218887Sdim}
168218887Sdim
169218887Sdim//===----------------------------------------------------------------------===//
170218887Sdim// Check: floating poing variable used as loop counter.
171218887Sdim// Originally: <rdar://problem/6336718>
172218887Sdim// Implements: CERT security coding advisory FLP-30.
173218887Sdim//===----------------------------------------------------------------------===//
174218887Sdim
175218887Sdimstatic const DeclRefExpr*
176221345SdimgetIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) {
177218887Sdim  expr = expr->IgnoreParenCasts();
178218887Sdim
179218887Sdim  if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) {
180218887Sdim    if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
181218887Sdim          B->getOpcode() == BO_Comma))
182218887Sdim      return NULL;
183218887Sdim
184221345Sdim    if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y))
185218887Sdim      return lhs;
186218887Sdim
187221345Sdim    if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y))
188218887Sdim      return rhs;
189218887Sdim
190218887Sdim    return NULL;
191218887Sdim  }
192218887Sdim
193218887Sdim  if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {
194218887Sdim    const NamedDecl *ND = DR->getDecl();
195218887Sdim    return ND == x || ND == y ? DR : NULL;
196218887Sdim  }
197218887Sdim
198218887Sdim  if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr))
199218887Sdim    return U->isIncrementDecrementOp()
200221345Sdim      ? getIncrementedVar(U->getSubExpr(), x, y) : NULL;
201218887Sdim
202218887Sdim  return NULL;
203218887Sdim}
204218887Sdim
205218887Sdim/// CheckLoopConditionForFloat - This check looks for 'for' statements that
206218887Sdim///  use a floating point variable as a loop counter.
207218887Sdim///  CERT: FLP30-C, FLP30-CPP.
208218887Sdim///
209221345Sdimvoid WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
210234353Sdim  if (!filter.check_FloatLoopCounter)
211234353Sdim    return;
212234353Sdim
213218887Sdim  // Does the loop have a condition?
214218887Sdim  const Expr *condition = FS->getCond();
215218887Sdim
216218887Sdim  if (!condition)
217218887Sdim    return;
218218887Sdim
219218887Sdim  // Does the loop have an increment?
220218887Sdim  const Expr *increment = FS->getInc();
221218887Sdim
222218887Sdim  if (!increment)
223218887Sdim    return;
224218887Sdim
225218887Sdim  // Strip away '()' and casts.
226218887Sdim  condition = condition->IgnoreParenCasts();
227218887Sdim  increment = increment->IgnoreParenCasts();
228218887Sdim
229218887Sdim  // Is the loop condition a comparison?
230218887Sdim  const BinaryOperator *B = dyn_cast<BinaryOperator>(condition);
231218887Sdim
232218887Sdim  if (!B)
233218887Sdim    return;
234218887Sdim
235218887Sdim  // Is this a comparison?
236218887Sdim  if (!(B->isRelationalOp() || B->isEqualityOp()))
237218887Sdim    return;
238218887Sdim
239218887Sdim  // Are we comparing variables?
240218887Sdim  const DeclRefExpr *drLHS =
241218887Sdim    dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts());
242218887Sdim  const DeclRefExpr *drRHS =
243218887Sdim    dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts());
244218887Sdim
245218887Sdim  // Does at least one of the variables have a floating point type?
246218887Sdim  drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : NULL;
247218887Sdim  drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : NULL;
248218887Sdim
249218887Sdim  if (!drLHS && !drRHS)
250218887Sdim    return;
251218887Sdim
252218887Sdim  const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : NULL;
253218887Sdim  const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : NULL;
254218887Sdim
255218887Sdim  if (!vdLHS && !vdRHS)
256218887Sdim    return;
257218887Sdim
258218887Sdim  // Does either variable appear in increment?
259221345Sdim  const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS);
260218887Sdim
261218887Sdim  if (!drInc)
262218887Sdim    return;
263218887Sdim
264218887Sdim  // Emit the error.  First figure out which DeclRefExpr in the condition
265218887Sdim  // referenced the compared variable.
266243830Sdim  assert(drInc->getDecl());
267218887Sdim  const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS;
268218887Sdim
269226633Sdim  SmallVector<SourceRange, 2> ranges;
270234353Sdim  SmallString<256> sbuf;
271218887Sdim  llvm::raw_svector_ostream os(sbuf);
272218887Sdim
273218887Sdim  os << "Variable '" << drCond->getDecl()->getName()
274218887Sdim     << "' with floating point type '" << drCond->getType().getAsString()
275218887Sdim     << "' should not be used as a loop counter";
276218887Sdim
277218887Sdim  ranges.push_back(drCond->getSourceRange());
278218887Sdim  ranges.push_back(drInc->getSourceRange());
279218887Sdim
280218887Sdim  const char *bugType = "Floating point variable used as loop counter";
281226633Sdim
282226633Sdim  PathDiagnosticLocation FSLoc =
283226633Sdim    PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC);
284234353Sdim  BR.EmitBasicReport(AC->getDecl(),
285234353Sdim                     bugType, "Security", os.str(),
286263508Sdim                     FSLoc, ranges);
287218887Sdim}
288218887Sdim
289218887Sdim//===----------------------------------------------------------------------===//
290218887Sdim// Check: Any use of 'gets' is insecure.
291218887Sdim// Originally: <rdar://problem/6335715>
292218887Sdim// Implements (part of): 300-BSI (buildsecurityin.us-cert.gov)
293218887Sdim// CWE-242: Use of Inherently Dangerous Function
294218887Sdim//===----------------------------------------------------------------------===//
295218887Sdim
296221345Sdimvoid WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) {
297234353Sdim  if (!filter.check_gets)
298234353Sdim    return;
299234353Sdim
300263508Sdim  const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
301218887Sdim  if (!FPT)
302218887Sdim    return;
303218887Sdim
304218887Sdim  // Verify that the function takes a single argument.
305218887Sdim  if (FPT->getNumArgs() != 1)
306218887Sdim    return;
307218887Sdim
308218887Sdim  // Is the argument a 'char*'?
309263508Sdim  const PointerType *PT = FPT->getArgType(0)->getAs<PointerType>();
310218887Sdim  if (!PT)
311218887Sdim    return;
312218887Sdim
313218887Sdim  if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
314218887Sdim    return;
315218887Sdim
316218887Sdim  // Issue a warning.
317226633Sdim  PathDiagnosticLocation CELoc =
318226633Sdim    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
319234353Sdim  BR.EmitBasicReport(AC->getDecl(),
320234353Sdim                     "Potential buffer overflow in call to 'gets'",
321218887Sdim                     "Security",
322218887Sdim                     "Call to function 'gets' is extremely insecure as it can "
323218887Sdim                     "always result in a buffer overflow",
324263508Sdim                     CELoc, CE->getCallee()->getSourceRange());
325218887Sdim}
326218887Sdim
327218887Sdim//===----------------------------------------------------------------------===//
328218887Sdim// Check: Any use of 'getpwd' is insecure.
329218887Sdim// CWE-477: Use of Obsolete Functions
330218887Sdim//===----------------------------------------------------------------------===//
331218887Sdim
332221345Sdimvoid WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) {
333234353Sdim  if (!filter.check_getpw)
334234353Sdim    return;
335234353Sdim
336263508Sdim  const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
337218887Sdim  if (!FPT)
338218887Sdim    return;
339218887Sdim
340218887Sdim  // Verify that the function takes two arguments.
341218887Sdim  if (FPT->getNumArgs() != 2)
342218887Sdim    return;
343218887Sdim
344218887Sdim  // Verify the first argument type is integer.
345251662Sdim  if (!FPT->getArgType(0)->isIntegralOrUnscopedEnumerationType())
346218887Sdim    return;
347218887Sdim
348218887Sdim  // Verify the second argument type is char*.
349263508Sdim  const PointerType *PT = FPT->getArgType(1)->getAs<PointerType>();
350218887Sdim  if (!PT)
351218887Sdim    return;
352218887Sdim
353218887Sdim  if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
354218887Sdim    return;
355218887Sdim
356218887Sdim  // Issue a warning.
357226633Sdim  PathDiagnosticLocation CELoc =
358226633Sdim    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
359234353Sdim  BR.EmitBasicReport(AC->getDecl(),
360234353Sdim                     "Potential buffer overflow in call to 'getpw'",
361218887Sdim                     "Security",
362218887Sdim                     "The getpw() function is dangerous as it may overflow the "
363218887Sdim                     "provided buffer. It is obsoleted by getpwuid().",
364263508Sdim                     CELoc, CE->getCallee()->getSourceRange());
365218887Sdim}
366218887Sdim
367218887Sdim//===----------------------------------------------------------------------===//
368234353Sdim// Check: Any use of 'mktemp' is insecure.  It is obsoleted by mkstemp().
369218887Sdim// CWE-377: Insecure Temporary File
370218887Sdim//===----------------------------------------------------------------------===//
371218887Sdim
372221345Sdimvoid WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {
373234353Sdim  if (!filter.check_mktemp) {
374234353Sdim    // Fall back to the security check of looking for enough 'X's in the
375234353Sdim    // format string, since that is a less severe warning.
376234353Sdim    checkCall_mkstemp(CE, FD);
377234353Sdim    return;
378234353Sdim  }
379234353Sdim
380263508Sdim  const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
381218887Sdim  if(!FPT)
382218887Sdim    return;
383218887Sdim
384221345Sdim  // Verify that the function takes a single argument.
385218887Sdim  if (FPT->getNumArgs() != 1)
386218887Sdim    return;
387218887Sdim
388218887Sdim  // Verify that the argument is Pointer Type.
389263508Sdim  const PointerType *PT = FPT->getArgType(0)->getAs<PointerType>();
390218887Sdim  if (!PT)
391218887Sdim    return;
392218887Sdim
393218887Sdim  // Verify that the argument is a 'char*'.
394218887Sdim  if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
395218887Sdim    return;
396218887Sdim
397218887Sdim  // Issue a waring.
398226633Sdim  PathDiagnosticLocation CELoc =
399226633Sdim    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
400234353Sdim  BR.EmitBasicReport(AC->getDecl(),
401234353Sdim                     "Potential insecure temporary file in call 'mktemp'",
402218887Sdim                     "Security",
403218887Sdim                     "Call to function 'mktemp' is insecure as it always "
404234353Sdim                     "creates or uses insecure temporary file.  Use 'mkstemp' "
405234353Sdim                     "instead",
406263508Sdim                     CELoc, CE->getCallee()->getSourceRange());
407218887Sdim}
408218887Sdim
409234353Sdim
410218887Sdim//===----------------------------------------------------------------------===//
411234353Sdim// Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's.
412234353Sdim//===----------------------------------------------------------------------===//
413234353Sdim
414234353Sdimvoid WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {
415234353Sdim  if (!filter.check_mkstemp)
416234353Sdim    return;
417234353Sdim
418234353Sdim  StringRef Name = FD->getIdentifier()->getName();
419234353Sdim  std::pair<signed, signed> ArgSuffix =
420234353Sdim    llvm::StringSwitch<std::pair<signed, signed> >(Name)
421234353Sdim      .Case("mktemp", std::make_pair(0,-1))
422234353Sdim      .Case("mkstemp", std::make_pair(0,-1))
423234353Sdim      .Case("mkdtemp", std::make_pair(0,-1))
424234353Sdim      .Case("mkstemps", std::make_pair(0,1))
425234353Sdim      .Default(std::make_pair(-1, -1));
426234353Sdim
427234353Sdim  assert(ArgSuffix.first >= 0 && "Unsupported function");
428234353Sdim
429234353Sdim  // Check if the number of arguments is consistent with out expectations.
430234353Sdim  unsigned numArgs = CE->getNumArgs();
431234353Sdim  if ((signed) numArgs <= ArgSuffix.first)
432234353Sdim    return;
433234353Sdim
434234353Sdim  const StringLiteral *strArg =
435234353Sdim    dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first)
436234353Sdim                              ->IgnoreParenImpCasts());
437234353Sdim
438234353Sdim  // Currently we only handle string literals.  It is possible to do better,
439234353Sdim  // either by looking at references to const variables, or by doing real
440234353Sdim  // flow analysis.
441234353Sdim  if (!strArg || strArg->getCharByteWidth() != 1)
442234353Sdim    return;
443234353Sdim
444234353Sdim  // Count the number of X's, taking into account a possible cutoff suffix.
445234353Sdim  StringRef str = strArg->getString();
446234353Sdim  unsigned numX = 0;
447234353Sdim  unsigned n = str.size();
448234353Sdim
449234353Sdim  // Take into account the suffix.
450234353Sdim  unsigned suffix = 0;
451234353Sdim  if (ArgSuffix.second >= 0) {
452234353Sdim    const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second);
453234353Sdim    llvm::APSInt Result;
454234353Sdim    if (!suffixEx->EvaluateAsInt(Result, BR.getContext()))
455234353Sdim      return;
456234353Sdim    // FIXME: Issue a warning.
457234353Sdim    if (Result.isNegative())
458234353Sdim      return;
459234353Sdim    suffix = (unsigned) Result.getZExtValue();
460234353Sdim    n = (n > suffix) ? n - suffix : 0;
461234353Sdim  }
462234353Sdim
463234353Sdim  for (unsigned i = 0; i < n; ++i)
464234353Sdim    if (str[i] == 'X') ++numX;
465234353Sdim
466234353Sdim  if (numX >= 6)
467234353Sdim    return;
468234353Sdim
469234353Sdim  // Issue a warning.
470234353Sdim  PathDiagnosticLocation CELoc =
471234353Sdim    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
472234353Sdim  SmallString<512> buf;
473234353Sdim  llvm::raw_svector_ostream out(buf);
474234353Sdim  out << "Call to '" << Name << "' should have at least 6 'X's in the"
475234353Sdim    " format string to be secure (" << numX << " 'X'";
476234353Sdim  if (numX != 1)
477234353Sdim    out << 's';
478234353Sdim  out << " seen";
479234353Sdim  if (suffix) {
480234353Sdim    out << ", " << suffix << " character";
481234353Sdim    if (suffix > 1)
482234353Sdim      out << 's';
483234353Sdim    out << " used as a suffix";
484234353Sdim  }
485234353Sdim  out << ')';
486234353Sdim  BR.EmitBasicReport(AC->getDecl(),
487234353Sdim                     "Insecure temporary file creation", "Security",
488263508Sdim                     out.str(), CELoc, strArg->getSourceRange());
489234353Sdim}
490234353Sdim
491234353Sdim//===----------------------------------------------------------------------===//
492221345Sdim// Check: Any use of 'strcpy' is insecure.
493221345Sdim//
494221345Sdim// CWE-119: Improper Restriction of Operations within
495221345Sdim// the Bounds of a Memory Buffer
496218887Sdim//===----------------------------------------------------------------------===//
497221345Sdimvoid WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
498234353Sdim  if (!filter.check_strcpy)
499234353Sdim    return;
500234353Sdim
501221345Sdim  if (!checkCall_strCommon(CE, FD))
502221345Sdim    return;
503218887Sdim
504221345Sdim  // Issue a warning.
505226633Sdim  PathDiagnosticLocation CELoc =
506226633Sdim    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
507234353Sdim  BR.EmitBasicReport(AC->getDecl(),
508234353Sdim                     "Potential insecure memory buffer bounds restriction in "
509221345Sdim                     "call 'strcpy'",
510221345Sdim                     "Security",
511221345Sdim                     "Call to function 'strcpy' is insecure as it does not "
512234353Sdim                     "provide bounding of the memory buffer. Replace "
513234353Sdim                     "unbounded copy functions with analogous functions that "
514234353Sdim                     "support length arguments such as 'strlcpy'. CWE-119.",
515263508Sdim                     CELoc, CE->getCallee()->getSourceRange());
516221345Sdim}
517218887Sdim
518221345Sdim//===----------------------------------------------------------------------===//
519221345Sdim// Check: Any use of 'strcat' is insecure.
520221345Sdim//
521221345Sdim// CWE-119: Improper Restriction of Operations within
522221345Sdim// the Bounds of a Memory Buffer
523221345Sdim//===----------------------------------------------------------------------===//
524221345Sdimvoid WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
525234353Sdim  if (!filter.check_strcpy)
526234353Sdim    return;
527234353Sdim
528221345Sdim  if (!checkCall_strCommon(CE, FD))
529221345Sdim    return;
530221345Sdim
531221345Sdim  // Issue a warning.
532226633Sdim  PathDiagnosticLocation CELoc =
533226633Sdim    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
534234353Sdim  BR.EmitBasicReport(AC->getDecl(),
535234353Sdim                     "Potential insecure memory buffer bounds restriction in "
536234353Sdim                     "call 'strcat'",
537234353Sdim                     "Security",
538234353Sdim                     "Call to function 'strcat' is insecure as it does not "
539234353Sdim                     "provide bounding of the memory buffer. Replace "
540234353Sdim                     "unbounded copy functions with analogous functions that "
541234353Sdim                     "support length arguments such as 'strlcat'. CWE-119.",
542263508Sdim                     CELoc, CE->getCallee()->getSourceRange());
543221345Sdim}
544221345Sdim
545221345Sdim//===----------------------------------------------------------------------===//
546221345Sdim// Common check for str* functions with no bounds parameters.
547221345Sdim//===----------------------------------------------------------------------===//
548221345Sdimbool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) {
549263508Sdim  const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
550221345Sdim  if (!FPT)
551221345Sdim    return false;
552221345Sdim
553221345Sdim  // Verify the function takes two arguments, three in the _chk version.
554221345Sdim  int numArgs = FPT->getNumArgs();
555221345Sdim  if (numArgs != 2 && numArgs != 3)
556221345Sdim    return false;
557221345Sdim
558221345Sdim  // Verify the type for both arguments.
559221345Sdim  for (int i = 0; i < 2; i++) {
560221345Sdim    // Verify that the arguments are pointers.
561263508Sdim    const PointerType *PT = FPT->getArgType(i)->getAs<PointerType>();
562221345Sdim    if (!PT)
563221345Sdim      return false;
564221345Sdim
565221345Sdim    // Verify that the argument is a 'char*'.
566221345Sdim    if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
567221345Sdim      return false;
568218887Sdim  }
569218887Sdim
570221345Sdim  return true;
571221345Sdim}
572218887Sdim
573221345Sdim//===----------------------------------------------------------------------===//
574221345Sdim// Check: Linear congruent random number generators should not be used
575221345Sdim// Originally: <rdar://problem/63371000>
576221345Sdim// CWE-338: Use of cryptographically weak prng
577221345Sdim//===----------------------------------------------------------------------===//
578218887Sdim
579221345Sdimvoid WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
580234353Sdim  if (!filter.check_rand || !CheckRand)
581218887Sdim    return;
582218887Sdim
583263508Sdim  const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
584218887Sdim  if (!FTP)
585218887Sdim    return;
586218887Sdim
587218887Sdim  if (FTP->getNumArgs() == 1) {
588218887Sdim    // Is the argument an 'unsigned short *'?
589218887Sdim    // (Actually any integer type is allowed.)
590263508Sdim    const PointerType *PT = FTP->getArgType(0)->getAs<PointerType>();
591218887Sdim    if (!PT)
592218887Sdim      return;
593218887Sdim
594251662Sdim    if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType())
595218887Sdim      return;
596218887Sdim  }
597218887Sdim  else if (FTP->getNumArgs() != 0)
598218887Sdim    return;
599218887Sdim
600218887Sdim  // Issue a warning.
601234353Sdim  SmallString<256> buf1;
602218887Sdim  llvm::raw_svector_ostream os1(buf1);
603226633Sdim  os1 << '\'' << *FD << "' is a poor random number generator";
604218887Sdim
605234353Sdim  SmallString<256> buf2;
606218887Sdim  llvm::raw_svector_ostream os2(buf2);
607226633Sdim  os2 << "Function '" << *FD
608218887Sdim      << "' is obsolete because it implements a poor random number generator."
609218887Sdim      << "  Use 'arc4random' instead";
610218887Sdim
611226633Sdim  PathDiagnosticLocation CELoc =
612226633Sdim    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
613234353Sdim  BR.EmitBasicReport(AC->getDecl(), os1.str(), "Security", os2.str(),
614263508Sdim                     CELoc, CE->getCallee()->getSourceRange());
615218887Sdim}
616218887Sdim
617218887Sdim//===----------------------------------------------------------------------===//
618218887Sdim// Check: 'random' should not be used
619218887Sdim// Originally: <rdar://problem/63371000>
620218887Sdim//===----------------------------------------------------------------------===//
621218887Sdim
622221345Sdimvoid WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) {
623234353Sdim  if (!CheckRand || !filter.check_rand)
624218887Sdim    return;
625218887Sdim
626263508Sdim  const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
627218887Sdim  if (!FTP)
628218887Sdim    return;
629218887Sdim
630218887Sdim  // Verify that the function takes no argument.
631218887Sdim  if (FTP->getNumArgs() != 0)
632218887Sdim    return;
633218887Sdim
634218887Sdim  // Issue a warning.
635226633Sdim  PathDiagnosticLocation CELoc =
636226633Sdim    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
637234353Sdim  BR.EmitBasicReport(AC->getDecl(),
638234353Sdim                     "'random' is not a secure random number generator",
639218887Sdim                     "Security",
640218887Sdim                     "The 'random' function produces a sequence of values that "
641218887Sdim                     "an adversary may be able to predict.  Use 'arc4random' "
642263508Sdim                     "instead", CELoc, CE->getCallee()->getSourceRange());
643218887Sdim}
644218887Sdim
645218887Sdim//===----------------------------------------------------------------------===//
646226633Sdim// Check: 'vfork' should not be used.
647226633Sdim// POS33-C: Do not use vfork().
648226633Sdim//===----------------------------------------------------------------------===//
649226633Sdim
650226633Sdimvoid WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
651234353Sdim  if (!filter.check_vfork)
652234353Sdim    return;
653234353Sdim
654226633Sdim  // All calls to vfork() are insecure, issue a warning.
655226633Sdim  PathDiagnosticLocation CELoc =
656226633Sdim    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
657234353Sdim  BR.EmitBasicReport(AC->getDecl(),
658234353Sdim                     "Potential insecure implementation-specific behavior in "
659226633Sdim                     "call 'vfork'",
660226633Sdim                     "Security",
661226633Sdim                     "Call to function 'vfork' is insecure as it can lead to "
662226633Sdim                     "denial of service situations in the parent process. "
663226633Sdim                     "Replace calls to vfork with calls to the safer "
664226633Sdim                     "'posix_spawn' function",
665263508Sdim                     CELoc, CE->getCallee()->getSourceRange());
666226633Sdim}
667226633Sdim
668226633Sdim//===----------------------------------------------------------------------===//
669218887Sdim// Check: Should check whether privileges are dropped successfully.
670218887Sdim// Originally: <rdar://problem/6337132>
671218887Sdim//===----------------------------------------------------------------------===//
672218887Sdim
673221345Sdimvoid WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
674234353Sdim  if (!filter.check_UncheckedReturn)
675234353Sdim    return;
676234353Sdim
677218887Sdim  const FunctionDecl *FD = CE->getDirectCallee();
678218887Sdim  if (!FD)
679218887Sdim    return;
680218887Sdim
681218887Sdim  if (II_setid[0] == NULL) {
682218887Sdim    static const char * const identifiers[num_setids] = {
683218887Sdim      "setuid", "setgid", "seteuid", "setegid",
684218887Sdim      "setreuid", "setregid"
685218887Sdim    };
686218887Sdim
687218887Sdim    for (size_t i = 0; i < num_setids; i++)
688218887Sdim      II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
689218887Sdim  }
690218887Sdim
691218887Sdim  const IdentifierInfo *id = FD->getIdentifier();
692218887Sdim  size_t identifierid;
693218887Sdim
694218887Sdim  for (identifierid = 0; identifierid < num_setids; identifierid++)
695218887Sdim    if (id == II_setid[identifierid])
696218887Sdim      break;
697218887Sdim
698218887Sdim  if (identifierid >= num_setids)
699218887Sdim    return;
700218887Sdim
701263508Sdim  const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
702218887Sdim  if (!FTP)
703218887Sdim    return;
704218887Sdim
705218887Sdim  // Verify that the function takes one or two arguments (depending on
706218887Sdim  //   the function).
707218887Sdim  if (FTP->getNumArgs() != (identifierid < 4 ? 1 : 2))
708218887Sdim    return;
709218887Sdim
710218887Sdim  // The arguments must be integers.
711218887Sdim  for (unsigned i = 0; i < FTP->getNumArgs(); i++)
712251662Sdim    if (! FTP->getArgType(i)->isIntegralOrUnscopedEnumerationType())
713218887Sdim      return;
714218887Sdim
715218887Sdim  // Issue a warning.
716234353Sdim  SmallString<256> buf1;
717218887Sdim  llvm::raw_svector_ostream os1(buf1);
718226633Sdim  os1 << "Return value is not checked in call to '" << *FD << '\'';
719218887Sdim
720234353Sdim  SmallString<256> buf2;
721218887Sdim  llvm::raw_svector_ostream os2(buf2);
722226633Sdim  os2 << "The return value from the call to '" << *FD
723226633Sdim      << "' is not checked.  If an error occurs in '" << *FD
724218887Sdim      << "', the following code may execute with unexpected privileges";
725218887Sdim
726226633Sdim  PathDiagnosticLocation CELoc =
727226633Sdim    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
728234353Sdim  BR.EmitBasicReport(AC->getDecl(), os1.str(), "Security", os2.str(),
729263508Sdim                     CELoc, CE->getCallee()->getSourceRange());
730218887Sdim}
731218887Sdim
732218887Sdim//===----------------------------------------------------------------------===//
733218887Sdim// SecuritySyntaxChecker
734218887Sdim//===----------------------------------------------------------------------===//
735218887Sdim
736218887Sdimnamespace {
737221345Sdimclass SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {
738218887Sdimpublic:
739234353Sdim  ChecksFilter filter;
740234353Sdim
741218887Sdim  void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
742218887Sdim                        BugReporter &BR) const {
743234353Sdim    WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);
744218887Sdim    walker.Visit(D->getBody());
745218887Sdim  }
746218887Sdim};
747218887Sdim}
748218887Sdim
749234353Sdim#define REGISTER_CHECKER(name) \
750234353Sdimvoid ento::register##name(CheckerManager &mgr) {\
751234353Sdim  mgr.registerChecker<SecuritySyntaxChecker>()->filter.check_##name = true;\
752218887Sdim}
753234353Sdim
754234353SdimREGISTER_CHECKER(gets)
755234353SdimREGISTER_CHECKER(getpw)
756234353SdimREGISTER_CHECKER(mkstemp)
757234353SdimREGISTER_CHECKER(mktemp)
758234353SdimREGISTER_CHECKER(strcpy)
759234353SdimREGISTER_CHECKER(rand)
760234353SdimREGISTER_CHECKER(vfork)
761234353SdimREGISTER_CHECKER(FloatLoopCounter)
762234353SdimREGISTER_CHECKER(UncheckedReturn)
763234353Sdim
764234353Sdim
765