1 // SmartPtrModeling.cpp - Model behavior of C++ smart pointers - C++ ------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file defines a checker that models various aspects of
10 // C++ smart pointer behavior.
12 //===----------------------------------------------------------------------===//
17 #include "clang/AST/DeclCXX.h"
18 #include "clang/AST/ExprCXX.h"
19 #include "clang/AST/Type.h"
20 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
21 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
22 #include "clang/StaticAnalyzer/Core/Checker.h"
23 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
24 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
25 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
27 #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
29 using namespace clang;
33 class SmartPtrModeling : public Checker<eval::Call, check::DeadSymbols> {
35 bool isNullAfterMoveMethod(const CallEvent &Call) const;
38 // Whether the checker should model for null dereferences of smart pointers.
39 DefaultBool ModelSmartPtrDereference;
40 bool evalCall(const CallEvent &Call, CheckerContext &C) const;
41 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
42 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
45 ProgramStateRef updateTrackedRegion(const CallEvent &Call, CheckerContext &C,
46 const MemRegion *ThisValRegion) const;
47 void handleReset(const CallEvent &Call, CheckerContext &C) const;
48 void handleRelease(const CallEvent &Call, CheckerContext &C) const;
49 void handleSwap(const CallEvent &Call, CheckerContext &C) const;
51 using SmartPtrMethodHandlerFn =
52 void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
53 CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
54 {{"reset"}, &SmartPtrModeling::handleReset},
55 {{"release"}, &SmartPtrModeling::handleRelease},
56 {{"swap", 1}, &SmartPtrModeling::handleSwap}};
58 } // end of anonymous namespace
60 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
62 // Define the inter-checker API.
66 bool isStdSmartPtrCall(const CallEvent &Call) {
67 const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
68 if (!MethodDecl || !MethodDecl->getParent())
71 const auto *RecordDecl = MethodDecl->getParent();
72 if (!RecordDecl || !RecordDecl->getDeclContext()->isStdNamespace())
75 if (RecordDecl->getDeclName().isIdentifier()) {
76 StringRef Name = RecordDecl->getName();
77 return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr";
82 bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
83 const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
84 return InnerPointVal && InnerPointVal->isZeroConstant();
86 } // namespace smartptr
90 bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const {
91 // TODO: Update CallDescription to support anonymous calls?
92 // TODO: Handle other methods, such as .get() or .release().
93 // But once we do, we'd need a visitor to explain null dereferences
94 // that are found via such modeling.
95 const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl());
96 return CD && CD->getConversionType()->isBooleanType();
99 bool SmartPtrModeling::evalCall(const CallEvent &Call,
100 CheckerContext &C) const {
102 if (!smartptr::isStdSmartPtrCall(Call))
105 if (isNullAfterMoveMethod(Call)) {
106 ProgramStateRef State = C.getState();
107 const MemRegion *ThisR =
108 cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
110 if (!move::isMovedFrom(State, ThisR)) {
111 // TODO: Model this case as well. At least, avoid invalidation of globals.
115 // TODO: Add a note to bug reports describing this decision.
117 State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
118 C.getSValBuilder().makeZeroVal(Call.getResultType())));
122 if (!ModelSmartPtrDereference)
125 if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
126 if (CC->getDecl()->isCopyOrMoveConstructor())
129 const MemRegion *ThisValRegion = CC->getCXXThisVal().getAsRegion();
133 auto State = updateTrackedRegion(Call, C, ThisValRegion);
134 C.addTransition(State);
138 const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
141 (this->**Handler)(Call, C);
143 return C.isDifferent();
146 void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
147 CheckerContext &C) const {
148 ProgramStateRef State = C.getState();
149 // Clean up dead regions from the region map.
150 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
151 for (auto E : TrackedRegions) {
152 const MemRegion *Region = E.first;
153 bool IsRegDead = !SymReaper.isLiveRegion(Region);
156 State = State->remove<TrackedRegionMap>(Region);
158 C.addTransition(State);
161 void SmartPtrModeling::handleReset(const CallEvent &Call,
162 CheckerContext &C) const {
163 const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
167 const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion();
170 auto State = updateTrackedRegion(Call, C, ThisValRegion);
171 C.addTransition(State);
172 // TODO: Make sure to ivalidate the the region in the Store if we don't have
173 // time to model all methods.
176 void SmartPtrModeling::handleRelease(const CallEvent &Call,
177 CheckerContext &C) const {
178 const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
182 const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion();
186 auto State = updateTrackedRegion(Call, C, ThisValRegion);
188 const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisValRegion);
190 State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
193 C.addTransition(State);
194 // TODO: Add support to enable MallocChecker to start tracking the raw
198 void SmartPtrModeling::handleSwap(const CallEvent &Call,
199 CheckerContext &C) const {
200 // TODO: Add support to handle swap method.
204 SmartPtrModeling::updateTrackedRegion(const CallEvent &Call, CheckerContext &C,
205 const MemRegion *ThisValRegion) const {
206 // TODO: Refactor and clean up handling too many things.
207 ProgramStateRef State = C.getState();
208 auto NumArgs = Call.getNumArgs();
211 auto NullSVal = C.getSValBuilder().makeNull();
212 State = State->set<TrackedRegionMap>(ThisValRegion, NullSVal);
213 } else if (NumArgs == 1) {
214 auto ArgVal = Call.getArgSVal(0);
215 assert(Call.getArgExpr(0)->getType()->isPointerType() &&
216 "Adding a non pointer value to TrackedRegionMap");
217 State = State->set<TrackedRegionMap>(ThisValRegion, ArgVal);
223 void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
224 auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
225 Checker->ModelSmartPtrDereference =
226 Mgr.getAnalyzerOptions().getCheckerBooleanOption(
227 Checker, "ModelSmartPtrDereference");
230 bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
231 const LangOptions &LO = mgr.getLangOpts();