1 //=== CastSizeChecker.cpp ---------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // CastSizeChecker checks when casting a malloc'ed symbolic region to type T,
10 // whether the size of the symbolic region is a multiple of the size of T.
12 //===----------------------------------------------------------------------===//
14 #include "clang/AST/CharUnits.h"
15 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17 #include "clang/StaticAnalyzer/Core/Checker.h"
18 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
22 using namespace clang
;
26 class CastSizeChecker
: public Checker
< check::PreStmt
<CastExpr
> > {
27 mutable std::unique_ptr
<BugType
> BT
;
30 void checkPreStmt(const CastExpr
*CE
, CheckerContext
&C
) const;
34 /// Check if we are casting to a struct with a flexible array at the end.
38 /// struct bar data[];
45 /// struct bar data[0];
48 /// In these cases it is also valid to allocate size of struct foo + a multiple
50 static bool evenFlexibleArraySize(ASTContext
&Ctx
, CharUnits RegionSize
,
51 CharUnits TypeSize
, QualType ToPointeeTy
) {
52 const RecordType
*RT
= ToPointeeTy
->getAs
<RecordType
>();
56 const RecordDecl
*RD
= RT
->getDecl();
57 RecordDecl::field_iterator
Iter(RD
->field_begin());
58 RecordDecl::field_iterator
End(RD
->field_end());
59 const FieldDecl
*Last
= nullptr;
60 for (; Iter
!= End
; ++Iter
)
62 assert(Last
&& "empty structs should already be handled");
64 const Type
*ElemType
= Last
->getType()->getArrayElementTypeNoTypeQual();
66 if (const ConstantArrayType
*ArrayTy
=
67 Ctx
.getAsConstantArrayType(Last
->getType())) {
68 FlexSize
= Ctx
.getTypeSizeInChars(ElemType
);
69 if (ArrayTy
->getSize() == 1 && TypeSize
> FlexSize
)
71 else if (ArrayTy
->getSize() != 0)
73 } else if (RD
->hasFlexibleArrayMember()) {
74 FlexSize
= Ctx
.getTypeSizeInChars(ElemType
);
79 if (FlexSize
.isZero())
82 CharUnits Left
= RegionSize
- TypeSize
;
83 if (Left
.isNegative())
86 return Left
% FlexSize
== 0;
89 void CastSizeChecker::checkPreStmt(const CastExpr
*CE
,CheckerContext
&C
) const {
90 const Expr
*E
= CE
->getSubExpr();
91 ASTContext
&Ctx
= C
.getASTContext();
92 QualType ToTy
= Ctx
.getCanonicalType(CE
->getType());
93 const PointerType
*ToPTy
= dyn_cast
<PointerType
>(ToTy
.getTypePtr());
98 QualType ToPointeeTy
= ToPTy
->getPointeeType();
100 // Only perform the check if 'ToPointeeTy' is a complete type.
101 if (ToPointeeTy
->isIncompleteType())
104 ProgramStateRef state
= C
.getState();
105 const MemRegion
*R
= C
.getSVal(E
).getAsRegion();
109 const SymbolicRegion
*SR
= dyn_cast
<SymbolicRegion
>(R
);
113 SValBuilder
&svalBuilder
= C
.getSValBuilder();
115 DefinedOrUnknownSVal Size
= getDynamicExtent(state
, SR
, svalBuilder
);
116 const llvm::APSInt
*SizeInt
= svalBuilder
.getKnownValue(state
, Size
);
120 CharUnits regionSize
= CharUnits::fromQuantity(SizeInt
->getZExtValue());
121 CharUnits typeSize
= C
.getASTContext().getTypeSizeInChars(ToPointeeTy
);
123 // Ignore void, and a few other un-sizeable types.
124 if (typeSize
.isZero())
127 if (regionSize
% typeSize
== 0)
130 if (evenFlexibleArraySize(Ctx
, regionSize
, typeSize
, ToPointeeTy
))
133 if (ExplodedNode
*errorNode
= C
.generateErrorNode()) {
135 BT
.reset(new BugType(this, "Cast region with wrong size."));
136 constexpr llvm::StringLiteral Msg
=
137 "Cast a region whose size is not a multiple of the destination type "
139 auto R
= std::make_unique
<PathSensitiveBugReport
>(*BT
, Msg
, errorNode
);
140 R
->addRange(CE
->getSourceRange());
141 C
.emitReport(std::move(R
));
145 void ento::registerCastSizeChecker(CheckerManager
&mgr
) {
146 mgr
.registerChecker
<CastSizeChecker
>();
149 bool ento::shouldRegisterCastSizeChecker(const CheckerManager
&mgr
) {
150 // PR31226: C++ is more complicated than what this checker currently supports.
151 // There are derived-to-base casts, there are different rules for 0-size
152 // structures, no flexible arrays, etc.
153 // FIXME: Disabled on C++ for now.
154 const LangOptions
&LO
= mgr
.getLangOpts();
155 return !LO
.CPlusPlus
;