SmartPtrModeling.cpp 35 KB

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