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