xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp (revision bda3dd0d986b33c3a327c0ee0eb8ba43aa140699)
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 
13 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
15 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
16 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
17 #include "llvm/Support/FormatVariadic.h"
18 
19 using namespace clang;
20 using namespace ento;
21 
22 namespace {
23 class PlacementNewChecker : public Checker<check::PreStmt<CXXNewExpr>> {
24 public:
25   void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const;
26 
27 private:
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, ProgramStateRef State,
31                                 CheckerContext &C) 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 Expr *NE, ProgramStateRef State,
35                             CheckerContext &C) const;
36   BugType BT{this, "Insufficient storage for placement new",
37              categories::MemoryError};
38 };
39 } // namespace
40 
41 SVal PlacementNewChecker::getExtentSizeOfPlace(const Expr *Place,
42                                                ProgramStateRef State,
43                                                CheckerContext &C) const {
44   const MemRegion *MRegion = C.getSVal(Place).getAsRegion();
45   if (!MRegion)
46     return UnknownVal();
47   RegionOffset Offset = MRegion->getAsOffset();
48   if (Offset.hasSymbolicOffset())
49     return UnknownVal();
50   const MemRegion *BaseRegion = MRegion->getBaseRegion();
51   if (!BaseRegion)
52     return UnknownVal();
53 
54   SValBuilder &SvalBuilder = C.getSValBuilder();
55   NonLoc OffsetInBytes = SvalBuilder.makeArrayIndex(
56       Offset.getOffset() / C.getASTContext().getCharWidth());
57   DefinedOrUnknownSVal ExtentInBytes =
58       getDynamicSize(State, BaseRegion, SvalBuilder);
59 
60   return SvalBuilder.evalBinOp(State, BinaryOperator::Opcode::BO_Sub,
61                                ExtentInBytes, OffsetInBytes,
62                                SvalBuilder.getArrayIndexType());
63 }
64 
65 SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE,
66                                                    ProgramStateRef State,
67                                                    CheckerContext &C) const {
68   SValBuilder &SvalBuilder = C.getSValBuilder();
69   QualType ElementType = NE->getAllocatedType();
70   ASTContext &AstContext = C.getASTContext();
71   CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType);
72   if (NE->isArray()) {
73     const Expr *SizeExpr = *NE->getArraySize();
74     SVal ElementCount = C.getSVal(SizeExpr);
75     if (auto ElementCountNL = ElementCount.getAs<NonLoc>()) {
76       // size in Bytes = ElementCountNL * TypeSize
77       return SvalBuilder.evalBinOp(
78           State, BO_Mul, *ElementCountNL,
79           SvalBuilder.makeArrayIndex(TypeSize.getQuantity()),
80           SvalBuilder.getArrayIndexType());
81     }
82   } else {
83     // Create a concrete int whose size in bits and signedness is equal to
84     // ArrayIndexType.
85     llvm::APInt I(AstContext.getTypeSizeInChars(SvalBuilder.getArrayIndexType())
86                           .getQuantity() *
87                       C.getASTContext().getCharWidth(),
88                   TypeSize.getQuantity());
89     return SvalBuilder.makeArrayIndex(I.getZExtValue());
90   }
91   return UnknownVal();
92 }
93 
94 void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE,
95                                        CheckerContext &C) const {
96   // Check only the default placement new.
97   if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator())
98     return;
99   if (NE->getNumPlacementArgs() == 0)
100     return;
101 
102   ProgramStateRef State = C.getState();
103   SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, State, C);
104   const Expr *Place = NE->getPlacementArg(0);
105   SVal SizeOfPlace = getExtentSizeOfPlace(Place, State, C);
106   const auto SizeOfTargetCI = SizeOfTarget.getAs<nonloc::ConcreteInt>();
107   if (!SizeOfTargetCI)
108     return;
109   const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>();
110   if (!SizeOfPlaceCI)
111     return;
112 
113   if (SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) {
114     if (ExplodedNode *N = C.generateErrorNode(State)) {
115       std::string Msg = std::string(
116           llvm::formatv("Storage provided to placement new is only {0} bytes, "
117                         "whereas the allocated type requires {1} bytes",
118                         SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
119 
120       auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
121       bugreporter::trackExpressionValue(N, Place, *R);
122       C.emitReport(std::move(R));
123       return;
124     }
125   }
126 }
127 
128 void ento::registerPlacementNewChecker(CheckerManager &mgr) {
129   mgr.registerChecker<PlacementNewChecker>();
130 }
131 
132 bool ento::shouldRegisterPlacementNewChecker(const CheckerManager &mgr) {
133   return true;
134 }
135