CheckPlacementNew.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. //==- CheckPlacementNew.cpp - Check for placement new operation --*- 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 for misuse of the default placement new operator.
  10. //
  11. //===----------------------------------------------------------------------===//
  12. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  13. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  14. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  15. #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
  16. #include "llvm/Support/FormatVariadic.h"
  17. using namespace clang;
  18. using namespace ento;
  19. namespace {
  20. class PlacementNewChecker : public Checker<check::PreStmt<CXXNewExpr>> {
  21. public:
  22. void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const;
  23. private:
  24. bool checkPlaceCapacityIsSufficient(const CXXNewExpr *NE,
  25. CheckerContext &C) const;
  26. bool checkPlaceIsAlignedProperly(const CXXNewExpr *NE,
  27. CheckerContext &C) const;
  28. // Returns the size of the target in a placement new expression.
  29. // E.g. in "new (&s) long" it returns the size of `long`.
  30. SVal getExtentSizeOfNewTarget(const CXXNewExpr *NE, CheckerContext &C,
  31. bool &IsArray) const;
  32. // Returns the size of the place in a placement new expression.
  33. // E.g. in "new (&s) long" it returns the size of `s`.
  34. SVal getExtentSizeOfPlace(const CXXNewExpr *NE, CheckerContext &C) const;
  35. void emitBadAlignReport(const Expr *P, CheckerContext &C,
  36. unsigned AllocatedTAlign,
  37. unsigned StorageTAlign) const;
  38. unsigned getStorageAlign(CheckerContext &C, const ValueDecl *VD) const;
  39. void checkElementRegionAlign(const ElementRegion *R, CheckerContext &C,
  40. const Expr *P, unsigned AllocatedTAlign) const;
  41. void checkFieldRegionAlign(const FieldRegion *R, CheckerContext &C,
  42. const Expr *P, unsigned AllocatedTAlign) const;
  43. bool isVarRegionAlignedProperly(const VarRegion *R, CheckerContext &C,
  44. const Expr *P,
  45. unsigned AllocatedTAlign) const;
  46. BugType SBT{this, "Insufficient storage for placement new",
  47. categories::MemoryError};
  48. BugType ABT{this, "Bad align storage for placement new",
  49. categories::MemoryError};
  50. };
  51. } // namespace
  52. SVal PlacementNewChecker::getExtentSizeOfPlace(const CXXNewExpr *NE,
  53. CheckerContext &C) const {
  54. const Expr *Place = NE->getPlacementArg(0);
  55. return getDynamicExtentWithOffset(C.getState(), C.getSVal(Place));
  56. }
  57. SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE,
  58. CheckerContext &C,
  59. bool &IsArray) const {
  60. ProgramStateRef State = C.getState();
  61. SValBuilder &SvalBuilder = C.getSValBuilder();
  62. QualType ElementType = NE->getAllocatedType();
  63. ASTContext &AstContext = C.getASTContext();
  64. CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType);
  65. IsArray = false;
  66. if (NE->isArray()) {
  67. IsArray = true;
  68. const Expr *SizeExpr = *NE->getArraySize();
  69. SVal ElementCount = C.getSVal(SizeExpr);
  70. if (auto ElementCountNL = ElementCount.getAs<NonLoc>()) {
  71. // size in Bytes = ElementCountNL * TypeSize
  72. return SvalBuilder.evalBinOp(
  73. State, BO_Mul, *ElementCountNL,
  74. SvalBuilder.makeArrayIndex(TypeSize.getQuantity()),
  75. SvalBuilder.getArrayIndexType());
  76. }
  77. } else {
  78. // Create a concrete int whose size in bits and signedness is equal to
  79. // ArrayIndexType.
  80. llvm::APInt I(AstContext.getTypeSizeInChars(SvalBuilder.getArrayIndexType())
  81. .getQuantity() *
  82. C.getASTContext().getCharWidth(),
  83. TypeSize.getQuantity());
  84. return SvalBuilder.makeArrayIndex(I.getZExtValue());
  85. }
  86. return UnknownVal();
  87. }
  88. bool PlacementNewChecker::checkPlaceCapacityIsSufficient(
  89. const CXXNewExpr *NE, CheckerContext &C) const {
  90. bool IsArrayTypeAllocated;
  91. SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, C, IsArrayTypeAllocated);
  92. SVal SizeOfPlace = getExtentSizeOfPlace(NE, C);
  93. const auto SizeOfTargetCI = SizeOfTarget.getAs<nonloc::ConcreteInt>();
  94. if (!SizeOfTargetCI)
  95. return true;
  96. const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>();
  97. if (!SizeOfPlaceCI)
  98. return true;
  99. if ((SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) ||
  100. (IsArrayTypeAllocated &&
  101. SizeOfPlaceCI->getValue() >= SizeOfTargetCI->getValue())) {
  102. if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
  103. std::string Msg;
  104. // TODO: use clang constant
  105. if (IsArrayTypeAllocated &&
  106. SizeOfPlaceCI->getValue() > SizeOfTargetCI->getValue())
  107. Msg = std::string(llvm::formatv(
  108. "{0} bytes is possibly not enough for array allocation which "
  109. "requires {1} bytes. Current overhead requires the size of {2} "
  110. "bytes",
  111. SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue(),
  112. SizeOfPlaceCI->getValue() - SizeOfTargetCI->getValue()));
  113. else if (IsArrayTypeAllocated &&
  114. SizeOfPlaceCI->getValue() == SizeOfTargetCI->getValue())
  115. Msg = std::string(llvm::formatv(
  116. "Storage provided to placement new is only {0} bytes, "
  117. "whereas the allocated array type requires more space for "
  118. "internal needs",
  119. SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
  120. else
  121. Msg = std::string(llvm::formatv(
  122. "Storage provided to placement new is only {0} bytes, "
  123. "whereas the allocated type requires {1} bytes",
  124. SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
  125. auto R = std::make_unique<PathSensitiveBugReport>(SBT, Msg, N);
  126. bugreporter::trackExpressionValue(N, NE->getPlacementArg(0), *R);
  127. C.emitReport(std::move(R));
  128. return false;
  129. }
  130. }
  131. return true;
  132. }
  133. void PlacementNewChecker::emitBadAlignReport(const Expr *P, CheckerContext &C,
  134. unsigned AllocatedTAlign,
  135. unsigned StorageTAlign) const {
  136. ProgramStateRef State = C.getState();
  137. if (ExplodedNode *N = C.generateErrorNode(State)) {
  138. std::string Msg(llvm::formatv("Storage type is aligned to {0} bytes but "
  139. "allocated type is aligned to {1} bytes",
  140. StorageTAlign, AllocatedTAlign));
  141. auto R = std::make_unique<PathSensitiveBugReport>(ABT, Msg, N);
  142. bugreporter::trackExpressionValue(N, P, *R);
  143. C.emitReport(std::move(R));
  144. }
  145. }
  146. unsigned PlacementNewChecker::getStorageAlign(CheckerContext &C,
  147. const ValueDecl *VD) const {
  148. unsigned StorageTAlign = C.getASTContext().getTypeAlign(VD->getType());
  149. if (unsigned SpecifiedAlignment = VD->getMaxAlignment())
  150. StorageTAlign = SpecifiedAlignment;
  151. return StorageTAlign / C.getASTContext().getCharWidth();
  152. }
  153. void PlacementNewChecker::checkElementRegionAlign(
  154. const ElementRegion *R, CheckerContext &C, const Expr *P,
  155. unsigned AllocatedTAlign) const {
  156. auto IsBaseRegionAlignedProperly = [this, R, &C, P,
  157. AllocatedTAlign]() -> bool {
  158. // Unwind nested ElementRegion`s to get the type.
  159. const MemRegion *SuperRegion = R;
  160. while (true) {
  161. if (SuperRegion->getKind() == MemRegion::ElementRegionKind) {
  162. SuperRegion = cast<SubRegion>(SuperRegion)->getSuperRegion();
  163. continue;
  164. }
  165. break;
  166. }
  167. const DeclRegion *TheElementDeclRegion = SuperRegion->getAs<DeclRegion>();
  168. if (!TheElementDeclRegion)
  169. return false;
  170. const DeclRegion *BaseDeclRegion = R->getBaseRegion()->getAs<DeclRegion>();
  171. if (!BaseDeclRegion)
  172. return false;
  173. unsigned BaseRegionAlign = 0;
  174. // We must use alignment TheElementDeclRegion if it has its own alignment
  175. // specifier
  176. if (TheElementDeclRegion->getDecl()->getMaxAlignment())
  177. BaseRegionAlign = getStorageAlign(C, TheElementDeclRegion->getDecl());
  178. else
  179. BaseRegionAlign = getStorageAlign(C, BaseDeclRegion->getDecl());
  180. if (AllocatedTAlign > BaseRegionAlign) {
  181. emitBadAlignReport(P, C, AllocatedTAlign, BaseRegionAlign);
  182. return false;
  183. }
  184. return true;
  185. };
  186. auto CheckElementRegionOffset = [this, R, &C, P, AllocatedTAlign]() -> void {
  187. RegionOffset TheOffsetRegion = R->getAsOffset();
  188. if (TheOffsetRegion.hasSymbolicOffset())
  189. return;
  190. unsigned Offset =
  191. TheOffsetRegion.getOffset() / C.getASTContext().getCharWidth();
  192. unsigned AddressAlign = Offset % AllocatedTAlign;
  193. if (AddressAlign != 0) {
  194. emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign);
  195. return;
  196. }
  197. };
  198. if (IsBaseRegionAlignedProperly()) {
  199. CheckElementRegionOffset();
  200. }
  201. }
  202. void PlacementNewChecker::checkFieldRegionAlign(
  203. const FieldRegion *R, CheckerContext &C, const Expr *P,
  204. unsigned AllocatedTAlign) const {
  205. const MemRegion *BaseRegion = R->getBaseRegion();
  206. if (!BaseRegion)
  207. return;
  208. if (const VarRegion *TheVarRegion = BaseRegion->getAs<VarRegion>()) {
  209. if (isVarRegionAlignedProperly(TheVarRegion, C, P, AllocatedTAlign)) {
  210. // We've checked type align but, unless FieldRegion
  211. // offset is zero, we also need to check its own
  212. // align.
  213. RegionOffset Offset = R->getAsOffset();
  214. if (Offset.hasSymbolicOffset())
  215. return;
  216. int64_t OffsetValue =
  217. Offset.getOffset() / C.getASTContext().getCharWidth();
  218. unsigned AddressAlign = OffsetValue % AllocatedTAlign;
  219. if (AddressAlign != 0)
  220. emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign);
  221. }
  222. }
  223. }
  224. bool PlacementNewChecker::isVarRegionAlignedProperly(
  225. const VarRegion *R, CheckerContext &C, const Expr *P,
  226. unsigned AllocatedTAlign) const {
  227. const VarDecl *TheVarDecl = R->getDecl();
  228. unsigned StorageTAlign = getStorageAlign(C, TheVarDecl);
  229. if (AllocatedTAlign > StorageTAlign) {
  230. emitBadAlignReport(P, C, AllocatedTAlign, StorageTAlign);
  231. return false;
  232. }
  233. return true;
  234. }
  235. bool PlacementNewChecker::checkPlaceIsAlignedProperly(const CXXNewExpr *NE,
  236. CheckerContext &C) const {
  237. const Expr *Place = NE->getPlacementArg(0);
  238. QualType AllocatedT = NE->getAllocatedType();
  239. unsigned AllocatedTAlign = C.getASTContext().getTypeAlign(AllocatedT) /
  240. C.getASTContext().getCharWidth();
  241. SVal PlaceVal = C.getSVal(Place);
  242. if (const MemRegion *MRegion = PlaceVal.getAsRegion()) {
  243. if (const ElementRegion *TheElementRegion = MRegion->getAs<ElementRegion>())
  244. checkElementRegionAlign(TheElementRegion, C, Place, AllocatedTAlign);
  245. else if (const FieldRegion *TheFieldRegion = MRegion->getAs<FieldRegion>())
  246. checkFieldRegionAlign(TheFieldRegion, C, Place, AllocatedTAlign);
  247. else if (const VarRegion *TheVarRegion = MRegion->getAs<VarRegion>())
  248. isVarRegionAlignedProperly(TheVarRegion, C, Place, AllocatedTAlign);
  249. }
  250. return true;
  251. }
  252. void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE,
  253. CheckerContext &C) const {
  254. // Check only the default placement new.
  255. if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator())
  256. return;
  257. if (NE->getNumPlacementArgs() == 0)
  258. return;
  259. if (!checkPlaceCapacityIsSufficient(NE, C))
  260. return;
  261. checkPlaceIsAlignedProperly(NE, C);
  262. }
  263. void ento::registerPlacementNewChecker(CheckerManager &mgr) {
  264. mgr.registerChecker<PlacementNewChecker>();
  265. }
  266. bool ento::shouldRegisterPlacementNewChecker(const CheckerManager &mgr) {
  267. return true;
  268. }