summaryrefslogtreecommitdiffstats
path: root/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp')
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp119
1 files changed, 119 insertions, 0 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
new file mode 100644
index 00000000000..6fe3cb63cde
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
@@ -0,0 +1,119 @@
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace clang;
+using namespace ento;
+
+class PlacementNewChecker : public Checker<check::PreStmt<CXXNewExpr>> {
+public:
+ void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const;
+
+private:
+ // Returns the size of the target in a placement new expression.
+ // E.g. in "new (&s) long" it returns the size of `long`.
+ SVal getExtentSizeOfNewTarget(const CXXNewExpr *NE, ProgramStateRef State,
+ CheckerContext &C) const;
+ // Returns the size of the place in a placement new expression.
+ // E.g. in "new (&s) long" it returns the size of `s`.
+ SVal getExtentSizeOfPlace(const Expr *NE, ProgramStateRef State,
+ CheckerContext &C) const;
+ BugType BT{this, "Insufficient storage for placement new",
+ categories::MemoryError};
+};
+
+SVal PlacementNewChecker::getExtentSizeOfPlace(const Expr *Place,
+ ProgramStateRef State,
+ CheckerContext &C) const {
+ const MemRegion *MRegion = C.getSVal(Place).getAsRegion();
+ if (!MRegion)
+ return UnknownVal();
+ RegionOffset Offset = MRegion->getAsOffset();
+ if (Offset.hasSymbolicOffset())
+ return UnknownVal();
+ const MemRegion *BaseRegion = MRegion->getBaseRegion();
+ if (!BaseRegion)
+ return UnknownVal();
+
+ SValBuilder &SvalBuilder = C.getSValBuilder();
+ NonLoc OffsetInBytes = SvalBuilder.makeArrayIndex(
+ Offset.getOffset() / C.getASTContext().getCharWidth());
+ DefinedOrUnknownSVal ExtentInBytes =
+ BaseRegion->castAs<SubRegion>()->getExtent(SvalBuilder);
+
+ return SvalBuilder.evalBinOp(State, BinaryOperator::Opcode::BO_Sub,
+ ExtentInBytes, OffsetInBytes,
+ SvalBuilder.getArrayIndexType());
+}
+
+SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE,
+ ProgramStateRef State,
+ CheckerContext &C) const {
+ SValBuilder &SvalBuilder = C.getSValBuilder();
+ QualType ElementType = NE->getAllocatedType();
+ ASTContext &AstContext = C.getASTContext();
+ CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType);
+ if (NE->isArray()) {
+ const Expr *SizeExpr = *NE->getArraySize();
+ SVal ElementCount = C.getSVal(SizeExpr);
+ if (auto ElementCountNL = ElementCount.getAs<NonLoc>()) {
+ // size in Bytes = ElementCountNL * TypeSize
+ return SvalBuilder.evalBinOp(
+ State, BO_Mul, *ElementCountNL,
+ SvalBuilder.makeArrayIndex(TypeSize.getQuantity()),
+ SvalBuilder.getArrayIndexType());
+ }
+ } else {
+ // Create a concrete int whose size in bits and signedness is equal to
+ // ArrayIndexType.
+ llvm::APInt I(AstContext.getTypeSizeInChars(SvalBuilder.getArrayIndexType())
+ .getQuantity() *
+ C.getASTContext().getCharWidth(),
+ TypeSize.getQuantity());
+ return SvalBuilder.makeArrayIndex(I.getZExtValue());
+ }
+ return UnknownVal();
+}
+
+void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE,
+ CheckerContext &C) const {
+ // Check only the default placement new.
+ if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator())
+ return;
+ if (NE->getNumPlacementArgs() == 0)
+ return;
+
+ ProgramStateRef State = C.getState();
+ SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, State, C);
+ const Expr *Place = NE->getPlacementArg(0);
+ SVal SizeOfPlace = getExtentSizeOfPlace(Place, State, C);
+ const auto SizeOfTargetCI = SizeOfTarget.getAs<nonloc::ConcreteInt>();
+ if (!SizeOfTargetCI)
+ return;
+ const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>();
+ if (!SizeOfPlaceCI)
+ return;
+
+ if (SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) {
+ if (ExplodedNode *N = C.generateErrorNode(State)) {
+ std::string Msg =
+ llvm::formatv("Storage provided to placement new is only {0} bytes, "
+ "whereas the allocated type requires {1} bytes",
+ SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue());
+
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
+ bugreporter::trackExpressionValue(N, Place, *R);
+ C.emitReport(std::move(R));
+ return;
+ }
+ }
+}
+
+void ento::registerPlacementNewChecker(CheckerManager &mgr) {
+ mgr.registerChecker<PlacementNewChecker>();
+}
+
+bool ento::shouldRegisterPlacementNewChecker(const LangOptions &LO) {
+ return true;
+}
OpenPOWER on IntegriCloud