blob: 559c75d7a5b06f71bb1ac1d07d8e80bae306870c [file] [log] [blame]
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +00001//=- NSErrorChecker.cpp - Coding conventions for uses of NSError -*- C++ -*-==//
Ted Kremenekfc3abeb2008-09-18 06:33:41 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines a CheckNSError, a flow-insenstive check
11// that determines if an Objective-C class interface correctly returns
12// a non-void return type.
13//
14// File under feature request PR 2600.
15//
16//===----------------------------------------------------------------------===//
17
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000018#include "ClangSACheckers.h"
Chandler Carruth3a022472012-12-04 09:13:33 +000019#include "clang/AST/Decl.h"
20#include "clang/AST/DeclObjC.h"
21#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +000022#include "clang/StaticAnalyzer/Core/Checker.h"
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000023#include "clang/StaticAnalyzer/Core/CheckerManager.h"
24#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
Ted Kremenek001fd5b2011-08-15 22:09:50 +000025#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
Ted Kremenek9deaef72013-01-04 19:04:40 +000026#include "llvm/ADT/SmallString.h"
Benjamin Kramer444a1302012-12-01 17:12:56 +000027#include "llvm/Support/raw_ostream.h"
Ted Kremenekfc3abeb2008-09-18 06:33:41 +000028
29using namespace clang;
Ted Kremenek98857c92010-12-23 07:20:52 +000030using namespace ento;
Ted Kremenekfc3abeb2008-09-18 06:33:41 +000031
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +000032static bool IsNSError(QualType T, IdentifierInfo *II);
33static bool IsCFError(QualType T, IdentifierInfo *II);
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000034
35//===----------------------------------------------------------------------===//
36// NSErrorMethodChecker
37//===----------------------------------------------------------------------===//
38
Ted Kremenekf0673e42008-09-18 21:25:13 +000039namespace {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000040class NSErrorMethodChecker
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +000041 : public Checker< check::ASTDecl<ObjCMethodDecl> > {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000042 mutable IdentifierInfo *II;
Mike Stump11289f42009-09-09 15:08:12 +000043
Ted Kremenekf0673e42008-09-18 21:25:13 +000044public:
Craig Topper0dbb7832014-05-27 02:45:47 +000045 NSErrorMethodChecker() : II(nullptr) {}
Mike Stump11289f42009-09-09 15:08:12 +000046
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000047 void checkASTDecl(const ObjCMethodDecl *D,
48 AnalysisManager &mgr, BugReporter &BR) const;
49};
Alexander Kornienkoab9db512015-06-22 23:07:51 +000050}
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000051
52void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D,
53 AnalysisManager &mgr,
54 BugReporter &BR) const {
55 if (!D->isThisDeclarationADefinition())
56 return;
Alp Toker314cc812014-01-25 16:55:45 +000057 if (!D->getReturnType()->isVoidType())
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000058 return;
59
60 if (!II)
Ted Kremenek3a0678e2015-09-08 03:50:52 +000061 II = &D->getASTContext().Idents.get("NSError");
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000062
63 bool hasNSError = false;
David Majnemer59f77922016-06-24 04:05:48 +000064 for (const auto *I : D->parameters()) {
Aaron Ballman43b68be2014-03-07 17:50:17 +000065 if (IsNSError(I->getType(), II)) {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000066 hasNSError = true;
67 break;
68 }
69 }
70
71 if (hasNSError) {
72 const char *err = "Method accepting NSError** "
73 "should have a non-void return value to indicate whether or not an "
74 "error occurred";
Anna Zaksc29bed32011-09-20 21:38:35 +000075 PathDiagnosticLocation L =
76 PathDiagnosticLocation::create(D, BR.getSourceManager());
Alexander Kornienko4aca9b12014-02-11 21:49:21 +000077 BR.EmitBasicReport(D, this, "Bad return type when passing NSError**",
Anna Zaksc29bed32011-09-20 21:38:35 +000078 "Coding conventions (Apple)", err, L);
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000079 }
80}
81
82//===----------------------------------------------------------------------===//
83// CFErrorFunctionChecker
84//===----------------------------------------------------------------------===//
85
86namespace {
87class CFErrorFunctionChecker
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +000088 : public Checker< check::ASTDecl<FunctionDecl> > {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000089 mutable IdentifierInfo *II;
90
91public:
Craig Topper0dbb7832014-05-27 02:45:47 +000092 CFErrorFunctionChecker() : II(nullptr) {}
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000093
94 void checkASTDecl(const FunctionDecl *D,
95 AnalysisManager &mgr, BugReporter &BR) const;
96};
Alexander Kornienkoab9db512015-06-22 23:07:51 +000097}
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000098
99void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
100 AnalysisManager &mgr,
101 BugReporter &BR) const {
Alexis Hunt4a8ea102011-05-06 20:44:56 +0000102 if (!D->doesThisDeclarationHaveABody())
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000103 return;
Alp Toker314cc812014-01-25 16:55:45 +0000104 if (!D->getReturnType()->isVoidType())
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000105 return;
106
107 if (!II)
Ted Kremenek3a0678e2015-09-08 03:50:52 +0000108 II = &D->getASTContext().Idents.get("CFErrorRef");
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000109
110 bool hasCFError = false;
David Majnemer59f77922016-06-24 04:05:48 +0000111 for (auto I : D->parameters()) {
Aaron Ballmanf6bf62e2014-03-07 15:12:56 +0000112 if (IsCFError(I->getType(), II)) {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000113 hasCFError = true;
114 break;
115 }
116 }
117
118 if (hasCFError) {
119 const char *err = "Function accepting CFErrorRef* "
120 "should have a non-void return value to indicate whether or not an "
121 "error occurred";
Anna Zaksc29bed32011-09-20 21:38:35 +0000122 PathDiagnosticLocation L =
123 PathDiagnosticLocation::create(D, BR.getSourceManager());
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000124 BR.EmitBasicReport(D, this, "Bad return type when passing CFErrorRef*",
Anna Zaksc29bed32011-09-20 21:38:35 +0000125 "Coding conventions (Apple)", err, L);
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000126 }
127}
128
129//===----------------------------------------------------------------------===//
130// NSOrCFErrorDerefChecker
131//===----------------------------------------------------------------------===//
132
133namespace {
134
135class NSErrorDerefBug : public BugType {
136public:
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000137 NSErrorDerefBug(const CheckerBase *Checker)
138 : BugType(Checker, "NSError** null dereference",
139 "Coding conventions (Apple)") {}
Mike Stump11289f42009-09-09 15:08:12 +0000140};
141
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000142class CFErrorDerefBug : public BugType {
143public:
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000144 CFErrorDerefBug(const CheckerBase *Checker)
145 : BugType(Checker, "CFErrorRef* null dereference",
146 "Coding conventions (Apple)") {}
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000147};
Ted Kremenekf0673e42008-09-18 21:25:13 +0000148
Alexander Kornienkoab9db512015-06-22 23:07:51 +0000149}
Ted Kremenekf0673e42008-09-18 21:25:13 +0000150
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000151namespace {
152class NSOrCFErrorDerefChecker
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000153 : public Checker< check::Location,
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000154 check::Event<ImplicitNullDerefEvent> > {
155 mutable IdentifierInfo *NSErrorII, *CFErrorII;
Nico Weber1fa575d2014-05-07 21:28:03 +0000156 mutable std::unique_ptr<NSErrorDerefBug> NSBT;
157 mutable std::unique_ptr<CFErrorDerefBug> CFBT;
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000158public:
159 bool ShouldCheckNSError, ShouldCheckCFError;
Craig Topper0dbb7832014-05-27 02:45:47 +0000160 NSOrCFErrorDerefChecker() : NSErrorII(nullptr), CFErrorII(nullptr),
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000161 ShouldCheckNSError(0), ShouldCheckCFError(0) { }
Mike Stump11289f42009-09-09 15:08:12 +0000162
Anna Zaks3e0f4152011-10-06 00:43:15 +0000163 void checkLocation(SVal loc, bool isLoad, const Stmt *S,
164 CheckerContext &C) const;
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000165 void checkEvent(ImplicitNullDerefEvent event) const;
166};
Alexander Kornienkoab9db512015-06-22 23:07:51 +0000167}
Ted Kremenekf0673e42008-09-18 21:25:13 +0000168
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000169typedef llvm::ImmutableMap<SymbolRef, unsigned> ErrorOutFlag;
Jordan Roseb9ed61f2012-11-02 01:54:42 +0000170REGISTER_TRAIT_WITH_PROGRAMSTATE(NSErrorOut, ErrorOutFlag)
171REGISTER_TRAIT_WITH_PROGRAMSTATE(CFErrorOut, ErrorOutFlag)
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000172
173template <typename T>
Ted Kremenek49b1e382012-01-26 21:29:00 +0000174static bool hasFlag(SVal val, ProgramStateRef state) {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000175 if (SymbolRef sym = val.getAsSymbol())
176 if (const unsigned *attachedFlags = state->get<T>(sym))
177 return *attachedFlags;
178 return false;
179}
180
181template <typename T>
Ted Kremenek49b1e382012-01-26 21:29:00 +0000182static void setFlag(ProgramStateRef state, SVal val, CheckerContext &C) {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000183 // We tag the symbol that the SVal wraps.
184 if (SymbolRef sym = val.getAsSymbol())
Anna Zaksda4c8d62011-10-26 21:06:34 +0000185 C.addTransition(state->set<T>(sym, true));
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000186}
187
Argyrios Kyrtzidisa9a0f5d2011-03-01 01:59:41 +0000188static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) {
189 const StackFrameContext *
Anna Zaksc9abbe22011-10-26 21:06:44 +0000190 SFC = C.getLocationContext()->getCurrentStackFrame();
David Blaikie05785d12013-02-20 22:23:23 +0000191 if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) {
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000192 const MemRegion* R = X->getRegion();
193 if (const VarRegion *VR = R->getAs<VarRegion>())
Argyrios Kyrtzidisa9a0f5d2011-03-01 01:59:41 +0000194 if (const StackArgumentsSpaceRegion *
195 stackReg = dyn_cast<StackArgumentsSpaceRegion>(VR->getMemorySpace()))
196 if (stackReg->getStackFrame() == SFC)
197 return VR->getValueType();
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000198 }
199
200 return QualType();
201}
202
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000203void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad,
Anna Zaks3e0f4152011-10-06 00:43:15 +0000204 const Stmt *S,
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000205 CheckerContext &C) const {
206 if (!isLoad)
207 return;
David Blaikie2fdacbc2013-02-20 05:52:05 +0000208 if (loc.isUndef() || !loc.getAs<Loc>())
Ted Kremenekf0673e42008-09-18 21:25:13 +0000209 return;
Mike Stump11289f42009-09-09 15:08:12 +0000210
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000211 ASTContext &Ctx = C.getASTContext();
Ted Kremenek49b1e382012-01-26 21:29:00 +0000212 ProgramStateRef state = C.getState();
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000213
214 // If we are loading from NSError**/CFErrorRef* parameter, mark the resulting
215 // SVal so that we can later check it when handling the
216 // ImplicitNullDerefEvent event.
217 // FIXME: Cumbersome! Maybe add hook at construction of SVals at start of
218 // function ?
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000219
Argyrios Kyrtzidisa9a0f5d2011-03-01 01:59:41 +0000220 QualType parmT = parameterTypeFromSVal(loc, C);
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000221 if (parmT.isNull())
222 return;
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000223
224 if (!NSErrorII)
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000225 NSErrorII = &Ctx.Idents.get("NSError");
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000226 if (!CFErrorII)
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000227 CFErrorII = &Ctx.Idents.get("CFErrorRef");
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000228
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000229 if (ShouldCheckNSError && IsNSError(parmT, NSErrorII)) {
David Blaikie2fdacbc2013-02-20 05:52:05 +0000230 setFlag<NSErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C);
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000231 return;
232 }
233
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000234 if (ShouldCheckCFError && IsCFError(parmT, CFErrorII)) {
David Blaikie2fdacbc2013-02-20 05:52:05 +0000235 setFlag<CFErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C);
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000236 return;
237 }
238}
239
240void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const {
241 if (event.IsLoad)
Ted Kremenek3aa89a92008-10-01 23:24:09 +0000242 return;
Mike Stump11289f42009-09-09 15:08:12 +0000243
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000244 SVal loc = event.Location;
Ted Kremenek49b1e382012-01-26 21:29:00 +0000245 ProgramStateRef state = event.SinkNode->getState();
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000246 BugReporter &BR = *event.BR;
Mike Stump11289f42009-09-09 15:08:12 +0000247
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000248 bool isNSError = hasFlag<NSErrorOut>(loc, state);
249 bool isCFError = false;
250 if (!isNSError)
251 isCFError = hasFlag<CFErrorOut>(loc, state);
Ted Kremenekf0673e42008-09-18 21:25:13 +0000252
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000253 if (!(isNSError || isCFError))
254 return;
Mike Stump11289f42009-09-09 15:08:12 +0000255
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000256 // Storing to possible null NSError/CFErrorRef out parameter.
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000257 SmallString<128> Buf;
Ted Kremenek9deaef72013-01-04 19:04:40 +0000258 llvm::raw_svector_ostream os(Buf);
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000259
Ted Kremenek9deaef72013-01-04 19:04:40 +0000260 os << "Potential null dereference. According to coding standards ";
261 os << (isNSError
262 ? "in 'Creating and Returning NSError Objects' the parameter"
263 : "documented in CoreFoundation/CFError.h the parameter");
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000264
Ted Kremenek9deaef72013-01-04 19:04:40 +0000265 os << " may be null";
Mike Stump11289f42009-09-09 15:08:12 +0000266
Craig Topper0dbb7832014-05-27 02:45:47 +0000267 BugType *bug = nullptr;
Nico Weber1fa575d2014-05-07 21:28:03 +0000268 if (isNSError) {
269 if (!NSBT)
270 NSBT.reset(new NSErrorDerefBug(this));
271 bug = NSBT.get();
272 }
273 else {
274 if (!CFBT)
275 CFBT.reset(new CFErrorDerefBug(this));
276 bug = CFBT.get();
277 }
Aaron Ballman8d3a7a52015-06-23 13:15:32 +0000278 BR.emitReport(llvm::make_unique<BugReport>(*bug, os.str(), event.SinkNode));
Ted Kremenek3aa89a92008-10-01 23:24:09 +0000279}
280
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000281static bool IsNSError(QualType T, IdentifierInfo *II) {
Ted Kremenekf0673e42008-09-18 21:25:13 +0000282
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000283 const PointerType* PPT = T->getAs<PointerType>();
Steve Naroff7cae42b2009-07-10 23:34:53 +0000284 if (!PPT)
285 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000286
Steve Naroff7cae42b2009-07-10 23:34:53 +0000287 const ObjCObjectPointerType* PT =
John McCall9dd450b2009-09-21 23:43:11 +0000288 PPT->getPointeeType()->getAs<ObjCObjectPointerType>();
Steve Naroff7cae42b2009-07-10 23:34:53 +0000289
290 if (!PT)
291 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000292
Steve Naroff7cae42b2009-07-10 23:34:53 +0000293 const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
Mike Stump11289f42009-09-09 15:08:12 +0000294
Steve Naroff7cae42b2009-07-10 23:34:53 +0000295 // FIXME: Can ID ever be NULL?
296 if (ID)
297 return II == ID->getIdentifier();
Mike Stump11289f42009-09-09 15:08:12 +0000298
Steve Naroff7cae42b2009-07-10 23:34:53 +0000299 return false;
Ted Kremenekf0673e42008-09-18 21:25:13 +0000300}
Ted Kremenekb42f4822008-09-18 23:09:54 +0000301
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000302static bool IsCFError(QualType T, IdentifierInfo *II) {
303 const PointerType* PPT = T->getAs<PointerType>();
Ted Kremenek3aa89a92008-10-01 23:24:09 +0000304 if (!PPT) return false;
Mike Stump11289f42009-09-09 15:08:12 +0000305
John McCall9dd450b2009-09-21 23:43:11 +0000306 const TypedefType* TT = PPT->getPointeeType()->getAs<TypedefType>();
Ted Kremenek3aa89a92008-10-01 23:24:09 +0000307 if (!TT) return false;
308
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000309 return TT->getDecl()->getIdentifier() == II;
Ted Kremenek3aa89a92008-10-01 23:24:09 +0000310}
311
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000312void ento::registerNSErrorChecker(CheckerManager &mgr) {
313 mgr.registerChecker<NSErrorMethodChecker>();
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000314 NSOrCFErrorDerefChecker *checker =
315 mgr.registerChecker<NSOrCFErrorDerefChecker>();
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000316 checker->ShouldCheckNSError = true;
317}
Mike Stump11289f42009-09-09 15:08:12 +0000318
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000319void ento::registerCFErrorChecker(CheckerManager &mgr) {
320 mgr.registerChecker<CFErrorFunctionChecker>();
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000321 NSOrCFErrorDerefChecker *checker =
322 mgr.registerChecker<NSOrCFErrorDerefChecker>();
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000323 checker->ShouldCheckCFError = true;
Ted Kremenekb42f4822008-09-18 23:09:54 +0000324}