Coverage Report

Created: 2020-02-15 09:57

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