/
CheckPlacementNew.cpp
134 lines (120 loc) · 5.31 KB
/
CheckPlacementNew.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//==- CheckPlacementNew.cpp - Check for placement new operation --*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines a check for misuse of the default placement new operator.
//
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
#include "llvm/Support/FormatVariadic.h"
using namespace clang;
using namespace ento;
namespace {
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};
};
} // namespace
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 =
getDynamicSize(State, BaseRegion, 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 = std::string(
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 CheckerManager &mgr) {
return true;
}