1cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks//== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==//
2cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks//
3cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks//                     The LLVM Compiler Infrastructure
4cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks//
5cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks// This file is distributed under the University of Illinois Open Source
6cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks// License. See LICENSE.TXT for details.
7cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks//
8cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks//===----------------------------------------------------------------------===//
9cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks//
10cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks// An AST checker that looks for common pitfalls when using 'CFArray',
11cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks// 'CFDictionary', 'CFSet' APIs.
12cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks//
13cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks//===----------------------------------------------------------------------===//
14cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks#include "ClangSACheckers.h"
15cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks#include "clang/AST/StmtVisitor.h"
1655fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth#include "clang/Analysis/AnalysisContext.h"
17cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks#include "clang/Basic/TargetInfo.h"
18cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
1955fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth#include "clang/StaticAnalyzer/Core/Checker.h"
20cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
218fe83e1df954d72c0f4ffc15d20a5222ec151c21Benjamin Kramer#include "llvm/ADT/SmallString.h"
22cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks#include "llvm/Support/raw_ostream.h"
23cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
24cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaksusing namespace clang;
25cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaksusing namespace ento;
26cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
27cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaksnamespace {
28cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaksclass WalkAST : public StmtVisitor<WalkAST> {
29cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  BugReporter &BR;
30651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines  const CheckerBase *Checker;
31cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  AnalysisDeclContext* AC;
32cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  ASTContext &ASTC;
33cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  uint64_t PtrWidth;
34cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
35cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  /// Check if the type has pointer size (very conservative).
36cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  inline bool isPointerSize(const Type *T) {
37cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    if (!T)
38cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks      return true;
39cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    if (T->isIncompleteType())
40cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks      return true;
41cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    return (ASTC.getTypeSize(T) == PtrWidth);
42cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  }
43cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
44cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  /// Check if the type is a pointer/array to pointer sized values.
45cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  inline bool hasPointerToPointerSizedType(const Expr *E) {
46cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    QualType T = E->getType();
47cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
48cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    // The type could be either a pointer or array.
49cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    const Type *TP = T.getTypePtr();
50cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    QualType PointeeT = TP->getPointeeType();
51f196a90b26479a2c67959c6715491763cbc8ade1Anna Zaks    if (!PointeeT.isNull()) {
52f196a90b26479a2c67959c6715491763cbc8ade1Anna Zaks      // If the type is a pointer to an array, check the size of the array
53f196a90b26479a2c67959c6715491763cbc8ade1Anna Zaks      // elements. To avoid false positives coming from assumption that the
54f196a90b26479a2c67959c6715491763cbc8ade1Anna Zaks      // values x and &x are equal when x is an array.
55f196a90b26479a2c67959c6715491763cbc8ade1Anna Zaks      if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual())
56f196a90b26479a2c67959c6715491763cbc8ade1Anna Zaks        if (isPointerSize(TElem))
57f196a90b26479a2c67959c6715491763cbc8ade1Anna Zaks          return true;
58f196a90b26479a2c67959c6715491763cbc8ade1Anna Zaks
59f196a90b26479a2c67959c6715491763cbc8ade1Anna Zaks      // Else, check the pointee size.
60cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks      return isPointerSize(PointeeT.getTypePtr());
61f196a90b26479a2c67959c6715491763cbc8ade1Anna Zaks    }
62cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
63cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    if (const Type *TElem = TP->getArrayElementTypeNoTypeQual())
64cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks      return isPointerSize(TElem);
65cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
66cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    // The type must be an array/pointer type.
67cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
68cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    // This could be a null constant, which is allowed.
694967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar    return static_cast<bool>(
704967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar        E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull));
71cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  }
72cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
73cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zakspublic:
74651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines  WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac)
75651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines      : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()),
76651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines        PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {}
77cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
78cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  // Statement visitor methods.
79cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  void VisitChildren(Stmt *S);
80cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  void VisitStmt(Stmt *S) { VisitChildren(S); }
81cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  void VisitCallExpr(CallExpr *CE);
82cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks};
83cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks} // end anonymous namespace
84cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
85cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaksstatic StringRef getCalleeName(CallExpr *CE) {
86cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  const FunctionDecl *FD = CE->getDirectCallee();
87cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  if (!FD)
88cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    return StringRef();
89cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
90cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  IdentifierInfo *II = FD->getIdentifier();
91cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  if (!II)   // if no identifier, not a simple C function
92cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    return StringRef();
93cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
94cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  return II->getName();
95cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks}
96cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
97cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaksvoid WalkAST::VisitCallExpr(CallExpr *CE) {
98cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  StringRef Name = getCalleeName(CE);
99cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  if (Name.empty())
100cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    return;
101cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
1026bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines  const Expr *Arg = nullptr;
103bc9e5ffb0d0757238c071764e4bc1fc8a1521097Jordan Rose  unsigned ArgNum;
104cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
105cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) {
106441ee1dfa5ff8d904ad07dc3b7837c44d9f173ebTed Kremenek    if (CE->getNumArgs() != 4)
107441ee1dfa5ff8d904ad07dc3b7837c44d9f173ebTed Kremenek      return;
108cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    ArgNum = 1;
109cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
110cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    if (hasPointerToPointerSizedType(Arg))
111cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks        return;
112bc9e5ffb0d0757238c071764e4bc1fc8a1521097Jordan Rose  } else if (Name.equals("CFDictionaryCreate")) {
113441ee1dfa5ff8d904ad07dc3b7837c44d9f173ebTed Kremenek    if (CE->getNumArgs() != 6)
114441ee1dfa5ff8d904ad07dc3b7837c44d9f173ebTed Kremenek      return;
115cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    // Check first argument.
116cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    ArgNum = 1;
117cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
118cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    if (hasPointerToPointerSizedType(Arg)) {
119cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks      // Check second argument.
120cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks      ArgNum = 2;
121cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks      Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
122cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks      if (hasPointerToPointerSizedType(Arg))
123cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks        // Both are good, return.
124cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks        return;
125cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    }
126cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  }
127cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
128bc9e5ffb0d0757238c071764e4bc1fc8a1521097Jordan Rose  if (Arg) {
129cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    assert(ArgNum == 1 || ArgNum == 2);
130cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
131bc9e5ffb0d0757238c071764e4bc1fc8a1521097Jordan Rose    SmallString<64> BufName;
132cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    llvm::raw_svector_ostream OsName(BufName);
133cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    OsName << " Invalid use of '" << Name << "'" ;
134cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
135f7ccbad5d9949e7ddd1cbef43d482553b811e026Dylan Noblesmith    SmallString<256> Buf;
136cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    llvm::raw_svector_ostream Os(Buf);
1372ab012a6de2b2769ec7ad99c4b61788cc5175d17Ted Kremenek    // Use "second" and "third" since users will expect 1-based indexing
1382ab012a6de2b2769ec7ad99c4b61788cc5175d17Ted Kremenek    // for parameter names when mentioned in prose.
1392ab012a6de2b2769ec7ad99c4b61788cc5175d17Ted Kremenek    Os << " The "<< ((ArgNum == 1) ? "second" : "third") << " argument to '"
140cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks        << Name << "' must be a C array of pointer-sized values, not '"
141cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks        << Arg->getType().getAsString() << "'";
142cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
143cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    PathDiagnosticLocation CELoc =
144cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks        PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
145651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines    BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(),
146651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines                       categories::CoreFoundationObjectiveC, Os.str(), CELoc,
147651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines                       Arg->getSourceRange());
148cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  }
149cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
150cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  // Recurse and check children.
151cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  VisitChildren(CE);
152cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks}
153cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
154cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaksvoid WalkAST::VisitChildren(Stmt *S) {
15587d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar  for (Stmt *Child : S->children())
15687d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar    if (Child)
15787d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar      Visit(Child);
158cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks}
159cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
160cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaksnamespace {
161cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaksclass ObjCContainersASTChecker : public Checker<check::ASTCodeBody> {
162cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zakspublic:
163cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
164cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
165cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks                        BugReporter &BR) const {
166651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines    WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D));
167cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks    walker.Visit(D->getBody());
168cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  }
169cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks};
170cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks}
171cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks
172cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaksvoid ento::registerObjCContainersASTChecker(CheckerManager &mgr) {
173cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks  mgr.registerChecker<ObjCContainersASTChecker>();
174cbd273387a61409f179fcfe8460a8733fcf8f872Anna Zaks}
175