xref: /aosp_15_r20/external/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp (revision 67e74705e28f6214e480b399dd47ea732279e315)
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