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 return smartptr::StdSmartPtrs.count(RecordDecl->getName().lower()); 77 } 78 return false; 79 } 80 81 bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) { 82 const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); 83 return InnerPointVal && InnerPointVal->isZeroConstant(); 84 } 85 } // namespace smartptr 86 } // namespace ento 87 } // namespace clang 88 89 bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const { 90 // TODO: Update CallDescription to support anonymous calls? 91 // TODO: Handle other methods, such as .get() or .release(). 92 // But once we do, we'd need a visitor to explain null dereferences 93 // that are found via such modeling. 94 const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl()); 95 return CD && CD->getConversionType()->isBooleanType(); 96 } 97 98 bool SmartPtrModeling::evalCall(const CallEvent &Call, 99 CheckerContext &C) const { 100 101 if (!smartptr::isStdSmartPtrCall(Call)) 102 return false; 103 104 if (isNullAfterMoveMethod(Call)) { 105 ProgramStateRef State = C.getState(); 106 const MemRegion *ThisR = 107 cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); 108 109 if (!move::isMovedFrom(State, ThisR)) { 110 // TODO: Model this case as well. At least, avoid invalidation of globals. 111 return false; 112 } 113 114 // TODO: Add a note to bug reports describing this decision. 115 C.addTransition( 116 State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), 117 C.getSValBuilder().makeZeroVal(Call.getResultType()))); 118 return true; 119 } 120 121 if (!ModelSmartPtrDereference) 122 return false; 123 124 if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { 125 if (CC->getDecl()->isCopyOrMoveConstructor()) 126 return false; 127 128 const MemRegion *ThisValRegion = CC->getCXXThisVal().getAsRegion(); 129 if (!ThisValRegion) 130 return false; 131 132 auto State = updateTrackedRegion(Call, C, ThisValRegion); 133 C.addTransition(State); 134 return true; 135 } 136 137 const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call); 138 if (!Handler) 139 return false; 140 (this->**Handler)(Call, C); 141 142 return C.isDifferent(); 143 } 144 145 void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper, 146 CheckerContext &C) const { 147 ProgramStateRef State = C.getState(); 148 // Clean up dead regions from the region map. 149 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); 150 for (auto E : TrackedRegions) { 151 const MemRegion *Region = E.first; 152 bool IsRegDead = !SymReaper.isLiveRegion(Region); 153 154 if (IsRegDead) 155 State = State->remove<TrackedRegionMap>(Region); 156 } 157 C.addTransition(State); 158 } 159 160 void SmartPtrModeling::handleReset(const CallEvent &Call, 161 CheckerContext &C) const { 162 const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 163 if (!IC) 164 return; 165 166 const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion(); 167 if (!ThisValRegion) 168 return; 169 auto State = updateTrackedRegion(Call, C, ThisValRegion); 170 C.addTransition(State); 171 // TODO: Make sure to ivalidate the the region in the Store if we don't have 172 // time to model all methods. 173 } 174 175 void SmartPtrModeling::handleRelease(const CallEvent &Call, 176 CheckerContext &C) const { 177 const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 178 if (!IC) 179 return; 180 181 const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion(); 182 if (!ThisValRegion) 183 return; 184 185 auto State = updateTrackedRegion(Call, C, ThisValRegion); 186 187 const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisValRegion); 188 if (InnerPointVal) { 189 State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), 190 *InnerPointVal); 191 } 192 C.addTransition(State); 193 // TODO: Add support to enable MallocChecker to start tracking the raw 194 // pointer. 195 } 196 197 void SmartPtrModeling::handleSwap(const CallEvent &Call, 198 CheckerContext &C) const { 199 // TODO: Add support to handle swap method. 200 } 201 202 ProgramStateRef 203 SmartPtrModeling::updateTrackedRegion(const CallEvent &Call, CheckerContext &C, 204 const MemRegion *ThisValRegion) const { 205 // TODO: Refactor and clean up handling too many things. 206 ProgramStateRef State = C.getState(); 207 auto NumArgs = Call.getNumArgs(); 208 209 if (NumArgs == 0) { 210 auto NullSVal = C.getSValBuilder().makeNull(); 211 State = State->set<TrackedRegionMap>(ThisValRegion, NullSVal); 212 } else if (NumArgs == 1) { 213 auto ArgVal = Call.getArgSVal(0); 214 assert(Call.getArgExpr(0)->getType()->isPointerType() && 215 "Adding a non pointer value to TrackedRegionMap"); 216 State = State->set<TrackedRegionMap>(ThisValRegion, ArgVal); 217 } 218 219 return State; 220 } 221 222 void ento::registerSmartPtrModeling(CheckerManager &Mgr) { 223 auto *Checker = Mgr.registerChecker<SmartPtrModeling>(); 224 Checker->ModelSmartPtrDereference = 225 Mgr.getAnalyzerOptions().getCheckerBooleanOption( 226 Checker, "ModelSmartPtrDereference"); 227 } 228 229 bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) { 230 const LangOptions &LO = mgr.getLangOpts(); 231 return LO.CPlusPlus; 232 } 233