1 // SmartPtrModeling.cpp - Model behavior of C++ smart pointers - C++ ------===// 2 // 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 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines a checker that models various aspects of 10 // C++ smart pointer behavior. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "Move.h" 15 #include "SmartPtr.h" 16 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" 28 29 using namespace clang; 30 using namespace ento; 31 32 namespace { 33 class SmartPtrModeling : public Checker<eval::Call, check::DeadSymbols> { 34 35 bool isNullAfterMoveMethod(const CallEvent &Call) const; 36 37 public: 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; 43 44 private: 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; 50 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}}; 57 }; 58 } // end of anonymous namespace 59 60 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal) 61 62 // Define the inter-checker API. 63 namespace clang { 64 namespace ento { 65 namespace smartptr { 66 bool isStdSmartPtrCall(const CallEvent &Call) { 67 const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); 68 if (!MethodDecl || !MethodDecl->getParent()) 69 return false; 70 71 const auto *RecordDecl = MethodDecl->getParent(); 72 if (!RecordDecl || !RecordDecl->getDeclContext()->isStdNamespace()) 73 return false; 74 75 if (RecordDecl->getDeclName().isIdentifier()) { 76 StringRef Name = RecordDecl->getName(); 77 return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr"; 78 } 79 return false; 80 } 81 82 bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) { 83 const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); 84 return InnerPointVal && InnerPointVal->isZeroConstant(); 85 } 86 } // namespace smartptr 87 } // namespace ento 88 } // namespace clang 89 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(); 97 } 98 99 bool SmartPtrModeling::evalCall(const CallEvent &Call, 100 CheckerContext &C) const { 101 102 if (!smartptr::isStdSmartPtrCall(Call)) 103 return false; 104 105 if (isNullAfterMoveMethod(Call)) { 106 ProgramStateRef State = C.getState(); 107 const MemRegion *ThisR = 108 cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); 109 110 if (!move::isMovedFrom(State, ThisR)) { 111 // TODO: Model this case as well. At least, avoid invalidation of globals. 112 return false; 113 } 114 115 // TODO: Add a note to bug reports describing this decision. 116 C.addTransition( 117 State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), 118 C.getSValBuilder().makeZeroVal(Call.getResultType()))); 119 return true; 120 } 121 122 if (!ModelSmartPtrDereference) 123 return false; 124 125 if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { 126 if (CC->getDecl()->isCopyOrMoveConstructor()) 127 return false; 128 129 const MemRegion *ThisValRegion = CC->getCXXThisVal().getAsRegion(); 130 if (!ThisValRegion) 131 return false; 132 133 auto State = updateTrackedRegion(Call, C, ThisValRegion); 134 C.addTransition(State); 135 return true; 136 } 137 138 const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call); 139 if (!Handler) 140 return false; 141 (this->**Handler)(Call, C); 142 143 return C.isDifferent(); 144 } 145 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); 154 155 if (IsRegDead) 156 State = State->remove<TrackedRegionMap>(Region); 157 } 158 C.addTransition(State); 159 } 160 161 void SmartPtrModeling::handleReset(const CallEvent &Call, 162 CheckerContext &C) const { 163 const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 164 if (!IC) 165 return; 166 167 const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion(); 168 if (!ThisValRegion) 169 return; 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. 174 } 175 176 void SmartPtrModeling::handleRelease(const CallEvent &Call, 177 CheckerContext &C) const { 178 const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 179 if (!IC) 180 return; 181 182 const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion(); 183 if (!ThisValRegion) 184 return; 185 186 auto State = updateTrackedRegion(Call, C, ThisValRegion); 187 188 const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisValRegion); 189 if (InnerPointVal) { 190 State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), 191 *InnerPointVal); 192 } 193 C.addTransition(State); 194 // TODO: Add support to enable MallocChecker to start tracking the raw 195 // pointer. 196 } 197 198 void SmartPtrModeling::handleSwap(const CallEvent &Call, 199 CheckerContext &C) const { 200 // TODO: Add support to handle swap method. 201 } 202 203 ProgramStateRef 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(); 209 210 if (NumArgs == 0) { 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); 218 } 219 220 return State; 221 } 222 223 void ento::registerSmartPtrModeling(CheckerManager &Mgr) { 224 auto *Checker = Mgr.registerChecker<SmartPtrModeling>(); 225 Checker->ModelSmartPtrDereference = 226 Mgr.getAnalyzerOptions().getCheckerBooleanOption( 227 Checker, "ModelSmartPtrDereference"); 228 } 229 230 bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) { 231 const LangOptions &LO = mgr.getLangOpts(); 232 return LO.CPlusPlus; 233 } 234