Coverage Report

Created: 2023-09-12 09:32

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