1 //=== PointerArithChecker.cpp - Pointer arithmetic checker -----*- 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 // This files defines PointerArithChecker, a builtin checker that checks for
10 // pointer arithmetic on locations other than array elements.
12 //===----------------------------------------------------------------------===//
14 #include "clang/AST/DeclCXX.h"
15 #include "clang/AST/ExprCXX.h"
16 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
17 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
18 #include "clang/StaticAnalyzer/Core/Checker.h"
19 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21 #include "llvm/ADT/StringRef.h"
23 using namespace clang
;
27 enum class AllocKind
{
31 Reinterpreted
// Single object interpreted as an array.
36 template <> struct FoldingSetTrait
<AllocKind
> {
37 static inline void Profile(AllocKind X
, FoldingSetNodeID
&ID
) {
38 ID
.AddInteger(static_cast<int>(X
));
41 } // end namespace llvm
44 class PointerArithChecker
46 check::PreStmt
<BinaryOperator
>, check::PreStmt
<UnaryOperator
>,
47 check::PreStmt
<ArraySubscriptExpr
>, check::PreStmt
<CastExpr
>,
48 check::PostStmt
<CastExpr
>, check::PostStmt
<CXXNewExpr
>,
49 check::PostStmt
<CallExpr
>, check::DeadSymbols
> {
50 AllocKind
getKindOfNewOp(const CXXNewExpr
*NE
, const FunctionDecl
*FD
) const;
51 const MemRegion
*getArrayRegion(const MemRegion
*Region
, bool &Polymorphic
,
52 AllocKind
&AKind
, CheckerContext
&C
) const;
53 const MemRegion
*getPointedRegion(const MemRegion
*Region
,
54 CheckerContext
&C
) const;
55 void reportPointerArithMisuse(const Expr
*E
, CheckerContext
&C
,
56 bool PointedNeeded
= false) const;
57 void initAllocIdentifiers(ASTContext
&C
) const;
59 mutable std::unique_ptr
<BugType
> BT_pointerArith
;
60 mutable std::unique_ptr
<BugType
> BT_polyArray
;
61 mutable llvm::SmallSet
<IdentifierInfo
*, 8> AllocFunctions
;
64 void checkPreStmt(const UnaryOperator
*UOp
, CheckerContext
&C
) const;
65 void checkPreStmt(const BinaryOperator
*BOp
, CheckerContext
&C
) const;
66 void checkPreStmt(const ArraySubscriptExpr
*SubExpr
, CheckerContext
&C
) const;
67 void checkPreStmt(const CastExpr
*CE
, CheckerContext
&C
) const;
68 void checkPostStmt(const CastExpr
*CE
, CheckerContext
&C
) const;
69 void checkPostStmt(const CXXNewExpr
*NE
, CheckerContext
&C
) const;
70 void checkPostStmt(const CallExpr
*CE
, CheckerContext
&C
) const;
71 void checkDeadSymbols(SymbolReaper
&SR
, CheckerContext
&C
) const;
75 REGISTER_MAP_WITH_PROGRAMSTATE(RegionState
, const MemRegion
*, AllocKind
)
77 void PointerArithChecker::checkDeadSymbols(SymbolReaper
&SR
,
78 CheckerContext
&C
) const {
79 // TODO: intentional leak. Some information is garbage collected too early,
80 // see http://reviews.llvm.org/D14203 for further information.
81 /*ProgramStateRef State = C.getState();
82 RegionStateTy RegionStates = State->get<RegionState>();
83 for (const MemRegion *Reg: llvm::make_first_range(RegionStates)) {
84 if (!SR.isLiveRegion(Reg))
85 State = State->remove<RegionState>(Reg);
87 C.addTransition(State);*/
90 AllocKind
PointerArithChecker::getKindOfNewOp(const CXXNewExpr
*NE
,
91 const FunctionDecl
*FD
) const {
92 // This checker try not to assume anything about placement and overloaded
93 // new to avoid false positives.
94 if (isa
<CXXMethodDecl
>(FD
))
95 return AllocKind::Unknown
;
96 if (FD
->getNumParams() != 1 || FD
->isVariadic())
97 return AllocKind::Unknown
;
99 return AllocKind::Array
;
101 return AllocKind::SingleObject
;
105 PointerArithChecker::getPointedRegion(const MemRegion
*Region
,
106 CheckerContext
&C
) const {
108 ProgramStateRef State
= C
.getState();
109 SVal S
= State
->getSVal(Region
);
110 return S
.getAsRegion();
113 /// Checks whether a region is the part of an array.
114 /// In case there is a derived to base cast above the array element, the
115 /// Polymorphic output value is set to true. AKind output value is set to the
116 /// allocation kind of the inspected region.
117 const MemRegion
*PointerArithChecker::getArrayRegion(const MemRegion
*Region
,
120 CheckerContext
&C
) const {
122 while (const auto *BaseRegion
= dyn_cast
<CXXBaseObjectRegion
>(Region
)) {
123 Region
= BaseRegion
->getSuperRegion();
126 if (const auto *ElemRegion
= dyn_cast
<ElementRegion
>(Region
)) {
127 Region
= ElemRegion
->getSuperRegion();
130 ProgramStateRef State
= C
.getState();
131 if (const AllocKind
*Kind
= State
->get
<RegionState
>(Region
)) {
133 if (*Kind
== AllocKind::Array
)
138 // When the region is symbolic and we do not have any information about it,
139 // assume that this is an array to avoid false positives.
140 if (isa
<SymbolicRegion
>(Region
))
143 // No AllocKind stored and not symbolic, assume that it points to a single
148 void PointerArithChecker::reportPointerArithMisuse(const Expr
*E
,
150 bool PointedNeeded
) const {
151 SourceRange SR
= E
->getSourceRange();
155 ProgramStateRef State
= C
.getState();
156 const MemRegion
*Region
= C
.getSVal(E
).getAsRegion();
160 Region
= getPointedRegion(Region
, C
);
164 bool IsPolymorphic
= false;
165 AllocKind Kind
= AllocKind::Unknown
;
166 if (const MemRegion
*ArrayRegion
=
167 getArrayRegion(Region
, IsPolymorphic
, Kind
, C
)) {
170 if (ExplodedNode
*N
= C
.generateNonFatalErrorNode()) {
172 BT_polyArray
.reset(new BugType(this, "Dangerous pointer arithmetic"));
173 constexpr llvm::StringLiteral Msg
=
174 "Pointer arithmetic on a pointer to base class is dangerous "
175 "because derived and base class may have different size.";
176 auto R
= std::make_unique
<PathSensitiveBugReport
>(*BT_polyArray
, Msg
, N
);
177 R
->addRange(E
->getSourceRange());
178 R
->markInteresting(ArrayRegion
);
179 C
.emitReport(std::move(R
));
184 if (Kind
== AllocKind::Reinterpreted
)
187 // We might not have enough information about symbolic regions.
188 if (Kind
!= AllocKind::SingleObject
&&
189 Region
->getKind() == MemRegion::Kind::SymbolicRegionKind
)
192 if (ExplodedNode
*N
= C
.generateNonFatalErrorNode()) {
193 if (!BT_pointerArith
)
194 BT_pointerArith
.reset(new BugType(this, "Dangerous pointer arithmetic"));
195 constexpr llvm::StringLiteral Msg
=
196 "Pointer arithmetic on non-array variables relies on memory layout, "
197 "which is dangerous.";
198 auto R
= std::make_unique
<PathSensitiveBugReport
>(*BT_pointerArith
, Msg
, N
);
200 R
->markInteresting(Region
);
201 C
.emitReport(std::move(R
));
205 void PointerArithChecker::initAllocIdentifiers(ASTContext
&C
) const {
206 if (!AllocFunctions
.empty())
208 AllocFunctions
.insert(&C
.Idents
.get("alloca"));
209 AllocFunctions
.insert(&C
.Idents
.get("malloc"));
210 AllocFunctions
.insert(&C
.Idents
.get("realloc"));
211 AllocFunctions
.insert(&C
.Idents
.get("calloc"));
212 AllocFunctions
.insert(&C
.Idents
.get("valloc"));
215 void PointerArithChecker::checkPostStmt(const CallExpr
*CE
,
216 CheckerContext
&C
) const {
217 ProgramStateRef State
= C
.getState();
218 const FunctionDecl
*FD
= C
.getCalleeDecl(CE
);
221 IdentifierInfo
*FunI
= FD
->getIdentifier();
222 initAllocIdentifiers(C
.getASTContext());
223 if (AllocFunctions
.count(FunI
) == 0)
226 SVal SV
= C
.getSVal(CE
);
227 const MemRegion
*Region
= SV
.getAsRegion();
230 // Assume that C allocation functions allocate arrays to avoid false
232 // TODO: Add heuristics to distinguish alloc calls that allocates single
234 State
= State
->set
<RegionState
>(Region
, AllocKind::Array
);
235 C
.addTransition(State
);
238 void PointerArithChecker::checkPostStmt(const CXXNewExpr
*NE
,
239 CheckerContext
&C
) const {
240 const FunctionDecl
*FD
= NE
->getOperatorNew();
244 AllocKind Kind
= getKindOfNewOp(NE
, FD
);
246 ProgramStateRef State
= C
.getState();
247 SVal AllocedVal
= C
.getSVal(NE
);
248 const MemRegion
*Region
= AllocedVal
.getAsRegion();
251 State
= State
->set
<RegionState
>(Region
, Kind
);
252 C
.addTransition(State
);
255 void PointerArithChecker::checkPostStmt(const CastExpr
*CE
,
256 CheckerContext
&C
) const {
257 if (CE
->getCastKind() != CastKind::CK_BitCast
)
260 const Expr
*CastedExpr
= CE
->getSubExpr();
261 ProgramStateRef State
= C
.getState();
262 SVal CastedVal
= C
.getSVal(CastedExpr
);
264 const MemRegion
*Region
= CastedVal
.getAsRegion();
268 // Suppress reinterpret casted hits.
269 State
= State
->set
<RegionState
>(Region
, AllocKind::Reinterpreted
);
270 C
.addTransition(State
);
273 void PointerArithChecker::checkPreStmt(const CastExpr
*CE
,
274 CheckerContext
&C
) const {
275 if (CE
->getCastKind() != CastKind::CK_ArrayToPointerDecay
)
278 const Expr
*CastedExpr
= CE
->getSubExpr();
279 ProgramStateRef State
= C
.getState();
280 SVal CastedVal
= C
.getSVal(CastedExpr
);
282 const MemRegion
*Region
= CastedVal
.getAsRegion();
286 if (const AllocKind
*Kind
= State
->get
<RegionState
>(Region
)) {
287 if (*Kind
== AllocKind::Array
|| *Kind
== AllocKind::Reinterpreted
)
290 State
= State
->set
<RegionState
>(Region
, AllocKind::Array
);
291 C
.addTransition(State
);
294 void PointerArithChecker::checkPreStmt(const UnaryOperator
*UOp
,
295 CheckerContext
&C
) const {
296 if (!UOp
->isIncrementDecrementOp() || !UOp
->getType()->isPointerType())
298 reportPointerArithMisuse(UOp
->getSubExpr(), C
, true);
301 void PointerArithChecker::checkPreStmt(const ArraySubscriptExpr
*SubsExpr
,
302 CheckerContext
&C
) const {
303 SVal Idx
= C
.getSVal(SubsExpr
->getIdx());
305 // Indexing with 0 is OK.
306 if (Idx
.isZeroConstant())
309 // Indexing vector-type expressions is also OK.
310 if (SubsExpr
->getBase()->getType()->isVectorType())
312 reportPointerArithMisuse(SubsExpr
->getBase(), C
);
315 void PointerArithChecker::checkPreStmt(const BinaryOperator
*BOp
,
316 CheckerContext
&C
) const {
317 BinaryOperatorKind OpKind
= BOp
->getOpcode();
318 if (!BOp
->isAdditiveOp() && OpKind
!= BO_AddAssign
&& OpKind
!= BO_SubAssign
)
321 const Expr
*Lhs
= BOp
->getLHS();
322 const Expr
*Rhs
= BOp
->getRHS();
323 ProgramStateRef State
= C
.getState();
325 if (Rhs
->getType()->isIntegerType() && Lhs
->getType()->isPointerType()) {
326 SVal RHSVal
= C
.getSVal(Rhs
);
327 if (State
->isNull(RHSVal
).isConstrainedTrue())
329 reportPointerArithMisuse(Lhs
, C
, !BOp
->isAdditiveOp());
331 // The int += ptr; case is not valid C++.
332 if (Lhs
->getType()->isIntegerType() && Rhs
->getType()->isPointerType()) {
333 SVal LHSVal
= C
.getSVal(Lhs
);
334 if (State
->isNull(LHSVal
).isConstrainedTrue())
336 reportPointerArithMisuse(Rhs
, C
);
340 void ento::registerPointerArithChecker(CheckerManager
&mgr
) {
341 mgr
.registerChecker
<PointerArithChecker
>();
344 bool ento::shouldRegisterPointerArithChecker(const CheckerManager
&mgr
) {