PointerArithChecker.cpp 12 KB

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