1e5dd7070Spatrick // SmartPtrModeling.cpp - Model behavior of C++ smart pointers - C++ ------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // This file defines a checker that models various aspects of
10e5dd7070Spatrick // C++ smart pointer behavior.
11e5dd7070Spatrick //
12e5dd7070Spatrick //===----------------------------------------------------------------------===//
13e5dd7070Spatrick
14e5dd7070Spatrick #include "Move.h"
15ec727ea7Spatrick #include "SmartPtr.h"
16e5dd7070Spatrick
17ec727ea7Spatrick #include "clang/AST/DeclCXX.h"
18a9ac8606Spatrick #include "clang/AST/DeclarationName.h"
19e5dd7070Spatrick #include "clang/AST/ExprCXX.h"
20ec727ea7Spatrick #include "clang/AST/Type.h"
21a9ac8606Spatrick #include "clang/Basic/LLVM.h"
22e5dd7070Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
23e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
24e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h"
25e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/CheckerManager.h"
26*12c85518Srobert #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
27e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
28e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
29a9ac8606Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
30a9ac8606Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
31ec727ea7Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
32ec727ea7Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
33a9ac8606Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
34a9ac8606Spatrick #include "llvm/ADT/StringMap.h"
35a9ac8606Spatrick #include "llvm/Support/ErrorHandling.h"
36*12c85518Srobert #include <optional>
37a9ac8606Spatrick #include <string>
38e5dd7070Spatrick
39e5dd7070Spatrick using namespace clang;
40e5dd7070Spatrick using namespace ento;
41e5dd7070Spatrick
42e5dd7070Spatrick namespace {
43ec727ea7Spatrick
44a9ac8606Spatrick class SmartPtrModeling
45a9ac8606Spatrick : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges,
46a9ac8606Spatrick check::LiveSymbols> {
47a9ac8606Spatrick
48a9ac8606Spatrick bool isBoolConversionMethod(const CallEvent &Call) const;
49e5dd7070Spatrick
50e5dd7070Spatrick public:
51ec727ea7Spatrick // Whether the checker should model for null dereferences of smart pointers.
52*12c85518Srobert bool ModelSmartPtrDereference = false;
53e5dd7070Spatrick bool evalCall(const CallEvent &Call, CheckerContext &C) const;
54ec727ea7Spatrick void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
55a9ac8606Spatrick ProgramStateRef
56a9ac8606Spatrick checkRegionChanges(ProgramStateRef State,
57a9ac8606Spatrick const InvalidatedSymbols *Invalidated,
58a9ac8606Spatrick ArrayRef<const MemRegion *> ExplicitRegions,
59a9ac8606Spatrick ArrayRef<const MemRegion *> Regions,
60a9ac8606Spatrick const LocationContext *LCtx, const CallEvent *Call) const;
61a9ac8606Spatrick void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
62a9ac8606Spatrick const char *Sep) const override;
63a9ac8606Spatrick void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
64ec727ea7Spatrick
65ec727ea7Spatrick private:
66ec727ea7Spatrick void handleReset(const CallEvent &Call, CheckerContext &C) const;
67ec727ea7Spatrick void handleRelease(const CallEvent &Call, CheckerContext &C) const;
68a9ac8606Spatrick void handleSwapMethod(const CallEvent &Call, CheckerContext &C) const;
69a9ac8606Spatrick void handleGet(const CallEvent &Call, CheckerContext &C) const;
70a9ac8606Spatrick bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const;
71a9ac8606Spatrick bool handleMoveCtr(const CallEvent &Call, CheckerContext &C,
72a9ac8606Spatrick const MemRegion *ThisRegion) const;
73a9ac8606Spatrick bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion,
74*12c85518Srobert const MemRegion *OtherSmartPtrRegion,
75*12c85518Srobert const CallEvent &Call) const;
76a9ac8606Spatrick void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const;
77a9ac8606Spatrick bool handleComparisionOp(const CallEvent &Call, CheckerContext &C) const;
78a9ac8606Spatrick bool handleOstreamOperator(const CallEvent &Call, CheckerContext &C) const;
79a9ac8606Spatrick bool handleSwap(ProgramStateRef State, SVal First, SVal Second,
80a9ac8606Spatrick CheckerContext &C) const;
81a9ac8606Spatrick std::pair<SVal, ProgramStateRef>
82a9ac8606Spatrick retrieveOrConjureInnerPtrVal(ProgramStateRef State,
83a9ac8606Spatrick const MemRegion *ThisRegion, const Expr *E,
84a9ac8606Spatrick QualType Type, CheckerContext &C) const;
85ec727ea7Spatrick
86ec727ea7Spatrick using SmartPtrMethodHandlerFn =
87ec727ea7Spatrick void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
88ec727ea7Spatrick CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
89*12c85518Srobert {{{"reset"}}, &SmartPtrModeling::handleReset},
90*12c85518Srobert {{{"release"}}, &SmartPtrModeling::handleRelease},
91*12c85518Srobert {{{"swap"}, 1}, &SmartPtrModeling::handleSwapMethod},
92*12c85518Srobert {{{"get"}}, &SmartPtrModeling::handleGet}};
93a9ac8606Spatrick const CallDescription StdSwapCall{{"std", "swap"}, 2};
94a9ac8606Spatrick const CallDescription StdMakeUniqueCall{{"std", "make_unique"}};
95a9ac8606Spatrick const CallDescription StdMakeUniqueForOverwriteCall{
96a9ac8606Spatrick {"std", "make_unique_for_overwrite"}};
97e5dd7070Spatrick };
98e5dd7070Spatrick } // end of anonymous namespace
99e5dd7070Spatrick
REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap,const MemRegion *,SVal)100ec727ea7Spatrick REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
101ec727ea7Spatrick
102a9ac8606Spatrick // Checks if RD has name in Names and is in std namespace
103a9ac8606Spatrick static bool hasStdClassWithName(const CXXRecordDecl *RD,
104a9ac8606Spatrick ArrayRef<llvm::StringLiteral> Names) {
105a9ac8606Spatrick if (!RD || !RD->getDeclContext()->isStdNamespace())
106a9ac8606Spatrick return false;
107*12c85518Srobert if (RD->getDeclName().isIdentifier())
108*12c85518Srobert return llvm::is_contained(Names, RD->getName());
109a9ac8606Spatrick return false;
110a9ac8606Spatrick }
111a9ac8606Spatrick
112a9ac8606Spatrick constexpr llvm::StringLiteral STD_PTR_NAMES[] = {"shared_ptr", "unique_ptr",
113a9ac8606Spatrick "weak_ptr"};
114a9ac8606Spatrick
isStdSmartPtr(const CXXRecordDecl * RD)115a9ac8606Spatrick static bool isStdSmartPtr(const CXXRecordDecl *RD) {
116a9ac8606Spatrick return hasStdClassWithName(RD, STD_PTR_NAMES);
117a9ac8606Spatrick }
118a9ac8606Spatrick
isStdSmartPtr(const Expr * E)119a9ac8606Spatrick static bool isStdSmartPtr(const Expr *E) {
120a9ac8606Spatrick return isStdSmartPtr(E->getType()->getAsCXXRecordDecl());
121a9ac8606Spatrick }
122a9ac8606Spatrick
123ec727ea7Spatrick // Define the inter-checker API.
124ec727ea7Spatrick namespace clang {
125ec727ea7Spatrick namespace ento {
126ec727ea7Spatrick namespace smartptr {
isStdSmartPtrCall(const CallEvent & Call)127ec727ea7Spatrick bool isStdSmartPtrCall(const CallEvent &Call) {
128ec727ea7Spatrick const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
129ec727ea7Spatrick if (!MethodDecl || !MethodDecl->getParent())
130ec727ea7Spatrick return false;
131a9ac8606Spatrick return isStdSmartPtr(MethodDecl->getParent());
132a9ac8606Spatrick }
133ec727ea7Spatrick
isStdSmartPtr(const CXXRecordDecl * RD)134a9ac8606Spatrick bool isStdSmartPtr(const CXXRecordDecl *RD) {
135a9ac8606Spatrick if (!RD || !RD->getDeclContext()->isStdNamespace())
136ec727ea7Spatrick return false;
137ec727ea7Spatrick
138a9ac8606Spatrick if (RD->getDeclName().isIdentifier()) {
139a9ac8606Spatrick StringRef Name = RD->getName();
140ec727ea7Spatrick return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr";
141ec727ea7Spatrick }
142ec727ea7Spatrick return false;
143ec727ea7Spatrick }
144ec727ea7Spatrick
isStdSmartPtr(const Expr * E)145a9ac8606Spatrick bool isStdSmartPtr(const Expr *E) {
146a9ac8606Spatrick return isStdSmartPtr(E->getType()->getAsCXXRecordDecl());
147a9ac8606Spatrick }
148a9ac8606Spatrick
isNullSmartPtr(const ProgramStateRef State,const MemRegion * ThisRegion)149ec727ea7Spatrick bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
150ec727ea7Spatrick const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
151a9ac8606Spatrick return InnerPointVal &&
152a9ac8606Spatrick !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true);
153ec727ea7Spatrick }
154ec727ea7Spatrick } // namespace smartptr
155ec727ea7Spatrick } // namespace ento
156ec727ea7Spatrick } // namespace clang
157ec727ea7Spatrick
158a9ac8606Spatrick // If a region is removed all of the subregions need to be removed too.
159a9ac8606Spatrick static TrackedRegionMapTy
removeTrackedSubregions(TrackedRegionMapTy RegionMap,TrackedRegionMapTy::Factory & RegionMapFactory,const MemRegion * Region)160a9ac8606Spatrick removeTrackedSubregions(TrackedRegionMapTy RegionMap,
161a9ac8606Spatrick TrackedRegionMapTy::Factory &RegionMapFactory,
162a9ac8606Spatrick const MemRegion *Region) {
163a9ac8606Spatrick if (!Region)
164a9ac8606Spatrick return RegionMap;
165a9ac8606Spatrick for (const auto &E : RegionMap) {
166a9ac8606Spatrick if (E.first->isSubRegionOf(Region))
167a9ac8606Spatrick RegionMap = RegionMapFactory.remove(RegionMap, E.first);
168a9ac8606Spatrick }
169a9ac8606Spatrick return RegionMap;
170a9ac8606Spatrick }
171a9ac8606Spatrick
updateSwappedRegion(ProgramStateRef State,const MemRegion * Region,const SVal * RegionInnerPointerVal)172a9ac8606Spatrick static ProgramStateRef updateSwappedRegion(ProgramStateRef State,
173a9ac8606Spatrick const MemRegion *Region,
174a9ac8606Spatrick const SVal *RegionInnerPointerVal) {
175a9ac8606Spatrick if (RegionInnerPointerVal) {
176a9ac8606Spatrick State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal);
177a9ac8606Spatrick } else {
178a9ac8606Spatrick State = State->remove<TrackedRegionMap>(Region);
179a9ac8606Spatrick }
180a9ac8606Spatrick return State;
181a9ac8606Spatrick }
182a9ac8606Spatrick
getInnerPointerType(CheckerContext C,const CXXRecordDecl * RD)183a9ac8606Spatrick static QualType getInnerPointerType(CheckerContext C, const CXXRecordDecl *RD) {
184a9ac8606Spatrick if (!RD || !RD->isInStdNamespace())
185a9ac8606Spatrick return {};
186a9ac8606Spatrick
187a9ac8606Spatrick const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
188a9ac8606Spatrick if (!TSD)
189a9ac8606Spatrick return {};
190a9ac8606Spatrick
191a9ac8606Spatrick auto TemplateArgs = TSD->getTemplateArgs().asArray();
192a9ac8606Spatrick if (TemplateArgs.empty())
193a9ac8606Spatrick return {};
194a9ac8606Spatrick auto InnerValueType = TemplateArgs[0].getAsType();
195a9ac8606Spatrick return C.getASTContext().getPointerType(InnerValueType.getCanonicalType());
196a9ac8606Spatrick }
197a9ac8606Spatrick
198a9ac8606Spatrick // This is for use with standalone-functions like std::make_unique,
199a9ac8606Spatrick // std::make_unique_for_overwrite, etc. It reads the template parameter and
200a9ac8606Spatrick // returns the pointer type corresponding to it,
getPointerTypeFromTemplateArg(const CallEvent & Call,CheckerContext & C)201a9ac8606Spatrick static QualType getPointerTypeFromTemplateArg(const CallEvent &Call,
202a9ac8606Spatrick CheckerContext &C) {
203a9ac8606Spatrick const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
204a9ac8606Spatrick if (!FD || !FD->isFunctionTemplateSpecialization())
205a9ac8606Spatrick return {};
206a9ac8606Spatrick const auto &TemplateArgs = FD->getTemplateSpecializationArgs()->asArray();
207a9ac8606Spatrick if (TemplateArgs.size() == 0)
208a9ac8606Spatrick return {};
209a9ac8606Spatrick auto ValueType = TemplateArgs[0].getAsType();
210a9ac8606Spatrick return C.getASTContext().getPointerType(ValueType.getCanonicalType());
211a9ac8606Spatrick }
212a9ac8606Spatrick
213a9ac8606Spatrick // Helper method to get the inner pointer type of specialized smart pointer
214a9ac8606Spatrick // Returns empty type if not found valid inner pointer type.
getInnerPointerType(const CallEvent & Call,CheckerContext & C)215a9ac8606Spatrick static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) {
216a9ac8606Spatrick const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
217a9ac8606Spatrick if (!MethodDecl || !MethodDecl->getParent())
218a9ac8606Spatrick return {};
219a9ac8606Spatrick
220a9ac8606Spatrick const auto *RecordDecl = MethodDecl->getParent();
221a9ac8606Spatrick return getInnerPointerType(C, RecordDecl);
222a9ac8606Spatrick }
223a9ac8606Spatrick
224a9ac8606Spatrick // Helper method to pretty print region and avoid extra spacing.
checkAndPrettyPrintRegion(llvm::raw_ostream & OS,const MemRegion * Region)225a9ac8606Spatrick static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS,
226a9ac8606Spatrick const MemRegion *Region) {
227a9ac8606Spatrick if (Region->canPrintPretty()) {
228a9ac8606Spatrick OS << " ";
229a9ac8606Spatrick Region->printPretty(OS);
230a9ac8606Spatrick }
231a9ac8606Spatrick }
232a9ac8606Spatrick
isBoolConversionMethod(const CallEvent & Call) const233a9ac8606Spatrick bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const {
234e5dd7070Spatrick // TODO: Update CallDescription to support anonymous calls?
235e5dd7070Spatrick // TODO: Handle other methods, such as .get() or .release().
236e5dd7070Spatrick // But once we do, we'd need a visitor to explain null dereferences
237e5dd7070Spatrick // that are found via such modeling.
238e5dd7070Spatrick const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl());
239e5dd7070Spatrick return CD && CD->getConversionType()->isBooleanType();
240e5dd7070Spatrick }
241e5dd7070Spatrick
242a9ac8606Spatrick constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES[] = {"basic_ostream"};
243a9ac8606Spatrick
isStdBasicOstream(const Expr * E)244a9ac8606Spatrick bool isStdBasicOstream(const Expr *E) {
245a9ac8606Spatrick const auto *RD = E->getType()->getAsCXXRecordDecl();
246a9ac8606Spatrick return hasStdClassWithName(RD, BASIC_OSTREAM_NAMES);
247a9ac8606Spatrick }
248a9ac8606Spatrick
isStdFunctionCall(const CallEvent & Call)249a9ac8606Spatrick static bool isStdFunctionCall(const CallEvent &Call) {
250a9ac8606Spatrick return Call.getDecl() && Call.getDecl()->getDeclContext()->isStdNamespace();
251a9ac8606Spatrick }
252a9ac8606Spatrick
isStdOstreamOperatorCall(const CallEvent & Call)253a9ac8606Spatrick bool isStdOstreamOperatorCall(const CallEvent &Call) {
254a9ac8606Spatrick if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call))
255a9ac8606Spatrick return false;
256a9ac8606Spatrick const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
257a9ac8606Spatrick if (!FC)
258a9ac8606Spatrick return false;
259a9ac8606Spatrick const FunctionDecl *FD = FC->getDecl();
260a9ac8606Spatrick if (!FD->isOverloadedOperator())
261a9ac8606Spatrick return false;
262a9ac8606Spatrick const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
263a9ac8606Spatrick if (OOK != clang::OO_LessLess)
264a9ac8606Spatrick return false;
265a9ac8606Spatrick return isStdSmartPtr(Call.getArgExpr(1)) &&
266a9ac8606Spatrick isStdBasicOstream(Call.getArgExpr(0));
267a9ac8606Spatrick }
268a9ac8606Spatrick
isPotentiallyComparisionOpCall(const CallEvent & Call)269a9ac8606Spatrick static bool isPotentiallyComparisionOpCall(const CallEvent &Call) {
270a9ac8606Spatrick if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call))
271a9ac8606Spatrick return false;
272a9ac8606Spatrick return smartptr::isStdSmartPtr(Call.getArgExpr(0)) ||
273a9ac8606Spatrick smartptr::isStdSmartPtr(Call.getArgExpr(1));
274a9ac8606Spatrick }
275a9ac8606Spatrick
evalCall(const CallEvent & Call,CheckerContext & C) const276e5dd7070Spatrick bool SmartPtrModeling::evalCall(const CallEvent &Call,
277e5dd7070Spatrick CheckerContext &C) const {
278ec727ea7Spatrick
279a9ac8606Spatrick ProgramStateRef State = C.getState();
280a9ac8606Spatrick
281a9ac8606Spatrick // If any one of the arg is a unique_ptr, then
282a9ac8606Spatrick // we can try this function
283a9ac8606Spatrick if (ModelSmartPtrDereference && isPotentiallyComparisionOpCall(Call))
284a9ac8606Spatrick if (handleComparisionOp(Call, C))
285a9ac8606Spatrick return true;
286a9ac8606Spatrick
287a9ac8606Spatrick if (ModelSmartPtrDereference && isStdOstreamOperatorCall(Call))
288a9ac8606Spatrick return handleOstreamOperator(Call, C);
289a9ac8606Spatrick
290*12c85518Srobert if (StdSwapCall.matches(Call)) {
291a9ac8606Spatrick // Check the first arg, if it is of std::unique_ptr type.
292a9ac8606Spatrick assert(Call.getNumArgs() == 2 && "std::swap should have two arguments");
293a9ac8606Spatrick const Expr *FirstArg = Call.getArgExpr(0);
294a9ac8606Spatrick if (!smartptr::isStdSmartPtr(FirstArg->getType()->getAsCXXRecordDecl()))
295a9ac8606Spatrick return false;
296a9ac8606Spatrick return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C);
297a9ac8606Spatrick }
298a9ac8606Spatrick
299*12c85518Srobert if (matchesAny(Call, StdMakeUniqueCall, StdMakeUniqueForOverwriteCall)) {
300a9ac8606Spatrick if (!ModelSmartPtrDereference)
301a9ac8606Spatrick return false;
302a9ac8606Spatrick
303*12c85518Srobert const std::optional<SVal> ThisRegionOpt =
304*12c85518Srobert Call.getReturnValueUnderConstruction();
305a9ac8606Spatrick if (!ThisRegionOpt)
306a9ac8606Spatrick return false;
307a9ac8606Spatrick
308a9ac8606Spatrick const auto PtrVal = C.getSValBuilder().getConjuredHeapSymbolVal(
309a9ac8606Spatrick Call.getOriginExpr(), C.getLocationContext(),
310a9ac8606Spatrick getPointerTypeFromTemplateArg(Call, C), C.blockCount());
311a9ac8606Spatrick
312a9ac8606Spatrick const MemRegion *ThisRegion = ThisRegionOpt->getAsRegion();
313a9ac8606Spatrick State = State->set<TrackedRegionMap>(ThisRegion, PtrVal);
314a9ac8606Spatrick State = State->assume(PtrVal, true);
315a9ac8606Spatrick
316a9ac8606Spatrick // TODO: ExprEngine should do this for us.
317a9ac8606Spatrick // For a bit more context:
318a9ac8606Spatrick // 1) Why do we need this? Since we are modelling a "function"
319a9ac8606Spatrick // that returns a constructed object we need to store this information in
320a9ac8606Spatrick // the program state.
321a9ac8606Spatrick //
322a9ac8606Spatrick // 2) Why does this work?
323a9ac8606Spatrick // `updateObjectsUnderConstruction` does exactly as it sounds.
324a9ac8606Spatrick //
325a9ac8606Spatrick // 3) How should it look like when moved to the Engine?
326a9ac8606Spatrick // It would be nice if we can just
327a9ac8606Spatrick // pretend we don't need to know about this - ie, completely automatic work.
328a9ac8606Spatrick // However, realistically speaking, I think we would need to "signal" the
329a9ac8606Spatrick // ExprEngine evalCall handler that we are constructing an object with this
330a9ac8606Spatrick // function call (constructors obviously construct, hence can be
331a9ac8606Spatrick // automatically deduced).
332a9ac8606Spatrick auto &Engine = State->getStateManager().getOwningEngine();
333a9ac8606Spatrick State = Engine.updateObjectsUnderConstruction(
334a9ac8606Spatrick *ThisRegionOpt, nullptr, State, C.getLocationContext(),
335a9ac8606Spatrick Call.getConstructionContext(), {});
336a9ac8606Spatrick
337a9ac8606Spatrick // We don't leave a note here since it is guaranteed the
338a9ac8606Spatrick // unique_ptr from this call is non-null (hence is safe to de-reference).
339a9ac8606Spatrick C.addTransition(State);
340a9ac8606Spatrick return true;
341a9ac8606Spatrick }
342a9ac8606Spatrick
343ec727ea7Spatrick if (!smartptr::isStdSmartPtrCall(Call))
344e5dd7070Spatrick return false;
345e5dd7070Spatrick
346a9ac8606Spatrick if (isBoolConversionMethod(Call)) {
347e5dd7070Spatrick const MemRegion *ThisR =
348e5dd7070Spatrick cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
349e5dd7070Spatrick
350a9ac8606Spatrick if (ModelSmartPtrDereference) {
351a9ac8606Spatrick // The check for the region is moved is duplicated in handleBoolOperation
352a9ac8606Spatrick // method.
353a9ac8606Spatrick // FIXME: Once we model std::move for smart pointers clean up this and use
354a9ac8606Spatrick // that modeling.
355a9ac8606Spatrick handleBoolConversion(Call, C);
356a9ac8606Spatrick return true;
357a9ac8606Spatrick } else {
358e5dd7070Spatrick if (!move::isMovedFrom(State, ThisR)) {
359a9ac8606Spatrick // TODO: Model this case as well. At least, avoid invalidation of
360a9ac8606Spatrick // globals.
361e5dd7070Spatrick return false;
362e5dd7070Spatrick }
363e5dd7070Spatrick
364e5dd7070Spatrick // TODO: Add a note to bug reports describing this decision.
365a9ac8606Spatrick C.addTransition(State->BindExpr(
366a9ac8606Spatrick Call.getOriginExpr(), C.getLocationContext(),
367e5dd7070Spatrick C.getSValBuilder().makeZeroVal(Call.getResultType())));
368a9ac8606Spatrick
369e5dd7070Spatrick return true;
370e5dd7070Spatrick }
371a9ac8606Spatrick }
372e5dd7070Spatrick
373ec727ea7Spatrick if (!ModelSmartPtrDereference)
374ec727ea7Spatrick return false;
375ec727ea7Spatrick
376ec727ea7Spatrick if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
377a9ac8606Spatrick if (CC->getDecl()->isCopyConstructor())
378ec727ea7Spatrick return false;
379ec727ea7Spatrick
380a9ac8606Spatrick const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion();
381a9ac8606Spatrick if (!ThisRegion)
382ec727ea7Spatrick return false;
383ec727ea7Spatrick
384*12c85518Srobert QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
385*12c85518Srobert
386a9ac8606Spatrick if (CC->getDecl()->isMoveConstructor())
387a9ac8606Spatrick return handleMoveCtr(Call, C, ThisRegion);
388a9ac8606Spatrick
389a9ac8606Spatrick if (Call.getNumArgs() == 0) {
390*12c85518Srobert auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
391a9ac8606Spatrick State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
392a9ac8606Spatrick
393a9ac8606Spatrick C.addTransition(
394a9ac8606Spatrick State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
395a9ac8606Spatrick llvm::raw_ostream &OS) {
396a9ac8606Spatrick if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
397a9ac8606Spatrick !BR.isInteresting(ThisRegion))
398a9ac8606Spatrick return;
399a9ac8606Spatrick OS << "Default constructed smart pointer";
400a9ac8606Spatrick checkAndPrettyPrintRegion(OS, ThisRegion);
401a9ac8606Spatrick OS << " is null";
402a9ac8606Spatrick }));
403a9ac8606Spatrick } else {
404a9ac8606Spatrick const auto *TrackingExpr = Call.getArgExpr(0);
405a9ac8606Spatrick assert(TrackingExpr->getType()->isPointerType() &&
406a9ac8606Spatrick "Adding a non pointer value to TrackedRegionMap");
407a9ac8606Spatrick auto ArgVal = Call.getArgSVal(0);
408a9ac8606Spatrick State = State->set<TrackedRegionMap>(ThisRegion, ArgVal);
409a9ac8606Spatrick
410a9ac8606Spatrick C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr,
411a9ac8606Spatrick ArgVal](PathSensitiveBugReport &BR,
412a9ac8606Spatrick llvm::raw_ostream &OS) {
413a9ac8606Spatrick if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
414a9ac8606Spatrick !BR.isInteresting(ThisRegion))
415a9ac8606Spatrick return;
416a9ac8606Spatrick bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
417a9ac8606Spatrick OS << "Smart pointer";
418a9ac8606Spatrick checkAndPrettyPrintRegion(OS, ThisRegion);
419a9ac8606Spatrick if (ArgVal.isZeroConstant())
420a9ac8606Spatrick OS << " is constructed using a null value";
421a9ac8606Spatrick else
422a9ac8606Spatrick OS << " is constructed";
423a9ac8606Spatrick }));
424a9ac8606Spatrick }
425ec727ea7Spatrick return true;
426e5dd7070Spatrick }
427e5dd7070Spatrick
428a9ac8606Spatrick if (handleAssignOp(Call, C))
429a9ac8606Spatrick return true;
430a9ac8606Spatrick
431ec727ea7Spatrick const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
432ec727ea7Spatrick if (!Handler)
433ec727ea7Spatrick return false;
434ec727ea7Spatrick (this->**Handler)(Call, C);
435ec727ea7Spatrick
436ec727ea7Spatrick return C.isDifferent();
437ec727ea7Spatrick }
438ec727ea7Spatrick
retrieveOrConjureInnerPtrVal(ProgramStateRef State,const MemRegion * ThisRegion,const Expr * E,QualType Type,CheckerContext & C) const439a9ac8606Spatrick std::pair<SVal, ProgramStateRef> SmartPtrModeling::retrieveOrConjureInnerPtrVal(
440a9ac8606Spatrick ProgramStateRef State, const MemRegion *ThisRegion, const Expr *E,
441a9ac8606Spatrick QualType Type, CheckerContext &C) const {
442a9ac8606Spatrick const auto *Ptr = State->get<TrackedRegionMap>(ThisRegion);
443a9ac8606Spatrick if (Ptr)
444a9ac8606Spatrick return {*Ptr, State};
445a9ac8606Spatrick auto Val = C.getSValBuilder().conjureSymbolVal(E, C.getLocationContext(),
446a9ac8606Spatrick Type, C.blockCount());
447a9ac8606Spatrick State = State->set<TrackedRegionMap>(ThisRegion, Val);
448a9ac8606Spatrick return {Val, State};
449a9ac8606Spatrick }
450a9ac8606Spatrick
handleComparisionOp(const CallEvent & Call,CheckerContext & C) const451a9ac8606Spatrick bool SmartPtrModeling::handleComparisionOp(const CallEvent &Call,
452a9ac8606Spatrick CheckerContext &C) const {
453a9ac8606Spatrick const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
454a9ac8606Spatrick if (!FC)
455a9ac8606Spatrick return false;
456a9ac8606Spatrick const FunctionDecl *FD = FC->getDecl();
457a9ac8606Spatrick if (!FD->isOverloadedOperator())
458a9ac8606Spatrick return false;
459a9ac8606Spatrick const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
460a9ac8606Spatrick if (!(OOK == OO_EqualEqual || OOK == OO_ExclaimEqual || OOK == OO_Less ||
461a9ac8606Spatrick OOK == OO_LessEqual || OOK == OO_Greater || OOK == OO_GreaterEqual ||
462a9ac8606Spatrick OOK == OO_Spaceship))
463a9ac8606Spatrick return false;
464a9ac8606Spatrick
465a9ac8606Spatrick // There are some special cases about which we can infer about
466a9ac8606Spatrick // the resulting answer.
467a9ac8606Spatrick // For reference, there is a discussion at https://reviews.llvm.org/D104616.
468a9ac8606Spatrick // Also, the cppreference page is good to look at
469a9ac8606Spatrick // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp.
470a9ac8606Spatrick
471a9ac8606Spatrick auto makeSValFor = [&C, this](ProgramStateRef State, const Expr *E,
472a9ac8606Spatrick SVal S) -> std::pair<SVal, ProgramStateRef> {
473a9ac8606Spatrick if (S.isZeroConstant()) {
474a9ac8606Spatrick return {S, State};
475a9ac8606Spatrick }
476a9ac8606Spatrick const MemRegion *Reg = S.getAsRegion();
477a9ac8606Spatrick assert(Reg &&
478a9ac8606Spatrick "this pointer of std::unique_ptr should be obtainable as MemRegion");
479a9ac8606Spatrick QualType Type = getInnerPointerType(C, E->getType()->getAsCXXRecordDecl());
480a9ac8606Spatrick return retrieveOrConjureInnerPtrVal(State, Reg, E, Type, C);
481a9ac8606Spatrick };
482a9ac8606Spatrick
483a9ac8606Spatrick SVal First = Call.getArgSVal(0);
484a9ac8606Spatrick SVal Second = Call.getArgSVal(1);
485a9ac8606Spatrick const auto *FirstExpr = Call.getArgExpr(0);
486a9ac8606Spatrick const auto *SecondExpr = Call.getArgExpr(1);
487a9ac8606Spatrick
488a9ac8606Spatrick const auto *ResultExpr = Call.getOriginExpr();
489a9ac8606Spatrick const auto *LCtx = C.getLocationContext();
490a9ac8606Spatrick auto &Bldr = C.getSValBuilder();
491a9ac8606Spatrick ProgramStateRef State = C.getState();
492a9ac8606Spatrick
493a9ac8606Spatrick SVal FirstPtrVal, SecondPtrVal;
494a9ac8606Spatrick std::tie(FirstPtrVal, State) = makeSValFor(State, FirstExpr, First);
495a9ac8606Spatrick std::tie(SecondPtrVal, State) = makeSValFor(State, SecondExpr, Second);
496a9ac8606Spatrick BinaryOperatorKind BOK =
497a9ac8606Spatrick operationKindFromOverloadedOperator(OOK, true).GetBinaryOpUnsafe();
498a9ac8606Spatrick auto RetVal = Bldr.evalBinOp(State, BOK, FirstPtrVal, SecondPtrVal,
499a9ac8606Spatrick Call.getResultType());
500a9ac8606Spatrick
501a9ac8606Spatrick if (OOK != OO_Spaceship) {
502a9ac8606Spatrick ProgramStateRef TrueState, FalseState;
503a9ac8606Spatrick std::tie(TrueState, FalseState) =
504a9ac8606Spatrick State->assume(*RetVal.getAs<DefinedOrUnknownSVal>());
505a9ac8606Spatrick if (TrueState)
506a9ac8606Spatrick C.addTransition(
507a9ac8606Spatrick TrueState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(true)));
508a9ac8606Spatrick if (FalseState)
509a9ac8606Spatrick C.addTransition(
510a9ac8606Spatrick FalseState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(false)));
511a9ac8606Spatrick } else {
512a9ac8606Spatrick C.addTransition(State->BindExpr(ResultExpr, LCtx, RetVal));
513a9ac8606Spatrick }
514a9ac8606Spatrick return true;
515a9ac8606Spatrick }
516a9ac8606Spatrick
handleOstreamOperator(const CallEvent & Call,CheckerContext & C) const517a9ac8606Spatrick bool SmartPtrModeling::handleOstreamOperator(const CallEvent &Call,
518a9ac8606Spatrick CheckerContext &C) const {
519a9ac8606Spatrick // operator<< does not modify the smart pointer.
520a9ac8606Spatrick // And we don't really have much of modelling of basic_ostream.
521a9ac8606Spatrick // So, we are better off:
522a9ac8606Spatrick // 1) Invalidating the mem-region of the ostream object at hand.
523a9ac8606Spatrick // 2) Setting the SVal of the basic_ostream as the return value.
524a9ac8606Spatrick // Not very satisfying, but it gets the job done, and is better
525a9ac8606Spatrick // than the default handling. :)
526a9ac8606Spatrick
527a9ac8606Spatrick ProgramStateRef State = C.getState();
528a9ac8606Spatrick const auto StreamVal = Call.getArgSVal(0);
529a9ac8606Spatrick const MemRegion *StreamThisRegion = StreamVal.getAsRegion();
530a9ac8606Spatrick if (!StreamThisRegion)
531a9ac8606Spatrick return false;
532a9ac8606Spatrick State =
533a9ac8606Spatrick State->invalidateRegions({StreamThisRegion}, Call.getOriginExpr(),
534a9ac8606Spatrick C.blockCount(), C.getLocationContext(), false);
535a9ac8606Spatrick State =
536a9ac8606Spatrick State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), StreamVal);
537a9ac8606Spatrick C.addTransition(State);
538a9ac8606Spatrick return true;
539a9ac8606Spatrick }
540a9ac8606Spatrick
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const541ec727ea7Spatrick void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
542ec727ea7Spatrick CheckerContext &C) const {
543ec727ea7Spatrick ProgramStateRef State = C.getState();
544ec727ea7Spatrick // Clean up dead regions from the region map.
545ec727ea7Spatrick TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
546ec727ea7Spatrick for (auto E : TrackedRegions) {
547ec727ea7Spatrick const MemRegion *Region = E.first;
548ec727ea7Spatrick bool IsRegDead = !SymReaper.isLiveRegion(Region);
549ec727ea7Spatrick
550ec727ea7Spatrick if (IsRegDead)
551ec727ea7Spatrick State = State->remove<TrackedRegionMap>(Region);
552ec727ea7Spatrick }
553ec727ea7Spatrick C.addTransition(State);
554ec727ea7Spatrick }
555ec727ea7Spatrick
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const556a9ac8606Spatrick void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State,
557a9ac8606Spatrick const char *NL, const char *Sep) const {
558a9ac8606Spatrick TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
559a9ac8606Spatrick
560a9ac8606Spatrick if (!RS.isEmpty()) {
561a9ac8606Spatrick Out << Sep << "Smart ptr regions :" << NL;
562a9ac8606Spatrick for (auto I : RS) {
563a9ac8606Spatrick I.first->dumpToStream(Out);
564a9ac8606Spatrick if (smartptr::isNullSmartPtr(State, I.first))
565a9ac8606Spatrick Out << ": Null";
566a9ac8606Spatrick else
567a9ac8606Spatrick Out << ": Non Null";
568a9ac8606Spatrick Out << NL;
569a9ac8606Spatrick }
570a9ac8606Spatrick }
571a9ac8606Spatrick }
572a9ac8606Spatrick
checkRegionChanges(ProgramStateRef State,const InvalidatedSymbols * Invalidated,ArrayRef<const MemRegion * > ExplicitRegions,ArrayRef<const MemRegion * > Regions,const LocationContext * LCtx,const CallEvent * Call) const573a9ac8606Spatrick ProgramStateRef SmartPtrModeling::checkRegionChanges(
574a9ac8606Spatrick ProgramStateRef State, const InvalidatedSymbols *Invalidated,
575a9ac8606Spatrick ArrayRef<const MemRegion *> ExplicitRegions,
576a9ac8606Spatrick ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
577a9ac8606Spatrick const CallEvent *Call) const {
578a9ac8606Spatrick TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>();
579a9ac8606Spatrick TrackedRegionMapTy::Factory &RegionMapFactory =
580a9ac8606Spatrick State->get_context<TrackedRegionMap>();
581a9ac8606Spatrick for (const auto *Region : Regions)
582a9ac8606Spatrick RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory,
583a9ac8606Spatrick Region->getBaseRegion());
584a9ac8606Spatrick return State->set<TrackedRegionMap>(RegionMap);
585a9ac8606Spatrick }
586a9ac8606Spatrick
checkLiveSymbols(ProgramStateRef State,SymbolReaper & SR) const587a9ac8606Spatrick void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State,
588a9ac8606Spatrick SymbolReaper &SR) const {
589a9ac8606Spatrick // Marking tracked symbols alive
590a9ac8606Spatrick TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
591a9ac8606Spatrick for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) {
592a9ac8606Spatrick SVal Val = I->second;
593a9ac8606Spatrick for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) {
594a9ac8606Spatrick SR.markLive(*si);
595a9ac8606Spatrick }
596a9ac8606Spatrick }
597a9ac8606Spatrick }
598a9ac8606Spatrick
handleReset(const CallEvent & Call,CheckerContext & C) const599ec727ea7Spatrick void SmartPtrModeling::handleReset(const CallEvent &Call,
600ec727ea7Spatrick CheckerContext &C) const {
601a9ac8606Spatrick ProgramStateRef State = C.getState();
602ec727ea7Spatrick const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
603ec727ea7Spatrick if (!IC)
604ec727ea7Spatrick return;
605ec727ea7Spatrick
606a9ac8606Spatrick const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
607a9ac8606Spatrick if (!ThisRegion)
608ec727ea7Spatrick return;
609a9ac8606Spatrick
610a9ac8606Spatrick assert(Call.getArgExpr(0)->getType()->isPointerType() &&
611a9ac8606Spatrick "Adding a non pointer value to TrackedRegionMap");
612a9ac8606Spatrick State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0));
613a9ac8606Spatrick const auto *TrackingExpr = Call.getArgExpr(0);
614a9ac8606Spatrick C.addTransition(
615a9ac8606Spatrick State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR,
616a9ac8606Spatrick llvm::raw_ostream &OS) {
617a9ac8606Spatrick if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
618a9ac8606Spatrick !BR.isInteresting(ThisRegion))
619a9ac8606Spatrick return;
620a9ac8606Spatrick bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
621a9ac8606Spatrick OS << "Smart pointer";
622a9ac8606Spatrick checkAndPrettyPrintRegion(OS, ThisRegion);
623a9ac8606Spatrick OS << " reset using a null value";
624a9ac8606Spatrick }));
625a9ac8606Spatrick // TODO: Make sure to ivalidate the region in the Store if we don't have
626ec727ea7Spatrick // time to model all methods.
627ec727ea7Spatrick }
628ec727ea7Spatrick
handleRelease(const CallEvent & Call,CheckerContext & C) const629ec727ea7Spatrick void SmartPtrModeling::handleRelease(const CallEvent &Call,
630ec727ea7Spatrick CheckerContext &C) const {
631a9ac8606Spatrick ProgramStateRef State = C.getState();
632ec727ea7Spatrick const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
633ec727ea7Spatrick if (!IC)
634ec727ea7Spatrick return;
635ec727ea7Spatrick
636a9ac8606Spatrick const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
637a9ac8606Spatrick if (!ThisRegion)
638ec727ea7Spatrick return;
639ec727ea7Spatrick
640a9ac8606Spatrick const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
641ec727ea7Spatrick
642ec727ea7Spatrick if (InnerPointVal) {
643ec727ea7Spatrick State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
644ec727ea7Spatrick *InnerPointVal);
645ec727ea7Spatrick }
646a9ac8606Spatrick
647*12c85518Srobert QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
648*12c85518Srobert auto ValueToUpdate = C.getSValBuilder().makeNullWithType(ThisType);
649a9ac8606Spatrick State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);
650a9ac8606Spatrick
651a9ac8606Spatrick C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
652a9ac8606Spatrick llvm::raw_ostream &OS) {
653a9ac8606Spatrick if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
654a9ac8606Spatrick !BR.isInteresting(ThisRegion))
655a9ac8606Spatrick return;
656a9ac8606Spatrick
657a9ac8606Spatrick OS << "Smart pointer";
658a9ac8606Spatrick checkAndPrettyPrintRegion(OS, ThisRegion);
659a9ac8606Spatrick OS << " is released and set to null";
660a9ac8606Spatrick }));
661ec727ea7Spatrick // TODO: Add support to enable MallocChecker to start tracking the raw
662ec727ea7Spatrick // pointer.
663ec727ea7Spatrick }
664ec727ea7Spatrick
handleSwapMethod(const CallEvent & Call,CheckerContext & C) const665a9ac8606Spatrick void SmartPtrModeling::handleSwapMethod(const CallEvent &Call,
666ec727ea7Spatrick CheckerContext &C) const {
667a9ac8606Spatrick // To model unique_ptr::swap() method.
668a9ac8606Spatrick const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
669a9ac8606Spatrick if (!IC)
670a9ac8606Spatrick return;
671a9ac8606Spatrick
672a9ac8606Spatrick auto State = C.getState();
673a9ac8606Spatrick handleSwap(State, IC->getCXXThisVal(), Call.getArgSVal(0), C);
674ec727ea7Spatrick }
675ec727ea7Spatrick
handleSwap(ProgramStateRef State,SVal First,SVal Second,CheckerContext & C) const676a9ac8606Spatrick bool SmartPtrModeling::handleSwap(ProgramStateRef State, SVal First,
677a9ac8606Spatrick SVal Second, CheckerContext &C) const {
678a9ac8606Spatrick const MemRegion *FirstThisRegion = First.getAsRegion();
679a9ac8606Spatrick if (!FirstThisRegion)
680a9ac8606Spatrick return false;
681a9ac8606Spatrick const MemRegion *SecondThisRegion = Second.getAsRegion();
682a9ac8606Spatrick if (!SecondThisRegion)
683a9ac8606Spatrick return false;
684a9ac8606Spatrick
685a9ac8606Spatrick const auto *FirstInnerPtrVal = State->get<TrackedRegionMap>(FirstThisRegion);
686a9ac8606Spatrick const auto *SecondInnerPtrVal =
687a9ac8606Spatrick State->get<TrackedRegionMap>(SecondThisRegion);
688a9ac8606Spatrick
689a9ac8606Spatrick State = updateSwappedRegion(State, FirstThisRegion, SecondInnerPtrVal);
690a9ac8606Spatrick State = updateSwappedRegion(State, SecondThisRegion, FirstInnerPtrVal);
691a9ac8606Spatrick
692a9ac8606Spatrick C.addTransition(State, C.getNoteTag([FirstThisRegion, SecondThisRegion](
693a9ac8606Spatrick PathSensitiveBugReport &BR,
694a9ac8606Spatrick llvm::raw_ostream &OS) {
695a9ac8606Spatrick if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
696a9ac8606Spatrick return;
697a9ac8606Spatrick if (BR.isInteresting(FirstThisRegion) &&
698a9ac8606Spatrick !BR.isInteresting(SecondThisRegion)) {
699a9ac8606Spatrick BR.markInteresting(SecondThisRegion);
700a9ac8606Spatrick BR.markNotInteresting(FirstThisRegion);
701a9ac8606Spatrick }
702a9ac8606Spatrick if (BR.isInteresting(SecondThisRegion) &&
703a9ac8606Spatrick !BR.isInteresting(FirstThisRegion)) {
704a9ac8606Spatrick BR.markInteresting(FirstThisRegion);
705a9ac8606Spatrick BR.markNotInteresting(SecondThisRegion);
706a9ac8606Spatrick }
707a9ac8606Spatrick // TODO: We need to emit some note here probably!!
708a9ac8606Spatrick }));
709a9ac8606Spatrick
710a9ac8606Spatrick return true;
711a9ac8606Spatrick }
712a9ac8606Spatrick
handleGet(const CallEvent & Call,CheckerContext & C) const713a9ac8606Spatrick void SmartPtrModeling::handleGet(const CallEvent &Call,
714a9ac8606Spatrick CheckerContext &C) const {
715ec727ea7Spatrick ProgramStateRef State = C.getState();
716a9ac8606Spatrick const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
717a9ac8606Spatrick if (!IC)
718a9ac8606Spatrick return;
719ec727ea7Spatrick
720a9ac8606Spatrick const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
721a9ac8606Spatrick if (!ThisRegion)
722a9ac8606Spatrick return;
723a9ac8606Spatrick
724a9ac8606Spatrick SVal InnerPointerVal;
725a9ac8606Spatrick std::tie(InnerPointerVal, State) = retrieveOrConjureInnerPtrVal(
726a9ac8606Spatrick State, ThisRegion, Call.getOriginExpr(), Call.getResultType(), C);
727a9ac8606Spatrick State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
728a9ac8606Spatrick InnerPointerVal);
729a9ac8606Spatrick // TODO: Add NoteTag, for how the raw pointer got using 'get' method.
730a9ac8606Spatrick C.addTransition(State);
731ec727ea7Spatrick }
732ec727ea7Spatrick
handleAssignOp(const CallEvent & Call,CheckerContext & C) const733a9ac8606Spatrick bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
734a9ac8606Spatrick CheckerContext &C) const {
735a9ac8606Spatrick ProgramStateRef State = C.getState();
736a9ac8606Spatrick const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call);
737a9ac8606Spatrick if (!OC)
738a9ac8606Spatrick return false;
739a9ac8606Spatrick OverloadedOperatorKind OOK = OC->getOverloadedOperator();
740a9ac8606Spatrick if (OOK != OO_Equal)
741a9ac8606Spatrick return false;
742a9ac8606Spatrick const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion();
743a9ac8606Spatrick if (!ThisRegion)
744a9ac8606Spatrick return false;
745a9ac8606Spatrick
746*12c85518Srobert QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
747*12c85518Srobert
748a9ac8606Spatrick const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion();
749a9ac8606Spatrick // In case of 'nullptr' or '0' assigned
750a9ac8606Spatrick if (!OtherSmartPtrRegion) {
751a9ac8606Spatrick bool AssignedNull = Call.getArgSVal(0).isZeroConstant();
752a9ac8606Spatrick if (!AssignedNull)
753a9ac8606Spatrick return false;
754*12c85518Srobert auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
755a9ac8606Spatrick State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
756a9ac8606Spatrick C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
757a9ac8606Spatrick llvm::raw_ostream &OS) {
758a9ac8606Spatrick if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
759a9ac8606Spatrick !BR.isInteresting(ThisRegion))
760a9ac8606Spatrick return;
761a9ac8606Spatrick OS << "Smart pointer";
762a9ac8606Spatrick checkAndPrettyPrintRegion(OS, ThisRegion);
763a9ac8606Spatrick OS << " is assigned to null";
764a9ac8606Spatrick }));
765a9ac8606Spatrick return true;
766a9ac8606Spatrick }
767a9ac8606Spatrick
768*12c85518Srobert return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call);
769a9ac8606Spatrick }
770a9ac8606Spatrick
handleMoveCtr(const CallEvent & Call,CheckerContext & C,const MemRegion * ThisRegion) const771a9ac8606Spatrick bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C,
772a9ac8606Spatrick const MemRegion *ThisRegion) const {
773a9ac8606Spatrick const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion();
774a9ac8606Spatrick if (!OtherSmartPtrRegion)
775a9ac8606Spatrick return false;
776a9ac8606Spatrick
777*12c85518Srobert return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call);
778a9ac8606Spatrick }
779a9ac8606Spatrick
updateMovedSmartPointers(CheckerContext & C,const MemRegion * ThisRegion,const MemRegion * OtherSmartPtrRegion,const CallEvent & Call) const780a9ac8606Spatrick bool SmartPtrModeling::updateMovedSmartPointers(
781a9ac8606Spatrick CheckerContext &C, const MemRegion *ThisRegion,
782*12c85518Srobert const MemRegion *OtherSmartPtrRegion, const CallEvent &Call) const {
783a9ac8606Spatrick ProgramStateRef State = C.getState();
784*12c85518Srobert QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
785a9ac8606Spatrick const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion);
786a9ac8606Spatrick if (OtherInnerPtr) {
787a9ac8606Spatrick State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
788*12c85518Srobert
789*12c85518Srobert auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
790a9ac8606Spatrick State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
791a9ac8606Spatrick bool IsArgValNull = OtherInnerPtr->isZeroConstant();
792a9ac8606Spatrick
793a9ac8606Spatrick C.addTransition(
794a9ac8606Spatrick State,
795a9ac8606Spatrick C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull](
796a9ac8606Spatrick PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
797a9ac8606Spatrick if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
798a9ac8606Spatrick return;
799a9ac8606Spatrick if (BR.isInteresting(OtherSmartPtrRegion)) {
800a9ac8606Spatrick OS << "Smart pointer";
801a9ac8606Spatrick checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
802a9ac8606Spatrick OS << " is null after being moved to";
803a9ac8606Spatrick checkAndPrettyPrintRegion(OS, ThisRegion);
804a9ac8606Spatrick }
805a9ac8606Spatrick if (BR.isInteresting(ThisRegion) && IsArgValNull) {
806a9ac8606Spatrick OS << "A null pointer value is moved to";
807a9ac8606Spatrick checkAndPrettyPrintRegion(OS, ThisRegion);
808a9ac8606Spatrick BR.markInteresting(OtherSmartPtrRegion);
809a9ac8606Spatrick }
810a9ac8606Spatrick }));
811a9ac8606Spatrick return true;
812a9ac8606Spatrick } else {
813a9ac8606Spatrick // In case we dont know anything about value we are moving from
814a9ac8606Spatrick // remove the entry from map for which smart pointer got moved to.
815*12c85518Srobert // For unique_ptr<A>, Ty will be 'A*'.
816*12c85518Srobert auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
817a9ac8606Spatrick State = State->remove<TrackedRegionMap>(ThisRegion);
818a9ac8606Spatrick State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
819a9ac8606Spatrick C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion,
820a9ac8606Spatrick ThisRegion](PathSensitiveBugReport &BR,
821a9ac8606Spatrick llvm::raw_ostream &OS) {
822a9ac8606Spatrick if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
823a9ac8606Spatrick !BR.isInteresting(OtherSmartPtrRegion))
824a9ac8606Spatrick return;
825a9ac8606Spatrick OS << "Smart pointer";
826a9ac8606Spatrick checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
827a9ac8606Spatrick OS << " is null after; previous value moved to";
828a9ac8606Spatrick checkAndPrettyPrintRegion(OS, ThisRegion);
829a9ac8606Spatrick }));
830a9ac8606Spatrick return true;
831a9ac8606Spatrick }
832a9ac8606Spatrick return false;
833a9ac8606Spatrick }
834a9ac8606Spatrick
handleBoolConversion(const CallEvent & Call,CheckerContext & C) const835a9ac8606Spatrick void SmartPtrModeling::handleBoolConversion(const CallEvent &Call,
836a9ac8606Spatrick CheckerContext &C) const {
837a9ac8606Spatrick // To model unique_ptr::operator bool
838a9ac8606Spatrick ProgramStateRef State = C.getState();
839a9ac8606Spatrick const Expr *CallExpr = Call.getOriginExpr();
840a9ac8606Spatrick const MemRegion *ThisRegion =
841a9ac8606Spatrick cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
842a9ac8606Spatrick
843*12c85518Srobert QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
844*12c85518Srobert
845a9ac8606Spatrick SVal InnerPointerVal;
846a9ac8606Spatrick if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
847a9ac8606Spatrick InnerPointerVal = *InnerValPtr;
848a9ac8606Spatrick } else {
849a9ac8606Spatrick // In case of inner pointer SVal is not available we create
850a9ac8606Spatrick // conjureSymbolVal for inner pointer value.
851a9ac8606Spatrick auto InnerPointerType = getInnerPointerType(Call, C);
852a9ac8606Spatrick if (InnerPointerType.isNull())
853a9ac8606Spatrick return;
854a9ac8606Spatrick
855a9ac8606Spatrick const LocationContext *LC = C.getLocationContext();
856a9ac8606Spatrick InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
857a9ac8606Spatrick CallExpr, LC, InnerPointerType, C.blockCount());
858a9ac8606Spatrick State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
859a9ac8606Spatrick }
860a9ac8606Spatrick
861a9ac8606Spatrick if (State->isNull(InnerPointerVal).isConstrainedTrue()) {
862a9ac8606Spatrick State = State->BindExpr(CallExpr, C.getLocationContext(),
863a9ac8606Spatrick C.getSValBuilder().makeTruthVal(false));
864a9ac8606Spatrick
865a9ac8606Spatrick C.addTransition(State);
866a9ac8606Spatrick return;
867a9ac8606Spatrick } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) {
868a9ac8606Spatrick State = State->BindExpr(CallExpr, C.getLocationContext(),
869a9ac8606Spatrick C.getSValBuilder().makeTruthVal(true));
870a9ac8606Spatrick
871a9ac8606Spatrick C.addTransition(State);
872a9ac8606Spatrick return;
873a9ac8606Spatrick } else if (move::isMovedFrom(State, ThisRegion)) {
874a9ac8606Spatrick C.addTransition(
875a9ac8606Spatrick State->BindExpr(CallExpr, C.getLocationContext(),
876a9ac8606Spatrick C.getSValBuilder().makeZeroVal(Call.getResultType())));
877a9ac8606Spatrick return;
878a9ac8606Spatrick } else {
879a9ac8606Spatrick ProgramStateRef NotNullState, NullState;
880a9ac8606Spatrick std::tie(NotNullState, NullState) =
881a9ac8606Spatrick State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>());
882a9ac8606Spatrick
883*12c85518Srobert auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
884a9ac8606Spatrick // Explicitly tracking the region as null.
885a9ac8606Spatrick NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal);
886a9ac8606Spatrick
887a9ac8606Spatrick NullState = NullState->BindExpr(CallExpr, C.getLocationContext(),
888a9ac8606Spatrick C.getSValBuilder().makeTruthVal(false));
889a9ac8606Spatrick C.addTransition(NullState, C.getNoteTag(
890a9ac8606Spatrick [ThisRegion](PathSensitiveBugReport &BR,
891a9ac8606Spatrick llvm::raw_ostream &OS) {
892a9ac8606Spatrick OS << "Assuming smart pointer";
893a9ac8606Spatrick checkAndPrettyPrintRegion(OS, ThisRegion);
894a9ac8606Spatrick OS << " is null";
895a9ac8606Spatrick },
896a9ac8606Spatrick /*IsPrunable=*/true));
897a9ac8606Spatrick NotNullState =
898a9ac8606Spatrick NotNullState->BindExpr(CallExpr, C.getLocationContext(),
899a9ac8606Spatrick C.getSValBuilder().makeTruthVal(true));
900a9ac8606Spatrick C.addTransition(
901a9ac8606Spatrick NotNullState,
902a9ac8606Spatrick C.getNoteTag(
903a9ac8606Spatrick [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
904a9ac8606Spatrick OS << "Assuming smart pointer";
905a9ac8606Spatrick checkAndPrettyPrintRegion(OS, ThisRegion);
906a9ac8606Spatrick OS << " is non-null";
907a9ac8606Spatrick },
908a9ac8606Spatrick /*IsPrunable=*/true));
909a9ac8606Spatrick return;
910a9ac8606Spatrick }
911ec727ea7Spatrick }
912ec727ea7Spatrick
registerSmartPtrModeling(CheckerManager & Mgr)913ec727ea7Spatrick void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
914ec727ea7Spatrick auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
915ec727ea7Spatrick Checker->ModelSmartPtrDereference =
916ec727ea7Spatrick Mgr.getAnalyzerOptions().getCheckerBooleanOption(
917ec727ea7Spatrick Checker, "ModelSmartPtrDereference");
918ec727ea7Spatrick }
919ec727ea7Spatrick
shouldRegisterSmartPtrModeling(const CheckerManager & mgr)920ec727ea7Spatrick bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
921ec727ea7Spatrick const LangOptions &LO = mgr.getLangOpts();
922e5dd7070Spatrick return LO.CPlusPlus;
923e5dd7070Spatrick }
924