1234287Sdim//== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==//
2234287Sdim//
3234287Sdim//                     The LLVM Compiler Infrastructure
4234287Sdim//
5234287Sdim// This file is distributed under the University of Illinois Open Source
6234287Sdim// License. See LICENSE.TXT for details.
7234287Sdim//
8234287Sdim//===----------------------------------------------------------------------===//
9234287Sdim//
10234287Sdim// An AST checker that looks for common pitfalls when using 'CFArray',
11234287Sdim// 'CFDictionary', 'CFSet' APIs.
12234287Sdim//
13234287Sdim//===----------------------------------------------------------------------===//
14234287Sdim#include "ClangSACheckers.h"
15249423Sdim#include "clang/AST/StmtVisitor.h"
16234287Sdim#include "clang/Analysis/AnalysisContext.h"
17234287Sdim#include "clang/Basic/TargetInfo.h"
18249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
19234287Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
20234287Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
21234287Sdim#include "llvm/ADT/SmallString.h"
22234287Sdim#include "llvm/Support/raw_ostream.h"
23234287Sdim
24234287Sdimusing namespace clang;
25234287Sdimusing namespace ento;
26234287Sdim
27234287Sdimnamespace {
28234287Sdimclass WalkAST : public StmtVisitor<WalkAST> {
29234287Sdim  BugReporter &BR;
30276479Sdim  const CheckerBase *Checker;
31234287Sdim  AnalysisDeclContext* AC;
32234287Sdim  ASTContext &ASTC;
33234287Sdim  uint64_t PtrWidth;
34234287Sdim
35234287Sdim  /// Check if the type has pointer size (very conservative).
36234287Sdim  inline bool isPointerSize(const Type *T) {
37234287Sdim    if (!T)
38234287Sdim      return true;
39234287Sdim    if (T->isIncompleteType())
40234287Sdim      return true;
41234287Sdim    return (ASTC.getTypeSize(T) == PtrWidth);
42234287Sdim  }
43234287Sdim
44234287Sdim  /// Check if the type is a pointer/array to pointer sized values.
45234287Sdim  inline bool hasPointerToPointerSizedType(const Expr *E) {
46234287Sdim    QualType T = E->getType();
47234287Sdim
48234287Sdim    // The type could be either a pointer or array.
49234287Sdim    const Type *TP = T.getTypePtr();
50234287Sdim    QualType PointeeT = TP->getPointeeType();
51234287Sdim    if (!PointeeT.isNull()) {
52234287Sdim      // If the type is a pointer to an array, check the size of the array
53234287Sdim      // elements. To avoid false positives coming from assumption that the
54234287Sdim      // values x and &x are equal when x is an array.
55234287Sdim      if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual())
56234287Sdim        if (isPointerSize(TElem))
57234287Sdim          return true;
58234287Sdim
59234287Sdim      // Else, check the pointee size.
60234287Sdim      return isPointerSize(PointeeT.getTypePtr());
61234287Sdim    }
62234287Sdim
63234287Sdim    if (const Type *TElem = TP->getArrayElementTypeNoTypeQual())
64234287Sdim      return isPointerSize(TElem);
65234287Sdim
66234287Sdim    // The type must be an array/pointer type.
67234287Sdim
68234287Sdim    // This could be a null constant, which is allowed.
69296417Sdim    return static_cast<bool>(
70296417Sdim        E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull));
71234287Sdim  }
72234287Sdim
73234287Sdimpublic:
74276479Sdim  WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac)
75276479Sdim      : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()),
76276479Sdim        PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {}
77234287Sdim
78234287Sdim  // Statement visitor methods.
79234287Sdim  void VisitChildren(Stmt *S);
80234287Sdim  void VisitStmt(Stmt *S) { VisitChildren(S); }
81234287Sdim  void VisitCallExpr(CallExpr *CE);
82234287Sdim};
83234287Sdim} // end anonymous namespace
84234287Sdim
85234287Sdimstatic StringRef getCalleeName(CallExpr *CE) {
86234287Sdim  const FunctionDecl *FD = CE->getDirectCallee();
87234287Sdim  if (!FD)
88234287Sdim    return StringRef();
89234287Sdim
90234287Sdim  IdentifierInfo *II = FD->getIdentifier();
91234287Sdim  if (!II)   // if no identifier, not a simple C function
92234287Sdim    return StringRef();
93234287Sdim
94234287Sdim  return II->getName();
95234287Sdim}
96234287Sdim
97234287Sdimvoid WalkAST::VisitCallExpr(CallExpr *CE) {
98234287Sdim  StringRef Name = getCalleeName(CE);
99234287Sdim  if (Name.empty())
100234287Sdim    return;
101234287Sdim
102276479Sdim  const Expr *Arg = nullptr;
103243830Sdim  unsigned ArgNum;
104234287Sdim
105234287Sdim  if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) {
106243830Sdim    if (CE->getNumArgs() != 4)
107243830Sdim      return;
108234287Sdim    ArgNum = 1;
109234287Sdim    Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
110234287Sdim    if (hasPointerToPointerSizedType(Arg))
111234287Sdim        return;
112243830Sdim  } else if (Name.equals("CFDictionaryCreate")) {
113243830Sdim    if (CE->getNumArgs() != 6)
114243830Sdim      return;
115234287Sdim    // Check first argument.
116234287Sdim    ArgNum = 1;
117234287Sdim    Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
118234287Sdim    if (hasPointerToPointerSizedType(Arg)) {
119234287Sdim      // Check second argument.
120234287Sdim      ArgNum = 2;
121234287Sdim      Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
122234287Sdim      if (hasPointerToPointerSizedType(Arg))
123234287Sdim        // Both are good, return.
124234287Sdim        return;
125234287Sdim    }
126234287Sdim  }
127234287Sdim
128243830Sdim  if (Arg) {
129234287Sdim    assert(ArgNum == 1 || ArgNum == 2);
130234287Sdim
131243830Sdim    SmallString<64> BufName;
132234287Sdim    llvm::raw_svector_ostream OsName(BufName);
133234287Sdim    OsName << " Invalid use of '" << Name << "'" ;
134234287Sdim
135234287Sdim    SmallString<256> Buf;
136234287Sdim    llvm::raw_svector_ostream Os(Buf);
137243830Sdim    // Use "second" and "third" since users will expect 1-based indexing
138243830Sdim    // for parameter names when mentioned in prose.
139243830Sdim    Os << " The "<< ((ArgNum == 1) ? "second" : "third") << " argument to '"
140234287Sdim        << Name << "' must be a C array of pointer-sized values, not '"
141234287Sdim        << Arg->getType().getAsString() << "'";
142234287Sdim
143234287Sdim    PathDiagnosticLocation CELoc =
144234287Sdim        PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
145276479Sdim    BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(),
146276479Sdim                       categories::CoreFoundationObjectiveC, Os.str(), CELoc,
147276479Sdim                       Arg->getSourceRange());
148234287Sdim  }
149234287Sdim
150234287Sdim  // Recurse and check children.
151234287Sdim  VisitChildren(CE);
152234287Sdim}
153234287Sdim
154234287Sdimvoid WalkAST::VisitChildren(Stmt *S) {
155288943Sdim  for (Stmt *Child : S->children())
156288943Sdim    if (Child)
157288943Sdim      Visit(Child);
158234287Sdim}
159234287Sdim
160234287Sdimnamespace {
161234287Sdimclass ObjCContainersASTChecker : public Checker<check::ASTCodeBody> {
162234287Sdimpublic:
163234287Sdim
164234287Sdim  void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
165234287Sdim                        BugReporter &BR) const {
166276479Sdim    WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D));
167234287Sdim    walker.Visit(D->getBody());
168234287Sdim  }
169234287Sdim};
170234287Sdim}
171234287Sdim
172234287Sdimvoid ento::registerObjCContainersASTChecker(CheckerManager &mgr) {
173234287Sdim  mgr.registerChecker<ObjCContainersASTChecker>();
174234287Sdim}
175