17330f729Sjoerg // SmartPtrModeling.cpp - Model behavior of C++ smart pointers - C++ ------===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // This file defines a checker that models various aspects of
107330f729Sjoerg // C++ smart pointer behavior.
117330f729Sjoerg //
127330f729Sjoerg //===----------------------------------------------------------------------===//
137330f729Sjoerg
147330f729Sjoerg #include "Move.h"
15*e038c9c4Sjoerg #include "SmartPtr.h"
167330f729Sjoerg
17*e038c9c4Sjoerg #include "clang/AST/DeclCXX.h"
18*e038c9c4Sjoerg #include "clang/AST/DeclarationName.h"
197330f729Sjoerg #include "clang/AST/ExprCXX.h"
20*e038c9c4Sjoerg #include "clang/AST/Type.h"
21*e038c9c4Sjoerg #include "clang/Basic/LLVM.h"
227330f729Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
237330f729Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
247330f729Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
257330f729Sjoerg #include "clang/StaticAnalyzer/Core/CheckerManager.h"
267330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
277330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
28*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
29*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
30*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
31*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
32*e038c9c4Sjoerg #include <string>
337330f729Sjoerg
347330f729Sjoerg using namespace clang;
357330f729Sjoerg using namespace ento;
367330f729Sjoerg
377330f729Sjoerg namespace {
38*e038c9c4Sjoerg class SmartPtrModeling
39*e038c9c4Sjoerg : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges,
40*e038c9c4Sjoerg check::LiveSymbols> {
41*e038c9c4Sjoerg
42*e038c9c4Sjoerg bool isBoolConversionMethod(const CallEvent &Call) const;
437330f729Sjoerg
447330f729Sjoerg public:
45*e038c9c4Sjoerg // Whether the checker should model for null dereferences of smart pointers.
46*e038c9c4Sjoerg DefaultBool ModelSmartPtrDereference;
477330f729Sjoerg bool evalCall(const CallEvent &Call, CheckerContext &C) const;
48*e038c9c4Sjoerg void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
49*e038c9c4Sjoerg void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
50*e038c9c4Sjoerg ProgramStateRef
51*e038c9c4Sjoerg checkRegionChanges(ProgramStateRef State,
52*e038c9c4Sjoerg const InvalidatedSymbols *Invalidated,
53*e038c9c4Sjoerg ArrayRef<const MemRegion *> ExplicitRegions,
54*e038c9c4Sjoerg ArrayRef<const MemRegion *> Regions,
55*e038c9c4Sjoerg const LocationContext *LCtx, const CallEvent *Call) const;
56*e038c9c4Sjoerg void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
57*e038c9c4Sjoerg const char *Sep) const override;
58*e038c9c4Sjoerg void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
59*e038c9c4Sjoerg
60*e038c9c4Sjoerg private:
61*e038c9c4Sjoerg void handleReset(const CallEvent &Call, CheckerContext &C) const;
62*e038c9c4Sjoerg void handleRelease(const CallEvent &Call, CheckerContext &C) const;
63*e038c9c4Sjoerg void handleSwap(const CallEvent &Call, CheckerContext &C) const;
64*e038c9c4Sjoerg void handleGet(const CallEvent &Call, CheckerContext &C) const;
65*e038c9c4Sjoerg bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const;
66*e038c9c4Sjoerg bool handleMoveCtr(const CallEvent &Call, CheckerContext &C,
67*e038c9c4Sjoerg const MemRegion *ThisRegion) const;
68*e038c9c4Sjoerg bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion,
69*e038c9c4Sjoerg const MemRegion *OtherSmartPtrRegion) const;
70*e038c9c4Sjoerg void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const;
71*e038c9c4Sjoerg
72*e038c9c4Sjoerg using SmartPtrMethodHandlerFn =
73*e038c9c4Sjoerg void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
74*e038c9c4Sjoerg CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
75*e038c9c4Sjoerg {{"reset"}, &SmartPtrModeling::handleReset},
76*e038c9c4Sjoerg {{"release"}, &SmartPtrModeling::handleRelease},
77*e038c9c4Sjoerg {{"swap", 1}, &SmartPtrModeling::handleSwap},
78*e038c9c4Sjoerg {{"get"}, &SmartPtrModeling::handleGet}};
797330f729Sjoerg };
807330f729Sjoerg } // end of anonymous namespace
817330f729Sjoerg
82*e038c9c4Sjoerg REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
83*e038c9c4Sjoerg
84*e038c9c4Sjoerg // Define the inter-checker API.
85*e038c9c4Sjoerg namespace clang {
86*e038c9c4Sjoerg namespace ento {
87*e038c9c4Sjoerg namespace smartptr {
isStdSmartPtrCall(const CallEvent & Call)88*e038c9c4Sjoerg bool isStdSmartPtrCall(const CallEvent &Call) {
89*e038c9c4Sjoerg const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
90*e038c9c4Sjoerg if (!MethodDecl || !MethodDecl->getParent())
91*e038c9c4Sjoerg return false;
92*e038c9c4Sjoerg
93*e038c9c4Sjoerg const auto *RecordDecl = MethodDecl->getParent();
94*e038c9c4Sjoerg if (!RecordDecl || !RecordDecl->getDeclContext()->isStdNamespace())
95*e038c9c4Sjoerg return false;
96*e038c9c4Sjoerg
97*e038c9c4Sjoerg if (RecordDecl->getDeclName().isIdentifier()) {
98*e038c9c4Sjoerg StringRef Name = RecordDecl->getName();
99*e038c9c4Sjoerg return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr";
100*e038c9c4Sjoerg }
101*e038c9c4Sjoerg return false;
102*e038c9c4Sjoerg }
103*e038c9c4Sjoerg
isNullSmartPtr(const ProgramStateRef State,const MemRegion * ThisRegion)104*e038c9c4Sjoerg bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
105*e038c9c4Sjoerg const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
106*e038c9c4Sjoerg return InnerPointVal &&
107*e038c9c4Sjoerg !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true);
108*e038c9c4Sjoerg }
109*e038c9c4Sjoerg } // namespace smartptr
110*e038c9c4Sjoerg } // namespace ento
111*e038c9c4Sjoerg } // namespace clang
112*e038c9c4Sjoerg
113*e038c9c4Sjoerg // If a region is removed all of the subregions need to be removed too.
114*e038c9c4Sjoerg static TrackedRegionMapTy
removeTrackedSubregions(TrackedRegionMapTy RegionMap,TrackedRegionMapTy::Factory & RegionMapFactory,const MemRegion * Region)115*e038c9c4Sjoerg removeTrackedSubregions(TrackedRegionMapTy RegionMap,
116*e038c9c4Sjoerg TrackedRegionMapTy::Factory &RegionMapFactory,
117*e038c9c4Sjoerg const MemRegion *Region) {
118*e038c9c4Sjoerg if (!Region)
119*e038c9c4Sjoerg return RegionMap;
120*e038c9c4Sjoerg for (const auto &E : RegionMap) {
121*e038c9c4Sjoerg if (E.first->isSubRegionOf(Region))
122*e038c9c4Sjoerg RegionMap = RegionMapFactory.remove(RegionMap, E.first);
123*e038c9c4Sjoerg }
124*e038c9c4Sjoerg return RegionMap;
125*e038c9c4Sjoerg }
126*e038c9c4Sjoerg
updateSwappedRegion(ProgramStateRef State,const MemRegion * Region,const SVal * RegionInnerPointerVal)127*e038c9c4Sjoerg static ProgramStateRef updateSwappedRegion(ProgramStateRef State,
128*e038c9c4Sjoerg const MemRegion *Region,
129*e038c9c4Sjoerg const SVal *RegionInnerPointerVal) {
130*e038c9c4Sjoerg if (RegionInnerPointerVal) {
131*e038c9c4Sjoerg State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal);
132*e038c9c4Sjoerg } else {
133*e038c9c4Sjoerg State = State->remove<TrackedRegionMap>(Region);
134*e038c9c4Sjoerg }
135*e038c9c4Sjoerg return State;
136*e038c9c4Sjoerg }
137*e038c9c4Sjoerg
138*e038c9c4Sjoerg // Helper method to get the inner pointer type of specialized smart pointer
139*e038c9c4Sjoerg // Returns empty type if not found valid inner pointer type.
getInnerPointerType(const CallEvent & Call,CheckerContext & C)140*e038c9c4Sjoerg static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) {
141*e038c9c4Sjoerg const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
142*e038c9c4Sjoerg if (!MethodDecl || !MethodDecl->getParent())
143*e038c9c4Sjoerg return {};
144*e038c9c4Sjoerg
145*e038c9c4Sjoerg const auto *RecordDecl = MethodDecl->getParent();
146*e038c9c4Sjoerg if (!RecordDecl || !RecordDecl->isInStdNamespace())
147*e038c9c4Sjoerg return {};
148*e038c9c4Sjoerg
149*e038c9c4Sjoerg const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl);
150*e038c9c4Sjoerg if (!TSD)
151*e038c9c4Sjoerg return {};
152*e038c9c4Sjoerg
153*e038c9c4Sjoerg auto TemplateArgs = TSD->getTemplateArgs().asArray();
154*e038c9c4Sjoerg if (TemplateArgs.size() == 0)
155*e038c9c4Sjoerg return {};
156*e038c9c4Sjoerg auto InnerValueType = TemplateArgs[0].getAsType();
157*e038c9c4Sjoerg return C.getASTContext().getPointerType(InnerValueType.getCanonicalType());
158*e038c9c4Sjoerg }
159*e038c9c4Sjoerg
160*e038c9c4Sjoerg // Helper method to pretty print region and avoid extra spacing.
checkAndPrettyPrintRegion(llvm::raw_ostream & OS,const MemRegion * Region)161*e038c9c4Sjoerg static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS,
162*e038c9c4Sjoerg const MemRegion *Region) {
163*e038c9c4Sjoerg if (Region->canPrintPretty()) {
164*e038c9c4Sjoerg OS << " ";
165*e038c9c4Sjoerg Region->printPretty(OS);
166*e038c9c4Sjoerg }
167*e038c9c4Sjoerg }
168*e038c9c4Sjoerg
isBoolConversionMethod(const CallEvent & Call) const169*e038c9c4Sjoerg bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const {
1707330f729Sjoerg // TODO: Update CallDescription to support anonymous calls?
1717330f729Sjoerg // TODO: Handle other methods, such as .get() or .release().
1727330f729Sjoerg // But once we do, we'd need a visitor to explain null dereferences
1737330f729Sjoerg // that are found via such modeling.
1747330f729Sjoerg const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl());
1757330f729Sjoerg return CD && CD->getConversionType()->isBooleanType();
1767330f729Sjoerg }
1777330f729Sjoerg
evalCall(const CallEvent & Call,CheckerContext & C) const1787330f729Sjoerg bool SmartPtrModeling::evalCall(const CallEvent &Call,
1797330f729Sjoerg CheckerContext &C) const {
180*e038c9c4Sjoerg ProgramStateRef State = C.getState();
181*e038c9c4Sjoerg if (!smartptr::isStdSmartPtrCall(Call))
1827330f729Sjoerg return false;
1837330f729Sjoerg
184*e038c9c4Sjoerg if (isBoolConversionMethod(Call)) {
1857330f729Sjoerg const MemRegion *ThisR =
1867330f729Sjoerg cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
1877330f729Sjoerg
188*e038c9c4Sjoerg if (ModelSmartPtrDereference) {
189*e038c9c4Sjoerg // The check for the region is moved is duplicated in handleBoolOperation
190*e038c9c4Sjoerg // method.
191*e038c9c4Sjoerg // FIXME: Once we model std::move for smart pointers clean up this and use
192*e038c9c4Sjoerg // that modeling.
193*e038c9c4Sjoerg handleBoolConversion(Call, C);
194*e038c9c4Sjoerg return true;
195*e038c9c4Sjoerg } else {
1967330f729Sjoerg if (!move::isMovedFrom(State, ThisR)) {
197*e038c9c4Sjoerg // TODO: Model this case as well. At least, avoid invalidation of
198*e038c9c4Sjoerg // globals.
1997330f729Sjoerg return false;
2007330f729Sjoerg }
2017330f729Sjoerg
2027330f729Sjoerg // TODO: Add a note to bug reports describing this decision.
203*e038c9c4Sjoerg C.addTransition(State->BindExpr(
204*e038c9c4Sjoerg Call.getOriginExpr(), C.getLocationContext(),
2057330f729Sjoerg C.getSValBuilder().makeZeroVal(Call.getResultType())));
206*e038c9c4Sjoerg
207*e038c9c4Sjoerg return true;
208*e038c9c4Sjoerg }
209*e038c9c4Sjoerg }
210*e038c9c4Sjoerg
211*e038c9c4Sjoerg if (!ModelSmartPtrDereference)
212*e038c9c4Sjoerg return false;
213*e038c9c4Sjoerg
214*e038c9c4Sjoerg if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
215*e038c9c4Sjoerg if (CC->getDecl()->isCopyConstructor())
216*e038c9c4Sjoerg return false;
217*e038c9c4Sjoerg
218*e038c9c4Sjoerg const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion();
219*e038c9c4Sjoerg if (!ThisRegion)
220*e038c9c4Sjoerg return false;
221*e038c9c4Sjoerg
222*e038c9c4Sjoerg if (CC->getDecl()->isMoveConstructor())
223*e038c9c4Sjoerg return handleMoveCtr(Call, C, ThisRegion);
224*e038c9c4Sjoerg
225*e038c9c4Sjoerg if (Call.getNumArgs() == 0) {
226*e038c9c4Sjoerg auto NullVal = C.getSValBuilder().makeNull();
227*e038c9c4Sjoerg State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
228*e038c9c4Sjoerg
229*e038c9c4Sjoerg C.addTransition(
230*e038c9c4Sjoerg State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
231*e038c9c4Sjoerg llvm::raw_ostream &OS) {
232*e038c9c4Sjoerg if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
233*e038c9c4Sjoerg !BR.isInteresting(ThisRegion))
234*e038c9c4Sjoerg return;
235*e038c9c4Sjoerg OS << "Default constructed smart pointer";
236*e038c9c4Sjoerg checkAndPrettyPrintRegion(OS, ThisRegion);
237*e038c9c4Sjoerg OS << " is null";
238*e038c9c4Sjoerg }));
239*e038c9c4Sjoerg } else {
240*e038c9c4Sjoerg const auto *TrackingExpr = Call.getArgExpr(0);
241*e038c9c4Sjoerg assert(TrackingExpr->getType()->isPointerType() &&
242*e038c9c4Sjoerg "Adding a non pointer value to TrackedRegionMap");
243*e038c9c4Sjoerg auto ArgVal = Call.getArgSVal(0);
244*e038c9c4Sjoerg State = State->set<TrackedRegionMap>(ThisRegion, ArgVal);
245*e038c9c4Sjoerg
246*e038c9c4Sjoerg C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr,
247*e038c9c4Sjoerg ArgVal](PathSensitiveBugReport &BR,
248*e038c9c4Sjoerg llvm::raw_ostream &OS) {
249*e038c9c4Sjoerg if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
250*e038c9c4Sjoerg !BR.isInteresting(ThisRegion))
251*e038c9c4Sjoerg return;
252*e038c9c4Sjoerg bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
253*e038c9c4Sjoerg OS << "Smart pointer";
254*e038c9c4Sjoerg checkAndPrettyPrintRegion(OS, ThisRegion);
255*e038c9c4Sjoerg if (ArgVal.isZeroConstant())
256*e038c9c4Sjoerg OS << " is constructed using a null value";
257*e038c9c4Sjoerg else
258*e038c9c4Sjoerg OS << " is constructed";
259*e038c9c4Sjoerg }));
260*e038c9c4Sjoerg }
2617330f729Sjoerg return true;
2627330f729Sjoerg }
2637330f729Sjoerg
264*e038c9c4Sjoerg if (handleAssignOp(Call, C))
265*e038c9c4Sjoerg return true;
266*e038c9c4Sjoerg
267*e038c9c4Sjoerg const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
268*e038c9c4Sjoerg if (!Handler)
269*e038c9c4Sjoerg return false;
270*e038c9c4Sjoerg (this->**Handler)(Call, C);
271*e038c9c4Sjoerg
272*e038c9c4Sjoerg return C.isDifferent();
2737330f729Sjoerg }
2747330f729Sjoerg
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const275*e038c9c4Sjoerg void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
276*e038c9c4Sjoerg CheckerContext &C) const {
277*e038c9c4Sjoerg ProgramStateRef State = C.getState();
278*e038c9c4Sjoerg // Clean up dead regions from the region map.
279*e038c9c4Sjoerg TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
280*e038c9c4Sjoerg for (auto E : TrackedRegions) {
281*e038c9c4Sjoerg const MemRegion *Region = E.first;
282*e038c9c4Sjoerg bool IsRegDead = !SymReaper.isLiveRegion(Region);
283*e038c9c4Sjoerg
284*e038c9c4Sjoerg if (IsRegDead)
285*e038c9c4Sjoerg State = State->remove<TrackedRegionMap>(Region);
286*e038c9c4Sjoerg }
287*e038c9c4Sjoerg C.addTransition(State);
288*e038c9c4Sjoerg }
289*e038c9c4Sjoerg
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const290*e038c9c4Sjoerg void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State,
291*e038c9c4Sjoerg const char *NL, const char *Sep) const {
292*e038c9c4Sjoerg TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
293*e038c9c4Sjoerg
294*e038c9c4Sjoerg if (!RS.isEmpty()) {
295*e038c9c4Sjoerg Out << Sep << "Smart ptr regions :" << NL;
296*e038c9c4Sjoerg for (auto I : RS) {
297*e038c9c4Sjoerg I.first->dumpToStream(Out);
298*e038c9c4Sjoerg if (smartptr::isNullSmartPtr(State, I.first))
299*e038c9c4Sjoerg Out << ": Null";
300*e038c9c4Sjoerg else
301*e038c9c4Sjoerg Out << ": Non Null";
302*e038c9c4Sjoerg Out << NL;
303*e038c9c4Sjoerg }
304*e038c9c4Sjoerg }
305*e038c9c4Sjoerg }
306*e038c9c4Sjoerg
checkRegionChanges(ProgramStateRef State,const InvalidatedSymbols * Invalidated,ArrayRef<const MemRegion * > ExplicitRegions,ArrayRef<const MemRegion * > Regions,const LocationContext * LCtx,const CallEvent * Call) const307*e038c9c4Sjoerg ProgramStateRef SmartPtrModeling::checkRegionChanges(
308*e038c9c4Sjoerg ProgramStateRef State, const InvalidatedSymbols *Invalidated,
309*e038c9c4Sjoerg ArrayRef<const MemRegion *> ExplicitRegions,
310*e038c9c4Sjoerg ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
311*e038c9c4Sjoerg const CallEvent *Call) const {
312*e038c9c4Sjoerg TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>();
313*e038c9c4Sjoerg TrackedRegionMapTy::Factory &RegionMapFactory =
314*e038c9c4Sjoerg State->get_context<TrackedRegionMap>();
315*e038c9c4Sjoerg for (const auto *Region : Regions)
316*e038c9c4Sjoerg RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory,
317*e038c9c4Sjoerg Region->getBaseRegion());
318*e038c9c4Sjoerg return State->set<TrackedRegionMap>(RegionMap);
319*e038c9c4Sjoerg }
320*e038c9c4Sjoerg
checkLiveSymbols(ProgramStateRef State,SymbolReaper & SR) const321*e038c9c4Sjoerg void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State,
322*e038c9c4Sjoerg SymbolReaper &SR) const {
323*e038c9c4Sjoerg // Marking tracked symbols alive
324*e038c9c4Sjoerg TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
325*e038c9c4Sjoerg for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) {
326*e038c9c4Sjoerg SVal Val = I->second;
327*e038c9c4Sjoerg for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) {
328*e038c9c4Sjoerg SR.markLive(*si);
329*e038c9c4Sjoerg }
330*e038c9c4Sjoerg }
331*e038c9c4Sjoerg }
332*e038c9c4Sjoerg
handleReset(const CallEvent & Call,CheckerContext & C) const333*e038c9c4Sjoerg void SmartPtrModeling::handleReset(const CallEvent &Call,
334*e038c9c4Sjoerg CheckerContext &C) const {
335*e038c9c4Sjoerg ProgramStateRef State = C.getState();
336*e038c9c4Sjoerg const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
337*e038c9c4Sjoerg if (!IC)
338*e038c9c4Sjoerg return;
339*e038c9c4Sjoerg
340*e038c9c4Sjoerg const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
341*e038c9c4Sjoerg if (!ThisRegion)
342*e038c9c4Sjoerg return;
343*e038c9c4Sjoerg
344*e038c9c4Sjoerg assert(Call.getArgExpr(0)->getType()->isPointerType() &&
345*e038c9c4Sjoerg "Adding a non pointer value to TrackedRegionMap");
346*e038c9c4Sjoerg State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0));
347*e038c9c4Sjoerg const auto *TrackingExpr = Call.getArgExpr(0);
348*e038c9c4Sjoerg C.addTransition(
349*e038c9c4Sjoerg State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR,
350*e038c9c4Sjoerg llvm::raw_ostream &OS) {
351*e038c9c4Sjoerg if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
352*e038c9c4Sjoerg !BR.isInteresting(ThisRegion))
353*e038c9c4Sjoerg return;
354*e038c9c4Sjoerg bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
355*e038c9c4Sjoerg OS << "Smart pointer";
356*e038c9c4Sjoerg checkAndPrettyPrintRegion(OS, ThisRegion);
357*e038c9c4Sjoerg OS << " reset using a null value";
358*e038c9c4Sjoerg }));
359*e038c9c4Sjoerg // TODO: Make sure to ivalidate the region in the Store if we don't have
360*e038c9c4Sjoerg // time to model all methods.
361*e038c9c4Sjoerg }
362*e038c9c4Sjoerg
handleRelease(const CallEvent & Call,CheckerContext & C) const363*e038c9c4Sjoerg void SmartPtrModeling::handleRelease(const CallEvent &Call,
364*e038c9c4Sjoerg CheckerContext &C) const {
365*e038c9c4Sjoerg ProgramStateRef State = C.getState();
366*e038c9c4Sjoerg const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
367*e038c9c4Sjoerg if (!IC)
368*e038c9c4Sjoerg return;
369*e038c9c4Sjoerg
370*e038c9c4Sjoerg const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
371*e038c9c4Sjoerg if (!ThisRegion)
372*e038c9c4Sjoerg return;
373*e038c9c4Sjoerg
374*e038c9c4Sjoerg const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
375*e038c9c4Sjoerg
376*e038c9c4Sjoerg if (InnerPointVal) {
377*e038c9c4Sjoerg State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
378*e038c9c4Sjoerg *InnerPointVal);
379*e038c9c4Sjoerg }
380*e038c9c4Sjoerg
381*e038c9c4Sjoerg auto ValueToUpdate = C.getSValBuilder().makeNull();
382*e038c9c4Sjoerg State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);
383*e038c9c4Sjoerg
384*e038c9c4Sjoerg C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
385*e038c9c4Sjoerg llvm::raw_ostream &OS) {
386*e038c9c4Sjoerg if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
387*e038c9c4Sjoerg !BR.isInteresting(ThisRegion))
388*e038c9c4Sjoerg return;
389*e038c9c4Sjoerg
390*e038c9c4Sjoerg OS << "Smart pointer";
391*e038c9c4Sjoerg checkAndPrettyPrintRegion(OS, ThisRegion);
392*e038c9c4Sjoerg OS << " is released and set to null";
393*e038c9c4Sjoerg }));
394*e038c9c4Sjoerg // TODO: Add support to enable MallocChecker to start tracking the raw
395*e038c9c4Sjoerg // pointer.
396*e038c9c4Sjoerg }
397*e038c9c4Sjoerg
handleSwap(const CallEvent & Call,CheckerContext & C) const398*e038c9c4Sjoerg void SmartPtrModeling::handleSwap(const CallEvent &Call,
399*e038c9c4Sjoerg CheckerContext &C) const {
400*e038c9c4Sjoerg // To model unique_ptr::swap() method.
401*e038c9c4Sjoerg const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
402*e038c9c4Sjoerg if (!IC)
403*e038c9c4Sjoerg return;
404*e038c9c4Sjoerg
405*e038c9c4Sjoerg const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
406*e038c9c4Sjoerg if (!ThisRegion)
407*e038c9c4Sjoerg return;
408*e038c9c4Sjoerg
409*e038c9c4Sjoerg const auto *ArgRegion = Call.getArgSVal(0).getAsRegion();
410*e038c9c4Sjoerg if (!ArgRegion)
411*e038c9c4Sjoerg return;
412*e038c9c4Sjoerg
413*e038c9c4Sjoerg auto State = C.getState();
414*e038c9c4Sjoerg const auto *ThisRegionInnerPointerVal =
415*e038c9c4Sjoerg State->get<TrackedRegionMap>(ThisRegion);
416*e038c9c4Sjoerg const auto *ArgRegionInnerPointerVal =
417*e038c9c4Sjoerg State->get<TrackedRegionMap>(ArgRegion);
418*e038c9c4Sjoerg
419*e038c9c4Sjoerg // Swap the tracked region values.
420*e038c9c4Sjoerg State = updateSwappedRegion(State, ThisRegion, ArgRegionInnerPointerVal);
421*e038c9c4Sjoerg State = updateSwappedRegion(State, ArgRegion, ThisRegionInnerPointerVal);
422*e038c9c4Sjoerg
423*e038c9c4Sjoerg C.addTransition(
424*e038c9c4Sjoerg State, C.getNoteTag([ThisRegion, ArgRegion](PathSensitiveBugReport &BR,
425*e038c9c4Sjoerg llvm::raw_ostream &OS) {
426*e038c9c4Sjoerg if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
427*e038c9c4Sjoerg !BR.isInteresting(ThisRegion))
428*e038c9c4Sjoerg return;
429*e038c9c4Sjoerg BR.markInteresting(ArgRegion);
430*e038c9c4Sjoerg OS << "Swapped null smart pointer";
431*e038c9c4Sjoerg checkAndPrettyPrintRegion(OS, ArgRegion);
432*e038c9c4Sjoerg OS << " with smart pointer";
433*e038c9c4Sjoerg checkAndPrettyPrintRegion(OS, ThisRegion);
434*e038c9c4Sjoerg }));
435*e038c9c4Sjoerg }
436*e038c9c4Sjoerg
handleGet(const CallEvent & Call,CheckerContext & C) const437*e038c9c4Sjoerg void SmartPtrModeling::handleGet(const CallEvent &Call,
438*e038c9c4Sjoerg CheckerContext &C) const {
439*e038c9c4Sjoerg ProgramStateRef State = C.getState();
440*e038c9c4Sjoerg const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
441*e038c9c4Sjoerg if (!IC)
442*e038c9c4Sjoerg return;
443*e038c9c4Sjoerg
444*e038c9c4Sjoerg const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
445*e038c9c4Sjoerg if (!ThisRegion)
446*e038c9c4Sjoerg return;
447*e038c9c4Sjoerg
448*e038c9c4Sjoerg SVal InnerPointerVal;
449*e038c9c4Sjoerg if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
450*e038c9c4Sjoerg InnerPointerVal = *InnerValPtr;
451*e038c9c4Sjoerg } else {
452*e038c9c4Sjoerg const auto *CallExpr = Call.getOriginExpr();
453*e038c9c4Sjoerg InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
454*e038c9c4Sjoerg CallExpr, C.getLocationContext(), Call.getResultType(), C.blockCount());
455*e038c9c4Sjoerg State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
456*e038c9c4Sjoerg }
457*e038c9c4Sjoerg
458*e038c9c4Sjoerg State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
459*e038c9c4Sjoerg InnerPointerVal);
460*e038c9c4Sjoerg // TODO: Add NoteTag, for how the raw pointer got using 'get' method.
461*e038c9c4Sjoerg C.addTransition(State);
462*e038c9c4Sjoerg }
463*e038c9c4Sjoerg
handleAssignOp(const CallEvent & Call,CheckerContext & C) const464*e038c9c4Sjoerg bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
465*e038c9c4Sjoerg CheckerContext &C) const {
466*e038c9c4Sjoerg ProgramStateRef State = C.getState();
467*e038c9c4Sjoerg const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call);
468*e038c9c4Sjoerg if (!OC)
469*e038c9c4Sjoerg return false;
470*e038c9c4Sjoerg OverloadedOperatorKind OOK = OC->getOverloadedOperator();
471*e038c9c4Sjoerg if (OOK != OO_Equal)
472*e038c9c4Sjoerg return false;
473*e038c9c4Sjoerg const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion();
474*e038c9c4Sjoerg if (!ThisRegion)
475*e038c9c4Sjoerg return false;
476*e038c9c4Sjoerg
477*e038c9c4Sjoerg const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion();
478*e038c9c4Sjoerg // In case of 'nullptr' or '0' assigned
479*e038c9c4Sjoerg if (!OtherSmartPtrRegion) {
480*e038c9c4Sjoerg bool AssignedNull = Call.getArgSVal(0).isZeroConstant();
481*e038c9c4Sjoerg if (!AssignedNull)
482*e038c9c4Sjoerg return false;
483*e038c9c4Sjoerg auto NullVal = C.getSValBuilder().makeNull();
484*e038c9c4Sjoerg State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
485*e038c9c4Sjoerg C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
486*e038c9c4Sjoerg llvm::raw_ostream &OS) {
487*e038c9c4Sjoerg if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
488*e038c9c4Sjoerg !BR.isInteresting(ThisRegion))
489*e038c9c4Sjoerg return;
490*e038c9c4Sjoerg OS << "Smart pointer";
491*e038c9c4Sjoerg checkAndPrettyPrintRegion(OS, ThisRegion);
492*e038c9c4Sjoerg OS << " is assigned to null";
493*e038c9c4Sjoerg }));
494*e038c9c4Sjoerg return true;
495*e038c9c4Sjoerg }
496*e038c9c4Sjoerg
497*e038c9c4Sjoerg return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
498*e038c9c4Sjoerg }
499*e038c9c4Sjoerg
handleMoveCtr(const CallEvent & Call,CheckerContext & C,const MemRegion * ThisRegion) const500*e038c9c4Sjoerg bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C,
501*e038c9c4Sjoerg const MemRegion *ThisRegion) const {
502*e038c9c4Sjoerg const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion();
503*e038c9c4Sjoerg if (!OtherSmartPtrRegion)
504*e038c9c4Sjoerg return false;
505*e038c9c4Sjoerg
506*e038c9c4Sjoerg return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
507*e038c9c4Sjoerg }
508*e038c9c4Sjoerg
updateMovedSmartPointers(CheckerContext & C,const MemRegion * ThisRegion,const MemRegion * OtherSmartPtrRegion) const509*e038c9c4Sjoerg bool SmartPtrModeling::updateMovedSmartPointers(
510*e038c9c4Sjoerg CheckerContext &C, const MemRegion *ThisRegion,
511*e038c9c4Sjoerg const MemRegion *OtherSmartPtrRegion) const {
512*e038c9c4Sjoerg ProgramStateRef State = C.getState();
513*e038c9c4Sjoerg const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion);
514*e038c9c4Sjoerg if (OtherInnerPtr) {
515*e038c9c4Sjoerg State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
516*e038c9c4Sjoerg auto NullVal = C.getSValBuilder().makeNull();
517*e038c9c4Sjoerg State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
518*e038c9c4Sjoerg bool IsArgValNull = OtherInnerPtr->isZeroConstant();
519*e038c9c4Sjoerg
520*e038c9c4Sjoerg C.addTransition(
521*e038c9c4Sjoerg State,
522*e038c9c4Sjoerg C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull](
523*e038c9c4Sjoerg PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
524*e038c9c4Sjoerg if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
525*e038c9c4Sjoerg return;
526*e038c9c4Sjoerg if (BR.isInteresting(OtherSmartPtrRegion)) {
527*e038c9c4Sjoerg OS << "Smart pointer";
528*e038c9c4Sjoerg checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
529*e038c9c4Sjoerg OS << " is null after being moved to";
530*e038c9c4Sjoerg checkAndPrettyPrintRegion(OS, ThisRegion);
531*e038c9c4Sjoerg }
532*e038c9c4Sjoerg if (BR.isInteresting(ThisRegion) && IsArgValNull) {
533*e038c9c4Sjoerg OS << "A null pointer value is moved to";
534*e038c9c4Sjoerg checkAndPrettyPrintRegion(OS, ThisRegion);
535*e038c9c4Sjoerg BR.markInteresting(OtherSmartPtrRegion);
536*e038c9c4Sjoerg }
537*e038c9c4Sjoerg }));
538*e038c9c4Sjoerg return true;
539*e038c9c4Sjoerg } else {
540*e038c9c4Sjoerg // In case we dont know anything about value we are moving from
541*e038c9c4Sjoerg // remove the entry from map for which smart pointer got moved to.
542*e038c9c4Sjoerg auto NullVal = C.getSValBuilder().makeNull();
543*e038c9c4Sjoerg State = State->remove<TrackedRegionMap>(ThisRegion);
544*e038c9c4Sjoerg State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
545*e038c9c4Sjoerg C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion,
546*e038c9c4Sjoerg ThisRegion](PathSensitiveBugReport &BR,
547*e038c9c4Sjoerg llvm::raw_ostream &OS) {
548*e038c9c4Sjoerg if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
549*e038c9c4Sjoerg !BR.isInteresting(OtherSmartPtrRegion))
550*e038c9c4Sjoerg return;
551*e038c9c4Sjoerg OS << "Smart pointer";
552*e038c9c4Sjoerg checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
553*e038c9c4Sjoerg OS << " is null after; previous value moved to";
554*e038c9c4Sjoerg checkAndPrettyPrintRegion(OS, ThisRegion);
555*e038c9c4Sjoerg }));
556*e038c9c4Sjoerg return true;
557*e038c9c4Sjoerg }
558*e038c9c4Sjoerg return false;
559*e038c9c4Sjoerg }
560*e038c9c4Sjoerg
handleBoolConversion(const CallEvent & Call,CheckerContext & C) const561*e038c9c4Sjoerg void SmartPtrModeling::handleBoolConversion(const CallEvent &Call,
562*e038c9c4Sjoerg CheckerContext &C) const {
563*e038c9c4Sjoerg // To model unique_ptr::operator bool
564*e038c9c4Sjoerg ProgramStateRef State = C.getState();
565*e038c9c4Sjoerg const Expr *CallExpr = Call.getOriginExpr();
566*e038c9c4Sjoerg const MemRegion *ThisRegion =
567*e038c9c4Sjoerg cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
568*e038c9c4Sjoerg
569*e038c9c4Sjoerg SVal InnerPointerVal;
570*e038c9c4Sjoerg if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
571*e038c9c4Sjoerg InnerPointerVal = *InnerValPtr;
572*e038c9c4Sjoerg } else {
573*e038c9c4Sjoerg // In case of inner pointer SVal is not available we create
574*e038c9c4Sjoerg // conjureSymbolVal for inner pointer value.
575*e038c9c4Sjoerg auto InnerPointerType = getInnerPointerType(Call, C);
576*e038c9c4Sjoerg if (InnerPointerType.isNull())
577*e038c9c4Sjoerg return;
578*e038c9c4Sjoerg
579*e038c9c4Sjoerg const LocationContext *LC = C.getLocationContext();
580*e038c9c4Sjoerg InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
581*e038c9c4Sjoerg CallExpr, LC, InnerPointerType, C.blockCount());
582*e038c9c4Sjoerg State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
583*e038c9c4Sjoerg }
584*e038c9c4Sjoerg
585*e038c9c4Sjoerg if (State->isNull(InnerPointerVal).isConstrainedTrue()) {
586*e038c9c4Sjoerg State = State->BindExpr(CallExpr, C.getLocationContext(),
587*e038c9c4Sjoerg C.getSValBuilder().makeTruthVal(false));
588*e038c9c4Sjoerg
589*e038c9c4Sjoerg C.addTransition(State);
590*e038c9c4Sjoerg return;
591*e038c9c4Sjoerg } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) {
592*e038c9c4Sjoerg State = State->BindExpr(CallExpr, C.getLocationContext(),
593*e038c9c4Sjoerg C.getSValBuilder().makeTruthVal(true));
594*e038c9c4Sjoerg
595*e038c9c4Sjoerg C.addTransition(State);
596*e038c9c4Sjoerg return;
597*e038c9c4Sjoerg } else if (move::isMovedFrom(State, ThisRegion)) {
598*e038c9c4Sjoerg C.addTransition(
599*e038c9c4Sjoerg State->BindExpr(CallExpr, C.getLocationContext(),
600*e038c9c4Sjoerg C.getSValBuilder().makeZeroVal(Call.getResultType())));
601*e038c9c4Sjoerg return;
602*e038c9c4Sjoerg } else {
603*e038c9c4Sjoerg ProgramStateRef NotNullState, NullState;
604*e038c9c4Sjoerg std::tie(NotNullState, NullState) =
605*e038c9c4Sjoerg State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>());
606*e038c9c4Sjoerg
607*e038c9c4Sjoerg auto NullVal = C.getSValBuilder().makeNull();
608*e038c9c4Sjoerg // Explicitly tracking the region as null.
609*e038c9c4Sjoerg NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal);
610*e038c9c4Sjoerg
611*e038c9c4Sjoerg NullState = NullState->BindExpr(CallExpr, C.getLocationContext(),
612*e038c9c4Sjoerg C.getSValBuilder().makeTruthVal(false));
613*e038c9c4Sjoerg C.addTransition(NullState, C.getNoteTag(
614*e038c9c4Sjoerg [ThisRegion](PathSensitiveBugReport &BR,
615*e038c9c4Sjoerg llvm::raw_ostream &OS) {
616*e038c9c4Sjoerg OS << "Assuming smart pointer";
617*e038c9c4Sjoerg checkAndPrettyPrintRegion(OS, ThisRegion);
618*e038c9c4Sjoerg OS << " is null";
619*e038c9c4Sjoerg },
620*e038c9c4Sjoerg /*IsPrunable=*/true));
621*e038c9c4Sjoerg NotNullState =
622*e038c9c4Sjoerg NotNullState->BindExpr(CallExpr, C.getLocationContext(),
623*e038c9c4Sjoerg C.getSValBuilder().makeTruthVal(true));
624*e038c9c4Sjoerg C.addTransition(
625*e038c9c4Sjoerg NotNullState,
626*e038c9c4Sjoerg C.getNoteTag(
627*e038c9c4Sjoerg [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
628*e038c9c4Sjoerg OS << "Assuming smart pointer";
629*e038c9c4Sjoerg checkAndPrettyPrintRegion(OS, ThisRegion);
630*e038c9c4Sjoerg OS << " is non-null";
631*e038c9c4Sjoerg },
632*e038c9c4Sjoerg /*IsPrunable=*/true));
633*e038c9c4Sjoerg return;
634*e038c9c4Sjoerg }
635*e038c9c4Sjoerg }
636*e038c9c4Sjoerg
registerSmartPtrModeling(CheckerManager & Mgr)637*e038c9c4Sjoerg void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
638*e038c9c4Sjoerg auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
639*e038c9c4Sjoerg Checker->ModelSmartPtrDereference =
640*e038c9c4Sjoerg Mgr.getAnalyzerOptions().getCheckerBooleanOption(
641*e038c9c4Sjoerg Checker, "ModelSmartPtrDereference");
642*e038c9c4Sjoerg }
643*e038c9c4Sjoerg
shouldRegisterSmartPtrModeling(const CheckerManager & mgr)644*e038c9c4Sjoerg bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
645*e038c9c4Sjoerg const LangOptions &LO = mgr.getLangOpts();
6467330f729Sjoerg return LO.CPlusPlus;
6477330f729Sjoerg }
648