InvalidPtrChecker.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. //== InvalidPtrChecker.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 InvalidPtrChecker which finds usages of possibly
  10. // invalidated pointer.
  11. // CERT SEI Rules ENV31-C and ENV34-C
  12. // For more information see:
  13. // https://wiki.sei.cmu.edu/confluence/x/8tYxBQ
  14. // https://wiki.sei.cmu.edu/confluence/x/5NUxBQ
  15. //===----------------------------------------------------------------------===//
  16. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  17. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  18. #include "clang/StaticAnalyzer/Core/Checker.h"
  19. #include "clang/StaticAnalyzer/Core/CheckerManager.h"
  20. #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
  21. #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
  22. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  23. using namespace clang;
  24. using namespace ento;
  25. namespace {
  26. class InvalidPtrChecker
  27. : public Checker<check::Location, check::BeginFunction, check::PostCall> {
  28. private:
  29. BugType BT{this, "Use of invalidated pointer", categories::MemoryError};
  30. void EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const;
  31. using HandlerFn = void (InvalidPtrChecker::*)(const CallEvent &Call,
  32. CheckerContext &C) const;
  33. // SEI CERT ENV31-C
  34. const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = {
  35. {{"setenv", 3}, &InvalidPtrChecker::EnvpInvalidatingCall},
  36. {{"unsetenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
  37. {{"putenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
  38. {{"_putenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
  39. {{"_wputenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
  40. };
  41. void postPreviousReturnInvalidatingCall(const CallEvent &Call,
  42. CheckerContext &C) const;
  43. // SEI CERT ENV34-C
  44. const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = {
  45. {{"getenv", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
  46. {{"setlocale", 2},
  47. &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
  48. {{"strerror", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
  49. {{"localeconv", 0},
  50. &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
  51. {{"asctime", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
  52. };
  53. public:
  54. // Obtain the environment pointer from 'main()' (if present).
  55. void checkBeginFunction(CheckerContext &C) const;
  56. // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
  57. // pointer from 'main()'
  58. // Handle functions in PreviousCallInvalidatingFunctions.
  59. // Also, check if invalidated region is passed to a
  60. // conservatively evaluated function call as an argument.
  61. void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
  62. // Check if invalidated region is being dereferenced.
  63. void checkLocation(SVal l, bool isLoad, const Stmt *S,
  64. CheckerContext &C) const;
  65. };
  66. } // namespace
  67. // Set of memory regions that were invalidated
  68. REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *)
  69. // Stores the region of the environment pointer of 'main' (if present).
  70. // Note: This pointer has type 'const MemRegion *', however the trait is only
  71. // specialized to 'const void*' and 'void*'
  72. REGISTER_TRAIT_WITH_PROGRAMSTATE(EnvPtrRegion, const void *)
  73. // Stores key-value pairs, where key is function declaration and value is
  74. // pointer to memory region returned by previous call of this function
  75. REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *,
  76. const MemRegion *)
  77. void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call,
  78. CheckerContext &C) const {
  79. StringRef FunctionName = Call.getCalleeIdentifier()->getName();
  80. ProgramStateRef State = C.getState();
  81. const auto *Reg = State->get<EnvPtrRegion>();
  82. if (!Reg)
  83. return;
  84. const auto *SymbolicEnvPtrRegion =
  85. reinterpret_cast<const MemRegion *>(const_cast<const void *>(Reg));
  86. State = State->add<InvalidMemoryRegions>(SymbolicEnvPtrRegion);
  87. const NoteTag *Note =
  88. C.getNoteTag([SymbolicEnvPtrRegion, FunctionName](
  89. PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
  90. if (!BR.isInteresting(SymbolicEnvPtrRegion))
  91. return;
  92. Out << '\'' << FunctionName
  93. << "' call may invalidate the environment parameter of 'main'";
  94. });
  95. C.addTransition(State, Note);
  96. }
  97. void InvalidPtrChecker::postPreviousReturnInvalidatingCall(
  98. const CallEvent &Call, CheckerContext &C) const {
  99. ProgramStateRef State = C.getState();
  100. const NoteTag *Note = nullptr;
  101. const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
  102. // Invalidate the region of the previously returned pointer - if there was
  103. // one.
  104. if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) {
  105. const MemRegion *PrevReg = *Reg;
  106. State = State->add<InvalidMemoryRegions>(PrevReg);
  107. Note = C.getNoteTag([PrevReg, FD](PathSensitiveBugReport &BR,
  108. llvm::raw_ostream &Out) {
  109. if (!BR.isInteresting(PrevReg))
  110. return;
  111. Out << '\'';
  112. FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
  113. Out << "' call may invalidate the the result of the previous " << '\'';
  114. FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
  115. Out << '\'';
  116. });
  117. }
  118. const LocationContext *LCtx = C.getLocationContext();
  119. const auto *CE = cast<CallExpr>(Call.getOriginExpr());
  120. // Function call will return a pointer to the new symbolic region.
  121. DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal(
  122. CE, LCtx, CE->getType(), C.blockCount());
  123. State = State->BindExpr(CE, LCtx, RetVal);
  124. // Remember to this region.
  125. const auto *SymRegOfRetVal = cast<SymbolicRegion>(RetVal.getAsRegion());
  126. const MemRegion *MR =
  127. const_cast<MemRegion *>(SymRegOfRetVal->getBaseRegion());
  128. State = State->set<PreviousCallResultMap>(FD, MR);
  129. ExplodedNode *Node = C.addTransition(State, Note);
  130. const NoteTag *PreviousCallNote =
  131. C.getNoteTag([MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
  132. if (!BR.isInteresting(MR))
  133. return;
  134. Out << '\'' << "'previous function call was here" << '\'';
  135. });
  136. C.addTransition(State, Node, PreviousCallNote);
  137. }
  138. // TODO: This seems really ugly. Simplify this.
  139. static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State,
  140. const MemRegion *Reg) {
  141. while (Reg) {
  142. if (State->contains<InvalidMemoryRegions>(Reg))
  143. return Reg;
  144. const auto *SymBase = Reg->getSymbolicBase();
  145. if (!SymBase)
  146. break;
  147. const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol());
  148. if (!SRV)
  149. break;
  150. Reg = SRV->getRegion();
  151. if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion()))
  152. Reg = VarReg;
  153. }
  154. return nullptr;
  155. }
  156. // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
  157. // pointer from 'main()' Also, check if invalidated region is passed to a
  158. // function call as an argument.
  159. void InvalidPtrChecker::checkPostCall(const CallEvent &Call,
  160. CheckerContext &C) const {
  161. // Check if function invalidates 'envp' argument of 'main'
  162. if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call))
  163. (this->**Handler)(Call, C);
  164. // Check if function invalidates the result of previous call
  165. if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call))
  166. (this->**Handler)(Call, C);
  167. // Check if one of the arguments of the function call is invalidated
  168. // If call was inlined, don't report invalidated argument
  169. if (C.wasInlined)
  170. return;
  171. ProgramStateRef State = C.getState();
  172. for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) {
  173. if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(
  174. Call.getArgSVal(I).getAsRegion())) {
  175. if (const MemRegion *InvalidatedSymbolicBase =
  176. findInvalidatedSymbolicBase(State, SR)) {
  177. ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
  178. if (!ErrorNode)
  179. return;
  180. SmallString<256> Msg;
  181. llvm::raw_svector_ostream Out(Msg);
  182. Out << "use of invalidated pointer '";
  183. Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr,
  184. C.getASTContext().getPrintingPolicy());
  185. Out << "' in a function call";
  186. auto Report =
  187. std::make_unique<PathSensitiveBugReport>(BT, Out.str(), ErrorNode);
  188. Report->markInteresting(InvalidatedSymbolicBase);
  189. Report->addRange(Call.getArgSourceRange(I));
  190. C.emitReport(std::move(Report));
  191. }
  192. }
  193. }
  194. }
  195. // Obtain the environment pointer from 'main()', if present.
  196. void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const {
  197. if (!C.inTopFrame())
  198. return;
  199. const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl());
  200. if (!FD || FD->param_size() != 3 || !FD->isMain())
  201. return;
  202. ProgramStateRef State = C.getState();
  203. const MemRegion *EnvpReg =
  204. State->getRegion(FD->parameters()[2], C.getLocationContext());
  205. // Save the memory region pointed by the environment pointer parameter of
  206. // 'main'.
  207. State = State->set<EnvPtrRegion>(
  208. reinterpret_cast<void *>(const_cast<MemRegion *>(EnvpReg)));
  209. C.addTransition(State);
  210. }
  211. // Check if invalidated region is being dereferenced.
  212. void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S,
  213. CheckerContext &C) const {
  214. ProgramStateRef State = C.getState();
  215. // Ignore memory operations involving 'non-invalidated' locations.
  216. const MemRegion *InvalidatedSymbolicBase =
  217. findInvalidatedSymbolicBase(State, Loc.getAsRegion());
  218. if (!InvalidatedSymbolicBase)
  219. return;
  220. ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
  221. if (!ErrorNode)
  222. return;
  223. auto Report = std::make_unique<PathSensitiveBugReport>(
  224. BT, "dereferencing an invalid pointer", ErrorNode);
  225. Report->markInteresting(InvalidatedSymbolicBase);
  226. C.emitReport(std::move(Report));
  227. }
  228. void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
  229. Mgr.registerChecker<InvalidPtrChecker>();
  230. }
  231. bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
  232. return true;
  233. }