PointerArithChecker.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. //=== PointerArithChecker.cpp - Pointer arithmetic checker -----*- 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 files defines PointerArithChecker, a builtin checker that checks for
  10. // pointer arithmetic on locations other than array elements.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  14. #include "clang/AST/DeclCXX.h"
  15. #include "clang/AST/ExprCXX.h"
  16. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  17. #include "clang/StaticAnalyzer/Core/Checker.h"
  18. #include "clang/StaticAnalyzer/Core/CheckerManager.h"
  19. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  20. using namespace clang;
  21. using namespace ento;
  22. namespace {
  23. enum class AllocKind {
  24. SingleObject,
  25. Array,
  26. Unknown,
  27. Reinterpreted // Single object interpreted as an array.
  28. };
  29. } // end namespace
  30. namespace llvm {
  31. template <> struct FoldingSetTrait<AllocKind> {
  32. static inline void Profile(AllocKind X, FoldingSetNodeID &ID) {
  33. ID.AddInteger(static_cast<int>(X));
  34. }
  35. };
  36. } // end namespace llvm
  37. namespace {
  38. class PointerArithChecker
  39. : public Checker<
  40. check::PreStmt<BinaryOperator>, check::PreStmt<UnaryOperator>,
  41. check::PreStmt<ArraySubscriptExpr>, check::PreStmt<CastExpr>,
  42. check::PostStmt<CastExpr>, check::PostStmt<CXXNewExpr>,
  43. check::PostStmt<CallExpr>, check::DeadSymbols> {
  44. AllocKind getKindOfNewOp(const CXXNewExpr *NE, const FunctionDecl *FD) const;
  45. const MemRegion *getArrayRegion(const MemRegion *Region, bool &Polymorphic,
  46. AllocKind &AKind, CheckerContext &C) const;
  47. const MemRegion *getPointedRegion(const MemRegion *Region,
  48. CheckerContext &C) const;
  49. void reportPointerArithMisuse(const Expr *E, CheckerContext &C,
  50. bool PointedNeeded = false) const;
  51. void initAllocIdentifiers(ASTContext &C) const;
  52. mutable std::unique_ptr<BuiltinBug> BT_pointerArith;
  53. mutable std::unique_ptr<BuiltinBug> BT_polyArray;
  54. mutable llvm::SmallSet<IdentifierInfo *, 8> AllocFunctions;
  55. public:
  56. void checkPreStmt(const UnaryOperator *UOp, CheckerContext &C) const;
  57. void checkPreStmt(const BinaryOperator *BOp, CheckerContext &C) const;
  58. void checkPreStmt(const ArraySubscriptExpr *SubExpr, CheckerContext &C) const;
  59. void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
  60. void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
  61. void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
  62. void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
  63. void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
  64. };
  65. } // end namespace
  66. REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, const MemRegion *, AllocKind)
  67. void PointerArithChecker::checkDeadSymbols(SymbolReaper &SR,
  68. CheckerContext &C) const {
  69. // TODO: intentional leak. Some information is garbage collected too early,
  70. // see http://reviews.llvm.org/D14203 for further information.
  71. /*ProgramStateRef State = C.getState();
  72. RegionStateTy RegionStates = State->get<RegionState>();
  73. for (RegionStateTy::iterator I = RegionStates.begin(), E = RegionStates.end();
  74. I != E; ++I) {
  75. if (!SR.isLiveRegion(I->first))
  76. State = State->remove<RegionState>(I->first);
  77. }
  78. C.addTransition(State);*/
  79. }
  80. AllocKind PointerArithChecker::getKindOfNewOp(const CXXNewExpr *NE,
  81. const FunctionDecl *FD) const {
  82. // This checker try not to assume anything about placement and overloaded
  83. // new to avoid false positives.
  84. if (isa<CXXMethodDecl>(FD))
  85. return AllocKind::Unknown;
  86. if (FD->getNumParams() != 1 || FD->isVariadic())
  87. return AllocKind::Unknown;
  88. if (NE->isArray())
  89. return AllocKind::Array;
  90. return AllocKind::SingleObject;
  91. }
  92. const MemRegion *
  93. PointerArithChecker::getPointedRegion(const MemRegion *Region,
  94. CheckerContext &C) const {
  95. assert(Region);
  96. ProgramStateRef State = C.getState();
  97. SVal S = State->getSVal(Region);
  98. return S.getAsRegion();
  99. }
  100. /// Checks whether a region is the part of an array.
  101. /// In case there is a derived to base cast above the array element, the
  102. /// Polymorphic output value is set to true. AKind output value is set to the
  103. /// allocation kind of the inspected region.
  104. const MemRegion *PointerArithChecker::getArrayRegion(const MemRegion *Region,
  105. bool &Polymorphic,
  106. AllocKind &AKind,
  107. CheckerContext &C) const {
  108. assert(Region);
  109. while (const auto *BaseRegion = dyn_cast<CXXBaseObjectRegion>(Region)) {
  110. Region = BaseRegion->getSuperRegion();
  111. Polymorphic = true;
  112. }
  113. if (const auto *ElemRegion = dyn_cast<ElementRegion>(Region)) {
  114. Region = ElemRegion->getSuperRegion();
  115. }
  116. ProgramStateRef State = C.getState();
  117. if (const AllocKind *Kind = State->get<RegionState>(Region)) {
  118. AKind = *Kind;
  119. if (*Kind == AllocKind::Array)
  120. return Region;
  121. else
  122. return nullptr;
  123. }
  124. // When the region is symbolic and we do not have any information about it,
  125. // assume that this is an array to avoid false positives.
  126. if (isa<SymbolicRegion>(Region))
  127. return Region;
  128. // No AllocKind stored and not symbolic, assume that it points to a single
  129. // object.
  130. return nullptr;
  131. }
  132. void PointerArithChecker::reportPointerArithMisuse(const Expr *E,
  133. CheckerContext &C,
  134. bool PointedNeeded) const {
  135. SourceRange SR = E->getSourceRange();
  136. if (SR.isInvalid())
  137. return;
  138. ProgramStateRef State = C.getState();
  139. const MemRegion *Region = C.getSVal(E).getAsRegion();
  140. if (!Region)
  141. return;
  142. if (PointedNeeded)
  143. Region = getPointedRegion(Region, C);
  144. if (!Region)
  145. return;
  146. bool IsPolymorphic = false;
  147. AllocKind Kind = AllocKind::Unknown;
  148. if (const MemRegion *ArrayRegion =
  149. getArrayRegion(Region, IsPolymorphic, Kind, C)) {
  150. if (!IsPolymorphic)
  151. return;
  152. if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
  153. if (!BT_polyArray)
  154. BT_polyArray.reset(new BuiltinBug(
  155. this, "Dangerous pointer arithmetic",
  156. "Pointer arithmetic on a pointer to base class is dangerous "
  157. "because derived and base class may have different size."));
  158. auto R = std::make_unique<PathSensitiveBugReport>(
  159. *BT_polyArray, BT_polyArray->getDescription(), N);
  160. R->addRange(E->getSourceRange());
  161. R->markInteresting(ArrayRegion);
  162. C.emitReport(std::move(R));
  163. }
  164. return;
  165. }
  166. if (Kind == AllocKind::Reinterpreted)
  167. return;
  168. // We might not have enough information about symbolic regions.
  169. if (Kind != AllocKind::SingleObject &&
  170. Region->getKind() == MemRegion::Kind::SymbolicRegionKind)
  171. return;
  172. if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
  173. if (!BT_pointerArith)
  174. BT_pointerArith.reset(new BuiltinBug(this, "Dangerous pointer arithmetic",
  175. "Pointer arithmetic on non-array "
  176. "variables relies on memory layout, "
  177. "which is dangerous."));
  178. auto R = std::make_unique<PathSensitiveBugReport>(
  179. *BT_pointerArith, BT_pointerArith->getDescription(), N);
  180. R->addRange(SR);
  181. R->markInteresting(Region);
  182. C.emitReport(std::move(R));
  183. }
  184. }
  185. void PointerArithChecker::initAllocIdentifiers(ASTContext &C) const {
  186. if (!AllocFunctions.empty())
  187. return;
  188. AllocFunctions.insert(&C.Idents.get("alloca"));
  189. AllocFunctions.insert(&C.Idents.get("malloc"));
  190. AllocFunctions.insert(&C.Idents.get("realloc"));
  191. AllocFunctions.insert(&C.Idents.get("calloc"));
  192. AllocFunctions.insert(&C.Idents.get("valloc"));
  193. }
  194. void PointerArithChecker::checkPostStmt(const CallExpr *CE,
  195. CheckerContext &C) const {
  196. ProgramStateRef State = C.getState();
  197. const FunctionDecl *FD = C.getCalleeDecl(CE);
  198. if (!FD)
  199. return;
  200. IdentifierInfo *FunI = FD->getIdentifier();
  201. initAllocIdentifiers(C.getASTContext());
  202. if (AllocFunctions.count(FunI) == 0)
  203. return;
  204. SVal SV = C.getSVal(CE);
  205. const MemRegion *Region = SV.getAsRegion();
  206. if (!Region)
  207. return;
  208. // Assume that C allocation functions allocate arrays to avoid false
  209. // positives.
  210. // TODO: Add heuristics to distinguish alloc calls that allocates single
  211. // objecs.
  212. State = State->set<RegionState>(Region, AllocKind::Array);
  213. C.addTransition(State);
  214. }
  215. void PointerArithChecker::checkPostStmt(const CXXNewExpr *NE,
  216. CheckerContext &C) const {
  217. const FunctionDecl *FD = NE->getOperatorNew();
  218. if (!FD)
  219. return;
  220. AllocKind Kind = getKindOfNewOp(NE, FD);
  221. ProgramStateRef State = C.getState();
  222. SVal AllocedVal = C.getSVal(NE);
  223. const MemRegion *Region = AllocedVal.getAsRegion();
  224. if (!Region)
  225. return;
  226. State = State->set<RegionState>(Region, Kind);
  227. C.addTransition(State);
  228. }
  229. void PointerArithChecker::checkPostStmt(const CastExpr *CE,
  230. CheckerContext &C) const {
  231. if (CE->getCastKind() != CastKind::CK_BitCast)
  232. return;
  233. const Expr *CastedExpr = CE->getSubExpr();
  234. ProgramStateRef State = C.getState();
  235. SVal CastedVal = C.getSVal(CastedExpr);
  236. const MemRegion *Region = CastedVal.getAsRegion();
  237. if (!Region)
  238. return;
  239. // Suppress reinterpret casted hits.
  240. State = State->set<RegionState>(Region, AllocKind::Reinterpreted);
  241. C.addTransition(State);
  242. }
  243. void PointerArithChecker::checkPreStmt(const CastExpr *CE,
  244. CheckerContext &C) const {
  245. if (CE->getCastKind() != CastKind::CK_ArrayToPointerDecay)
  246. return;
  247. const Expr *CastedExpr = CE->getSubExpr();
  248. ProgramStateRef State = C.getState();
  249. SVal CastedVal = C.getSVal(CastedExpr);
  250. const MemRegion *Region = CastedVal.getAsRegion();
  251. if (!Region)
  252. return;
  253. if (const AllocKind *Kind = State->get<RegionState>(Region)) {
  254. if (*Kind == AllocKind::Array || *Kind == AllocKind::Reinterpreted)
  255. return;
  256. }
  257. State = State->set<RegionState>(Region, AllocKind::Array);
  258. C.addTransition(State);
  259. }
  260. void PointerArithChecker::checkPreStmt(const UnaryOperator *UOp,
  261. CheckerContext &C) const {
  262. if (!UOp->isIncrementDecrementOp() || !UOp->getType()->isPointerType())
  263. return;
  264. reportPointerArithMisuse(UOp->getSubExpr(), C, true);
  265. }
  266. void PointerArithChecker::checkPreStmt(const ArraySubscriptExpr *SubsExpr,
  267. CheckerContext &C) const {
  268. SVal Idx = C.getSVal(SubsExpr->getIdx());
  269. // Indexing with 0 is OK.
  270. if (Idx.isZeroConstant())
  271. return;
  272. // Indexing vector-type expressions is also OK.
  273. if (SubsExpr->getBase()->getType()->isVectorType())
  274. return;
  275. reportPointerArithMisuse(SubsExpr->getBase(), C);
  276. }
  277. void PointerArithChecker::checkPreStmt(const BinaryOperator *BOp,
  278. CheckerContext &C) const {
  279. BinaryOperatorKind OpKind = BOp->getOpcode();
  280. if (!BOp->isAdditiveOp() && OpKind != BO_AddAssign && OpKind != BO_SubAssign)
  281. return;
  282. const Expr *Lhs = BOp->getLHS();
  283. const Expr *Rhs = BOp->getRHS();
  284. ProgramStateRef State = C.getState();
  285. if (Rhs->getType()->isIntegerType() && Lhs->getType()->isPointerType()) {
  286. SVal RHSVal = C.getSVal(Rhs);
  287. if (State->isNull(RHSVal).isConstrainedTrue())
  288. return;
  289. reportPointerArithMisuse(Lhs, C, !BOp->isAdditiveOp());
  290. }
  291. // The int += ptr; case is not valid C++.
  292. if (Lhs->getType()->isIntegerType() && Rhs->getType()->isPointerType()) {
  293. SVal LHSVal = C.getSVal(Lhs);
  294. if (State->isNull(LHSVal).isConstrainedTrue())
  295. return;
  296. reportPointerArithMisuse(Rhs, C);
  297. }
  298. }
  299. void ento::registerPointerArithChecker(CheckerManager &mgr) {
  300. mgr.registerChecker<PointerArithChecker>();
  301. }
  302. bool ento::shouldRegisterPointerArithChecker(const CheckerManager &mgr) {
  303. return true;
  304. }