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