Coverage Report

Created: 2019-07-24 05:18

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
Line
Count
Source (jump to first uncovered line)
1
//=- NSErrorChecker.cpp - Coding conventions for uses of NSError -*- 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
//  This file defines a CheckNSError, a flow-insenstive check
10
//  that determines if an Objective-C class interface correctly returns
11
//  a non-void return type.
12
//
13
//  File under feature request PR 2600.
14
//
15
//===----------------------------------------------------------------------===//
16
17
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18
#include "clang/AST/Decl.h"
19
#include "clang/AST/DeclObjC.h"
20
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
21
#include "clang/StaticAnalyzer/Core/Checker.h"
22
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
23
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
24
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
25
#include "llvm/ADT/SmallString.h"
26
#include "llvm/Support/raw_ostream.h"
27
28
using namespace clang;
29
using namespace ento;
30
31
static bool IsNSError(QualType T, IdentifierInfo *II);
32
static bool IsCFError(QualType T, IdentifierInfo *II);
33
34
//===----------------------------------------------------------------------===//
35
// NSErrorMethodChecker
36
//===----------------------------------------------------------------------===//
37
38
namespace {
39
class NSErrorMethodChecker
40
    : public Checker< check::ASTDecl<ObjCMethodDecl> > {
41
  mutable IdentifierInfo *II;
42
43
public:
44
24
  NSErrorMethodChecker() : II(nullptr) {}
45
46
  void checkASTDecl(const ObjCMethodDecl *D,
47
                    AnalysisManager &mgr, BugReporter &BR) const;
48
};
49
}
50
51
void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D,
52
                                        AnalysisManager &mgr,
53
86
                                        BugReporter &BR) const {
54
86
  if (!D->isThisDeclarationADefinition())
55
62
    return;
56
24
  if (!D->getReturnType()->isVoidType())
57
15
    return;
58
9
59
9
  if (!II)
60
5
    II = &D->getASTContext().Idents.get("NSError");
61
9
62
9
  bool hasNSError = false;
63
9
  for (const auto *I : D->parameters())  {
64
4
    if (IsNSError(I->getType(), II)) {
65
1
      hasNSError = true;
66
1
      break;
67
1
    }
68
4
  }
69
9
70
9
  if (hasNSError) {
71
1
    const char *err = "Method accepting NSError** "
72
1
        "should have a non-void return value to indicate whether or not an "
73
1
        "error occurred";
74
1
    PathDiagnosticLocation L =
75
1
      PathDiagnosticLocation::create(D, BR.getSourceManager());
76
1
    BR.EmitBasicReport(D, this, "Bad return type when passing NSError**",
77
1
                       "Coding conventions (Apple)", err, L);
78
1
  }
79
9
}
80
81
//===----------------------------------------------------------------------===//
82
// CFErrorFunctionChecker
83
//===----------------------------------------------------------------------===//
84
85
namespace {
86
class CFErrorFunctionChecker
87
    : public Checker< check::ASTDecl<FunctionDecl> > {
88
  mutable IdentifierInfo *II;
89
90
public:
91
24
  CFErrorFunctionChecker() : II(nullptr) {}
92
93
  void checkASTDecl(const FunctionDecl *D,
94
                    AnalysisManager &mgr, BugReporter &BR) const;
95
};
96
}
97
98
void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
99
                                        AnalysisManager &mgr,
