xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp (revision f99d595cfd32465c97fe1dea2638d7f967ef6089)
1 //== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==//
2 //
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 defines NullDerefChecker, a builtin check in ExprEngine that performs
11 // checks for null pointers at loads and stores.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "InternalChecks.h"
16 #include "clang/StaticAnalyzer/BugReporter/BugType.h"
17 #include "clang/StaticAnalyzer/Checkers/DereferenceChecker.h"
18 #include "clang/StaticAnalyzer/PathSensitive/Checker.h"
19 #include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h"
20 
21 using namespace clang;
22 using namespace ento;
23 
24 namespace {
25 class DereferenceChecker : public Checker {
26   BuiltinBug *BT_null;
27   BuiltinBug *BT_undef;
28   llvm::SmallVector<ExplodedNode*, 2> ImplicitNullDerefNodes;
29 public:
30   DereferenceChecker() : BT_null(0), BT_undef(0) {}
31   static void *getTag() { static int tag = 0; return &tag; }
32   void visitLocation(CheckerContext &C, const Stmt *S, SVal location,
33                      bool isLoad);
34 
35   std::pair<ExplodedNode * const*, ExplodedNode * const*>
36   getImplicitNodes() const {
37     return std::make_pair(ImplicitNullDerefNodes.data(),
38                           ImplicitNullDerefNodes.data() +
39                           ImplicitNullDerefNodes.size());
40   }
41   void AddDerefSource(llvm::raw_ostream &os,
42                       llvm::SmallVectorImpl<SourceRange> &Ranges,
43                       const Expr *Ex, bool loadedFrom = false);
44 };
45 } // end anonymous namespace
46 
47 void ento::RegisterDereferenceChecker(ExprEngine &Eng) {
48   Eng.registerCheck(new DereferenceChecker());
49 }
50 
51 std::pair<ExplodedNode * const *, ExplodedNode * const *>
52 ento::GetImplicitNullDereferences(ExprEngine &Eng) {
53   DereferenceChecker *checker = Eng.getChecker<DereferenceChecker>();
54   if (!checker)
55     return std::make_pair((ExplodedNode * const *) 0,
56                           (ExplodedNode * const *) 0);
57   return checker->getImplicitNodes();
58 }
59 
60 void DereferenceChecker::AddDerefSource(llvm::raw_ostream &os,
61                                      llvm::SmallVectorImpl<SourceRange> &Ranges,
62                                         const Expr *Ex,
63                                         bool loadedFrom) {
64   Ex = Ex->IgnoreParenLValueCasts();
65   switch (Ex->getStmtClass()) {
66     default:
67       return;
68     case Stmt::DeclRefExprClass: {
69       const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
70       if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
71         os << " (" << (loadedFrom ? "loaded from" : "from")
72            << " variable '" <<  VD->getName() << "')";
73         Ranges.push_back(DR->getSourceRange());
74       }
75       return;
76     }
77     case Stmt::MemberExprClass: {
78       const MemberExpr *ME = cast<MemberExpr>(Ex);
79       os << " (" << (loadedFrom ? "loaded from" : "via")
80          << " field '" << ME->getMemberNameInfo() << "')";
81       SourceLocation L = ME->getMemberLoc();
82       Ranges.push_back(SourceRange(L, L));
83       break;
84     }
85   }
86 }
87 
88 void DereferenceChecker::visitLocation(CheckerContext &C, const Stmt *S,
89                                        SVal l, bool isLoad) {
90   // Check for dereference of an undefined value.
91   if (l.isUndef()) {
92     if (ExplodedNode *N = C.generateSink()) {
93       if (!BT_undef)
94         BT_undef = new BuiltinBug("Dereference of undefined pointer value");
95 
96       EnhancedBugReport *report =
97         new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N);
98       report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
99                                 bugreporter::GetDerefExpr(N));
100       C.EmitReport(report);
101     }
102     return;
103   }
104 
105   DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l);
106 
107   // Check for null dereferences.
108   if (!isa<Loc>(location))
109     return;
110 
111   const GRState *state = C.getState();
112   const GRState *notNullState, *nullState;
113   llvm::tie(notNullState, nullState) = state->assume(location);
114 
115   // The explicit NULL case.
116   if (nullState) {
117     if (!notNullState) {
118       // Generate an error node.
119       ExplodedNode *N = C.generateSink(nullState);
120       if (!N)
121         return;
122 
123       // We know that 'location' cannot be non-null.  This is what
124       // we call an "explicit" null dereference.
125       if (!BT_null)
126         BT_null = new BuiltinBug("Dereference of null pointer");
127 
128       llvm::SmallString<100> buf;
129       llvm::SmallVector<SourceRange, 2> Ranges;
130 
131       // Walk through lvalue casts to get the original expression
132       // that syntactically caused the load.
133       if (const Expr *expr = dyn_cast<Expr>(S))
134         S = expr->IgnoreParenLValueCasts();
135 
136       switch (S->getStmtClass()) {
137         case Stmt::ArraySubscriptExprClass: {
138           llvm::raw_svector_ostream os(buf);
139           os << "Array access";
140           const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
141           AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts());
142           os << " results in a null pointer dereference";
143           break;
144         }
145         case Stmt::UnaryOperatorClass: {
146           llvm::raw_svector_ostream os(buf);
147           os << "Dereference of null pointer";
148           const UnaryOperator *U = cast<UnaryOperator>(S);
149           AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), true);
150           break;
151         }
152         case Stmt::MemberExprClass: {
153           const MemberExpr *M = cast<MemberExpr>(S);
154           if (M->isArrow()) {
155             llvm::raw_svector_ostream os(buf);
156             os << "Access to field '" << M->getMemberNameInfo()
157                << "' results in a dereference of a null pointer";
158             AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), true);
159           }
160           break;
161         }
162         case Stmt::ObjCIvarRefExprClass: {
163           const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
164           if (const DeclRefExpr *DR =
165               dyn_cast<DeclRefExpr>(IV->getBase()->IgnoreParenCasts())) {
166             if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
167               llvm::raw_svector_ostream os(buf);
168               os << "Instance variable access (via '" << VD->getName()
169                  << "') results in a null pointer dereference";
170             }
171           }
172           Ranges.push_back(IV->getSourceRange());
173           break;
174         }
175         default:
176           break;
177       }
178 
179       EnhancedBugReport *report =
180         new EnhancedBugReport(*BT_null,
181                               buf.empty() ? BT_null->getDescription():buf.str(),
182                               N);
183 
184       report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
185                                 bugreporter::GetDerefExpr(N));
186 
187       for (llvm::SmallVectorImpl<SourceRange>::iterator
188             I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
189         report->addRange(*I);
190 
191       C.EmitReport(report);
192       return;
193     }
194     else {
195       // Otherwise, we have the case where the location could either be
196       // null or not-null.  Record the error node as an "implicit" null
197       // dereference.
198       if (ExplodedNode *N = C.generateSink(nullState))
199         ImplicitNullDerefNodes.push_back(N);
200     }
201   }
202 
203   // From this point forward, we know that the location is not null.
204   C.addTransition(notNullState);
205 }
206