Coverage Report

Created: 2019-07-24 05:18

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
Line
Count
Source (jump to first uncovered line)
1
//== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// An AST checker that looks for common pitfalls when using 'CFArray',
10
// 'CFDictionary', 'CFSet' APIs.
11
//
12
//===----------------------------------------------------------------------===//
13
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14
#include "clang/AST/StmtVisitor.h"
15
#include "clang/Analysis/AnalysisDeclContext.h"
16
#include "clang/Basic/TargetInfo.h"
17
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
18
#include "clang/StaticAnalyzer/Core/Checker.h"
19
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
20
#include "llvm/ADT/SmallString.h"
21
#include "llvm/Support/raw_ostream.h"
22
23
using namespace clang;
24
using namespace ento;
25
26
namespace {
27
class WalkAST : public StmtVisitor<WalkAST> {
28
  BugReporter &BR;
29
  const CheckerBase *Checker;
30
  AnalysisDeclContext* AC;
31
  ASTContext &ASTC;
32
  uint64_t PtrWidth;
33
34
  /// Check if the type has pointer size (very conservative).
35
21
  inline bool isPointerSize(const Type *T) {
36
21
    if (!T)
37
0
      return true;
38
21
    if (T->isIncompleteType())
39
0
      return true;
40
21
    return (ASTC.getTypeSize(T) == PtrWidth);
41
21
  }
42
43
  /// Check if the type is a pointer/array to pointer sized values.
44
24
  inline bool hasPointerToPointerSizedType(const Expr *E) {
45
24
    QualType T = E->getType();
46
24
47
24
    // The type could be either a pointer or array.
48
24
    const Type *TP = T.getTypePtr();
49
24
    QualType PointeeT = TP->getPointeeType();
50
24
    if (!PointeeT.isNull()) {
51
13
      // If the type is a pointer to an array, check the size of the array
52
13
      // elements. To avoid false positives coming from assumption that the
53
13
      // values x and &x are equal when x is an array.
54
13
      if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual())
55
3
        if (isPointerSize(TElem))
56
2
          return true;
57
11
58
11
      // Else, check the pointee size.
59
11
      return isPointerSize(PointeeT.getTypePtr());
60
11
    }
61
11
62
11
    if (const Type *TElem = TP->getArrayElementTypeNoTypeQual())
63
7
      return isPointerSize(TElem);
64
4
65
4
    // The type must be an array/pointer type.
66
4
67
4
    // This could be a null constant, which is allowed.
68
4
    return static_cast<bool>(
69
4
        E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull));
70
4
  }
71
72
public:
73
  WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac)
74
      : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()),
75
188
        PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {}
76
77
  // Statement visitor methods.
78
  void VisitChildren(Stmt *S);
79
2.54k
  void VisitStmt(Stmt *S) { VisitChildren(S); }
80
  void VisitCallExpr(CallExpr *CE);
81
};
82
} // end anonymous namespace
83
84
283
static StringRef getCalleeName(CallExpr *CE) {
85
283
  const FunctionDecl *FD = CE->getDirectCallee();
86
283
  if (!FD)
87
1
    return StringRef();
88
282
89
282
  IdentifierInfo *II = FD->getIdentifier();
90
282
  if (!II)   // if no identifier, not a simple C function
91
0
    return StringRef();
92
282
93
282
  return II->getName();
94
282
}
95
96
283
void WalkAST::VisitCallExpr(CallExpr *CE) {
97
283
  StringRef Name = getCalleeName(CE);
98
283
  if (Name.empty())
99
1
    return;
100
282
101
282
  const Expr *Arg = nullptr;
102
282
  unsigned ArgNum;
103
282
104
282
  if (Name.equals("CFArrayCreate") || 
Name.equals("CFSetCreate")264
) {
105
21
    if (CE->getNumArgs() != 4)
106
2
      return;
107
19
    ArgNum = 1;
108
19
    Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
109
19
    if (hasPointerToPointerSizedType(Arg))
110
14
        return;
111
261
  } else if (Name.equals("CFDictionaryCreate")) {
112
4
    if (CE->getNumArgs() != 6)
113
1
      return;
114
3
    // Check first argument.
115
3
    ArgNum = 1;
116
3
    Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
117
3
    if (hasPointerToPointerSizedType(Arg)) {
118
2
      // Check second argument.
119
2
      ArgNum = 2;
120
2
      Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
121
2
      if (hasPointerToPointerSizedType(Arg))
122
1
        // Both are good, return.
123
1
        return;
124
264
    }
125
3
  }
126
264
127
264
  if (Arg) {
128
7
    assert(ArgNum == 1 || ArgNum == 2);
129
7
130
7
    SmallString<64> BufName;
131
7
    llvm::raw_svector_ostream OsName(BufName);
132
7
    OsName << " Invalid use of '" << Name << "'" ;
133
7
134
7
    SmallString<256> Buf;
135
7
    llvm::raw_svector_ostream Os(Buf);
136
7
    // Use "second" and "third" since users will expect 1-based indexing
137
7
    // for parameter names when mentioned in prose.
138
7
    Os << " The "<< ((ArgNum == 1) ? 
"second"6
:
"third"1
) << " argument to '"
139
7
        << Name << "' must be a C array of pointer-sized values, not '"
140
7
        << Arg->getType().getAsString() << "'";
141
7
142
7
    PathDiagnosticLocation CELoc =
143
7
        PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
144
7
    BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(),
145
7
                       categories::CoreFoundationObjectiveC, Os.str(), CELoc,
146
7
                       Arg->getSourceRange());
147
7
  }
148
264
149
264
  // Recurse and check children.
150
264
  VisitChildren(CE);
151
264
}
152
153
2.80k
void WalkAST::VisitChildren(Stmt *S) {
154
2.80k
  for (Stmt *Child : S->children())
155
2.64k
    if (Child)
156
2.63k
      Visit(Child);
157
2.80k
}
158
159
namespace {
160
class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> {
161
public:
162
163
  void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
164
188
                        BugReporter &BR) const {
165
188
    WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D));
166
188
    walker.Visit(D->getBody());
167
188
  }
168
};
169
}
170
171
25
void ento::registerObjCContainersASTChecker(CheckerManager &mgr) {
172
25
  mgr.registerChecker<ObjCContainersASTChecker>();
173
25
}
174
175
25
bool ento::shouldRegisterObjCContainersASTChecker(const LangOptions &LO) {
176
25
  return true;
177
25
}