100
304
                                        BugReporter &BR) const {
101
304
  if (!D->doesThisDeclarationHaveABody())
102
132
    return;
103
172
  if (!D->getReturnType()->isVoidType())
104
56
    return;
105
116
106
116
  if (!II)
107
16
    II = &D->getASTContext().Idents.get("CFErrorRef");
108
116
109
116
  bool hasCFError = false;
110
116
  for (auto I : D->parameters())  {
111
52
    if (IsCFError(I->getType(), II)) {
112
1
      hasCFError = true;
113
1
      break;
114
1
    }
115
52
  }
116
116
117
116
  if (hasCFError) {
118
1
    const char *err = "Function accepting CFErrorRef* "
119
1
        "should have a non-void return value to indicate whether or not an "
120
1
        "error occurred";
121
1
    PathDiagnosticLocation L =
122
1
      PathDiagnosticLocation::create(D, BR.getSourceManager());
123
1
    BR.EmitBasicReport(D, this, "Bad return type when passing CFErrorRef*",
124
1
                       "Coding conventions (Apple)", err, L);
125
1
  }
126
116
}
127
128
//===----------------------------------------------------------------------===//
129
// NSOrCFErrorDerefChecker
130
//===----------------------------------------------------------------------===//
131
132
namespace {
133
134
class NSErrorDerefBug : public BugType {
135
public:
136
  NSErrorDerefBug(const CheckerBase *Checker)
137
      : BugType(Checker, "NSError** null dereference",
138
1
                "Coding conventions (Apple)") {}
139
};
140
141
class CFErrorDerefBug : public BugType {
142
public:
143
  CFErrorDerefBug(const CheckerBase *Checker)
144
      : BugType(Checker, "CFErrorRef* null dereference",
145
1
                "Coding conventions (Apple)") {}
146
};
147
148
}
149
150
namespace {
151
class NSOrCFErrorDerefChecker
152
    : public Checker< check::Location,
153
                        check::Event<ImplicitNullDerefEvent> > {
154
  mutable IdentifierInfo *NSErrorII, *CFErrorII;
155
  mutable std::unique_ptr<NSErrorDerefBug> NSBT;
156
  mutable std::unique_ptr<CFErrorDerefBug> CFBT;
157
public:
158
  bool ShouldCheckNSError, ShouldCheckCFError;
159
  NSOrCFErrorDerefChecker() : NSErrorII(nullptr), CFErrorII(nullptr),
160
24
                              ShouldCheckNSError(0), ShouldCheckCFError(0) { }
161
162
  void checkLocation(SVal loc, bool isLoad, const Stmt *S,
163
                     CheckerContext &C) const;
164
  void checkEvent(ImplicitNullDerefEvent event) const;
165
};
166
}
167
168
typedef llvm::ImmutableMap<SymbolRef, unsigned> ErrorOutFlag;
169
REGISTER_TRAIT_WITH_PROGRAMSTATE(NSErrorOut, ErrorOutFlag)
170
REGISTER_TRAIT_WITH_PROGRAMSTATE(CFErrorOut, ErrorOutFlag)
171
172
template <typename T>
173
3
static bool hasFlag(SVal val, ProgramStateRef state) {
174
3
  if (SymbolRef sym = val.getAsSymbol())
175
3
    if (const unsigned *attachedFlags = state->get<T>(sym))
176
2
      return *attachedFlags;
177
1
  return false;
178
1
}
NSErrorChecker.cpp:bool hasFlag<(anonymous namespace)::NSErrorOut>(clang::ento::SVal, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>)
Line
Count
Source
173
2
static bool hasFlag(SVal val, ProgramStateRef state) {
174
2
  if (SymbolRef sym = val.getAsSymbol())
175
2
    if (const unsigned *attachedFlags = state->get<T>(sym))
176
1
      return *attachedFlags;
177
1
  return false;
178
1
}
NSErrorChecker.cpp:bool hasFlag<(anonymous namespace)::CFErrorOut>(clang::ento::SVal, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>)
Line
Count
Source
173
1
static bool hasFlag(SVal val, ProgramStateRef state) {
174
1
  if (SymbolRef sym = val.getAsSymbol())
175
1
    if (const unsigned *attachedFlags = state->get<T>(sym))
176
1
      return *attachedFlags;
177
0
  return false;
178
0
}
179
180
template <typename T>
181
10
static void setFlag(ProgramStateRef state, SVal val, CheckerContext &C) {
182
10
  // We tag the symbol that the SVal wraps.
183
10
  if (SymbolRef sym = val.getAsSymbol())
184
10
    C.addTransition(state->set<T>(sym, true));
185
10
}
NSErrorChecker.cpp:void setFlag<(anonymous namespace)::NSErrorOut>(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::SVal, clang::ento::CheckerContext&)
Line
Count
Source
181
3
static void setFlag(ProgramStateRef state, SVal val, CheckerContext &C) {
182
3
  // We tag the symbol that the SVal wraps.
183
3
  if (SymbolRef sym = val.getAsSymbol())
184
3
    C.addTransition(state->set<T>(sym, true));
185
3
}
NSErrorChecker.cpp:void setFlag<(anonymous namespace)::CFErrorOut>(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::SVal, clang::ento::CheckerContext&)
Line
Count
Source
181
7
static void setFlag(ProgramStateRef state, SVal val, CheckerContext &C) {
182
7
  // We tag the symbol that the SVal wraps.
183
7
  if (SymbolRef sym = val.getAsSymbol())
184
7
    C.addTransition(state->set<T>(sym, true));
185
7
}
186
187
367
static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) {
188
367
  const StackFrameContext * SFC = C.getStackFrame();
189
367
  if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) {
190
367
    const MemRegion* R = X->getRegion();
191
367
    if (const VarRegion *VR = R->getAs<VarRegion>())
192
339
      if (const StackArgumentsSpaceRegion *
193
339
          stackReg = dyn_cast<StackArgumentsSpaceRegion>(VR->getMemorySpace()))
194
163
        if (stackReg->getStackFrame() == SFC)
195
163
          return VR->getValueType();
196
204
  }
197
204
198
204
  return QualType();
199
204
}
200
201
void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad,
202
                                            const Stmt *S,
