InnerPointerChecker.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. //=== InnerPointerChecker.cpp -------------------------------------*- 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 check that marks a raw pointer to a C++ container's
  10. // inner buffer released when the object is destroyed. This information can
  11. // be used by MallocChecker to detect use-after-free problems.
  12. //
  13. //===----------------------------------------------------------------------===//
  14. #include "AllocationState.h"
  15. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  16. #include "InterCheckerAPI.h"
  17. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  18. #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
  19. #include "clang/StaticAnalyzer/Core/Checker.h"
  20. #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
  21. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  22. using namespace clang;
  23. using namespace ento;
  24. // Associate container objects with a set of raw pointer symbols.
  25. REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(PtrSet, SymbolRef)
  26. REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, PtrSet)
  27. namespace {
  28. class InnerPointerChecker
  29. : public Checker<check::DeadSymbols, check::PostCall> {
  30. CallDescription AppendFn, AssignFn, ClearFn, CStrFn, DataFn, EraseFn,
  31. InsertFn, PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn,
  32. ShrinkToFitFn, SwapFn;
  33. public:
  34. class InnerPointerBRVisitor : public BugReporterVisitor {
  35. SymbolRef PtrToBuf;
  36. public:
  37. InnerPointerBRVisitor(SymbolRef Sym) : PtrToBuf(Sym) {}
  38. static void *getTag() {
  39. static int Tag = 0;
  40. return &Tag;
  41. }
  42. void Profile(llvm::FoldingSetNodeID &ID) const override {
  43. ID.AddPointer(getTag());
  44. }
  45. virtual PathDiagnosticPieceRef
  46. VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
  47. PathSensitiveBugReport &BR) override;
  48. // FIXME: Scan the map once in the visitor's constructor and do a direct
  49. // lookup by region.
  50. bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) {
  51. RawPtrMapTy Map = State->get<RawPtrMap>();
  52. for (const auto Entry : Map) {
  53. if (Entry.second.contains(Sym))
  54. return true;
  55. }
  56. return false;
  57. }
  58. };
  59. InnerPointerChecker()
  60. : AppendFn({"std", "basic_string", "append"}),
  61. AssignFn({"std", "basic_string", "assign"}),
  62. ClearFn({"std", "basic_string", "clear"}),
  63. CStrFn({"std", "basic_string", "c_str"}),
  64. DataFn({"std", "basic_string", "data"}),
  65. EraseFn({"std", "basic_string", "erase"}),
  66. InsertFn({"std", "basic_string", "insert"}),
  67. PopBackFn({"std", "basic_string", "pop_back"}),
  68. PushBackFn({"std", "basic_string", "push_back"}),
  69. ReplaceFn({"std", "basic_string", "replace"}),
  70. ReserveFn({"std", "basic_string", "reserve"}),
  71. ResizeFn({"std", "basic_string", "resize"}),
  72. ShrinkToFitFn({"std", "basic_string", "shrink_to_fit"}),
  73. SwapFn({"std", "basic_string", "swap"}) {}
  74. /// Check whether the called member function potentially invalidates
  75. /// pointers referring to the container object's inner buffer.
  76. bool isInvalidatingMemberFunction(const CallEvent &Call) const;
  77. /// Mark pointer symbols associated with the given memory region released
  78. /// in the program state.
  79. void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State,
  80. const MemRegion *ObjRegion,
  81. CheckerContext &C) const;
  82. /// Standard library functions that take a non-const `basic_string` argument by
  83. /// reference may invalidate its inner pointers. Check for these cases and
  84. /// mark the pointers released.
  85. void checkFunctionArguments(const CallEvent &Call, ProgramStateRef State,
  86. CheckerContext &C) const;
  87. /// Record the connection between raw pointers referring to a container
  88. /// object's inner buffer and the object's memory region in the program state.
  89. /// Mark potentially invalidated pointers released.
  90. void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
  91. /// Clean up the program state map.
  92. void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
  93. };
  94. } // end anonymous namespace
  95. bool InnerPointerChecker::isInvalidatingMemberFunction(
  96. const CallEvent &Call) const {
  97. if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) {
  98. OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator();
  99. if (Opc == OO_Equal || Opc == OO_PlusEqual)
  100. return true;
  101. return false;
  102. }
  103. return (isa<CXXDestructorCall>(Call) || Call.isCalled(AppendFn) ||
  104. Call.isCalled(AssignFn) || Call.isCalled(ClearFn) ||
  105. Call.isCalled(EraseFn) || Call.isCalled(InsertFn) ||
  106. Call.isCalled(PopBackFn) || Call.isCalled(PushBackFn) ||
  107. Call.isCalled(ReplaceFn) || Call.isCalled(ReserveFn) ||
  108. Call.isCalled(ResizeFn) || Call.isCalled(ShrinkToFitFn) ||
  109. Call.isCalled(SwapFn));
  110. }
  111. void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call,
  112. ProgramStateRef State,
  113. const MemRegion *MR,
  114. CheckerContext &C) const {
  115. if (const PtrSet *PS = State->get<RawPtrMap>(MR)) {
  116. const Expr *Origin = Call.getOriginExpr();
  117. for (const auto Symbol : *PS) {
  118. // NOTE: `Origin` may be null, and will be stored so in the symbol's
  119. // `RefState` in MallocChecker's `RegionState` program state map.
  120. State = allocation_state::markReleased(State, Symbol, Origin);
  121. }
  122. State = State->remove<RawPtrMap>(MR);
  123. C.addTransition(State);
  124. return;
  125. }
  126. }
  127. void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call,
  128. ProgramStateRef State,
  129. CheckerContext &C) const {
  130. if (const auto *FC = dyn_cast<AnyFunctionCall>(&Call)) {
  131. const FunctionDecl *FD = FC->getDecl();
  132. if (!FD || !FD->isInStdNamespace())
  133. return;
  134. for (unsigned I = 0, E = FD->getNumParams(); I != E; ++I) {
  135. QualType ParamTy = FD->getParamDecl(I)->getType();
  136. if (!ParamTy->isReferenceType() ||
  137. ParamTy->getPointeeType().isConstQualified())
  138. continue;
  139. // In case of member operator calls, `this` is counted as an
  140. // argument but not as a parameter.
  141. bool isaMemberOpCall = isa<CXXMemberOperatorCall>(FC);
  142. unsigned ArgI = isaMemberOpCall ? I+1 : I;
  143. SVal Arg = FC->getArgSVal(ArgI);
  144. const auto *ArgRegion =
  145. dyn_cast_or_null<TypedValueRegion>(Arg.getAsRegion());
  146. if (!ArgRegion)
  147. continue;
  148. markPtrSymbolsReleased(Call, State, ArgRegion, C);
  149. }
  150. }
  151. }
  152. // [string.require]
  153. //
  154. // "References, pointers, and iterators referring to the elements of a
  155. // basic_string sequence may be invalidated by the following uses of that
  156. // basic_string object:
  157. //
  158. // -- As an argument to any standard library function taking a reference
  159. // to non-const basic_string as an argument. For example, as an argument to
  160. // non-member functions swap(), operator>>(), and getline(), or as an argument
  161. // to basic_string::swap().
  162. //
  163. // -- Calling non-const member functions, except operator[], at, front, back,
  164. // begin, rbegin, end, and rend."
  165. void InnerPointerChecker::checkPostCall(const CallEvent &Call,
  166. CheckerContext &C) const {
  167. ProgramStateRef State = C.getState();
  168. if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) {
  169. // TODO: Do we need these to be typed?
  170. const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>(
  171. ICall->getCXXThisVal().getAsRegion());
  172. if (!ObjRegion)
  173. return;
  174. if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) {
  175. SVal RawPtr = Call.getReturnValue();
  176. if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) {
  177. // Start tracking this raw pointer by adding it to the set of symbols
  178. // associated with this container object in the program state map.
  179. PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();
  180. const PtrSet *SetPtr = State->get<RawPtrMap>(ObjRegion);
  181. PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet();
  182. assert(C.wasInlined || !Set.contains(Sym));
  183. Set = F.add(Set, Sym);
  184. State = State->set<RawPtrMap>(ObjRegion, Set);
  185. C.addTransition(State);
  186. }
  187. return;
  188. }
  189. // Check [string.require] / second point.
  190. if (isInvalidatingMemberFunction(Call)) {
  191. markPtrSymbolsReleased(Call, State, ObjRegion, C);
  192. return;
  193. }
  194. }
  195. // Check [string.require] / first point.
  196. checkFunctionArguments(Call, State, C);
  197. }
  198. void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper,
  199. CheckerContext &C) const {
  200. ProgramStateRef State = C.getState();
  201. PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();
  202. RawPtrMapTy RPM = State->get<RawPtrMap>();
  203. for (const auto Entry : RPM) {
  204. if (!SymReaper.isLiveRegion(Entry.first)) {
  205. // Due to incomplete destructor support, some dead regions might
  206. // remain in the program state map. Clean them up.
  207. State = State->remove<RawPtrMap>(Entry.first);
  208. }
  209. if (const PtrSet *OldSet = State->get<RawPtrMap>(Entry.first)) {
  210. PtrSet CleanedUpSet = *OldSet;
  211. for (const auto Symbol : Entry.second) {
  212. if (!SymReaper.isLive(Symbol))
  213. CleanedUpSet = F.remove(CleanedUpSet, Symbol);
  214. }
  215. State = CleanedUpSet.isEmpty()
  216. ? State->remove<RawPtrMap>(Entry.first)
  217. : State->set<RawPtrMap>(Entry.first, CleanedUpSet);
  218. }
  219. }
  220. C.addTransition(State);
  221. }
  222. namespace clang {
  223. namespace ento {
  224. namespace allocation_state {
  225. std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym) {
  226. return std::make_unique<InnerPointerChecker::InnerPointerBRVisitor>(Sym);
  227. }
  228. const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym) {
  229. RawPtrMapTy Map = State->get<RawPtrMap>();
  230. for (const auto Entry : Map) {
  231. if (Entry.second.contains(Sym)) {
  232. return Entry.first;
  233. }
  234. }
  235. return nullptr;
  236. }
  237. } // end namespace allocation_state
  238. } // end namespace ento
  239. } // end namespace clang
  240. PathDiagnosticPieceRef InnerPointerChecker::InnerPointerBRVisitor::VisitNode(
  241. const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
  242. if (!isSymbolTracked(N->getState(), PtrToBuf) ||
  243. isSymbolTracked(N->getFirstPred()->getState(), PtrToBuf))
  244. return nullptr;
  245. const Stmt *S = PathDiagnosticLocation::getStmt(N);
  246. if (!S)
  247. return nullptr;
  248. const MemRegion *ObjRegion =
  249. allocation_state::getContainerObjRegion(N->getState(), PtrToBuf);
  250. const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion);
  251. QualType ObjTy = TypedRegion->getValueType();
  252. SmallString<256> Buf;
  253. llvm::raw_svector_ostream OS(Buf);
  254. OS << "Pointer to inner buffer of '" << ObjTy.getAsString()
  255. << "' obtained here";
  256. PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
  257. N->getLocationContext());
  258. return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
  259. }
  260. void ento::registerInnerPointerChecker(CheckerManager &Mgr) {
  261. registerInnerPointerCheckerAux(Mgr);
  262. Mgr.registerChecker<InnerPointerChecker>();
  263. }
  264. bool ento::shouldRegisterInnerPointerChecker(const LangOptions &LO) {
  265. return true;
  266. }