203
402
                                            CheckerContext &C) const {
204
402
  if (!isLoad)
205
35
    return;
206
367
  if (loc.isUndef() || !loc.getAs<Loc>())
207
0
    return;
208
367
209
367
  ASTContext &Ctx = C.getASTContext();
210
367
  ProgramStateRef state = C.getState();
211
367
212
367
  // If we are loading from NSError**/CFErrorRef* parameter, mark the resulting
213
367
  // SVal so that we can later check it when handling the
214
367
  // ImplicitNullDerefEvent event.
215
367
  // FIXME: Cumbersome! Maybe add hook at construction of SVals at start of
216
367
  // function ?
217
367
218
367
  QualType parmT = parameterTypeFromSVal(loc, C);
219
367
  if (parmT.isNull())
220
204
    return;
221
163
222
163
  if (!NSErrorII)
223
15
    NSErrorII = &Ctx.Idents.get("NSError");
224
163
  if (!CFErrorII)
225
15
    CFErrorII = &Ctx.Idents.get("CFErrorRef");
226
163
227
163
  if (ShouldCheckNSError && IsNSError(parmT, NSErrorII)) {
228
3
    setFlag<NSErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C);
229
3
    return;
230
3
  }
231
160
232
160
  if (ShouldCheckCFError && IsCFError(parmT, CFErrorII)) {
233
7
    setFlag<CFErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C);
234
7
    return;
235
7
  }
236
160
}
237
238
2
void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const {
239
2
  if (event.IsLoad)
240
0
    return;
241
2
242
2
  SVal loc = event.Location;
243
2
  ProgramStateRef state = event.SinkNode->getState();
244
2
  BugReporter &BR = *event.BR;
245
2
246
2
  bool isNSError = hasFlag<NSErrorOut>(loc, state);
247
2
  bool isCFError = false;
248
2
  if (!isNSError)
249
1
    isCFError = hasFlag<CFErrorOut>(loc, state);
250
2
251
2
  if (!(isNSError || 
isCFError1
))
252
0
    return;
253
2
254
2
  // Storing to possible null NSError/CFErrorRef out parameter.
255
2
  SmallString<128> Buf;
256
2
  llvm::raw_svector_ostream os(Buf);
257
2
258
2
  os << "Potential null dereference.  According to coding standards ";
259
2
  os << (isNSError
260
2
         ? 
"in 'Creating and Returning NSError Objects' the parameter"1
261
2
         : 
"documented in CoreFoundation/CFError.h the parameter"1
);
262
2
263
2
  os  << " may be null";
264
2
265
2
  BugType *bug = nullptr;
266
2
  if (isNSError) {
267
1
    if (!NSBT)
268
1
      NSBT.reset(new NSErrorDerefBug(this));
269
1
    bug = NSBT.get();
270
1
  }
271
1
  else {
272
1
    if (!CFBT)
273
1
      CFBT.reset(new CFErrorDerefBug(this));
274
1
    bug = CFBT.get();
275
1
  }
276
2
  BR.emitReport(llvm::make_unique<BugReport>(*bug, os.str(), event.SinkNode));
277
2
}
278
279
167
static bool IsNSError(QualType T, IdentifierInfo *II) {
280
167
281
167
  const PointerType* PPT = T->getAs<PointerType>();
282
167
  if (!PPT)
283
74
    return false;
284
93
285
93
  const ObjCObjectPointerType* PT =
286
93
    PPT->getPointeeType()->getAs<ObjCObjectPointerType>();
287
93
288
93
  if (!PT)
289
83
    return false;
290
10
291
10
  const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
292
10
293
10
  // FIXME: Can ID ever be NULL?
294
10
  if (ID)
295
10
    return II == ID->getIdentifier();
296
0
297
0
  return false;
298
0
}
299
300
212
static bool IsCFError(QualType T, IdentifierInfo *II) {
301
212
  const PointerType* PPT = T->getAs<PointerType>();
302
212
  if (!PPT) 
return false89
;
303
123
304
123
  const TypedefType* TT = PPT->getPointeeType()->getAs<TypedefType>();
305
123
  if (!TT) 
return false105
;
306
18
307
18
  return TT->getDecl()->getIdentifier() == II;
308
18
}
309
310
24
void ento::registerNSOrCFErrorDerefChecker(CheckerManager &mgr) {
311
24
  mgr.registerChecker<NSOrCFErrorDerefChecker>();
312
24
}
313
314
23
bool ento::shouldRegisterNSOrCFErrorDerefChecker(const LangOptions &LO) {
315
23
  return true;
316
23
}
317
318
24
void ento::registerNSErrorChecker(CheckerManager &mgr) {
319
24
  mgr.registerChecker<NSErrorMethodChecker>();
320
24
  NSOrCFErrorDerefChecker *checker = mgr.getChecker<NSOrCFErrorDerefChecker>();
321
24
  checker->ShouldCheckNSError = true;
322
24
}
323
324
24
bool ento::shouldRegisterNSErrorChecker(const LangOptions &LO) {
325
24
  return true;
326
24
}
327
328
24
void ento::registerCFErrorChecker(CheckerManager &mgr) {
329
24
  mgr.registerChecker<CFErrorFunctionChecker>();
330
24
  NSOrCFErrorDerefChecker *checker = mgr.getChecker<NSOrCFErrorDerefChecker>();
331
24
  checker->ShouldCheckCFError = true;
332
24
}
333
334
24
bool ento::shouldRegisterCFErrorChecker(const LangOptions &LO) {
335
24
  return true;
336
24
}