[DFAJumpThreading] Remove incoming StartBlock from all phis when unfolding select...
[llvm-project.git] / clang / lib / StaticAnalyzer / Checkers / PointerArithChecker.cpp
blob1d63c0dd01f3d6a10ef4edadc67b85bdfecb7ba3
1 //=== PointerArithChecker.cpp - Pointer arithmetic checker -----*- 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 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;
24 using namespace ento;
26 namespace {
27 enum class AllocKind {
28 SingleObject,
29 Array,
30 Unknown,
31 Reinterpreted // Single object interpreted as an array.
33 } // end namespace
35 namespace llvm {
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
43 namespace {
44 class PointerArithChecker
45 : public Checker<
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;
63 public:
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;
73 } // end namespace
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;
98 if (NE->isArray())
99 return AllocKind::Array;
101 return AllocKind::SingleObject;
104 const MemRegion *
105 PointerArithChecker::getPointedRegion(const MemRegion *Region,
106 CheckerContext &C) const {
107 assert(Region);
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,
118 bool &Polymorphic,
119 AllocKind &AKind,
120 CheckerContext &C) const {
121 assert(Region);
122 while (const auto *BaseRegion = dyn_cast<CXXBaseObjectRegion>(Region)) {
123 Region = BaseRegion->getSuperRegion();
124 Polymorphic = true;
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)) {
132 AKind = *Kind;
133 if (*Kind == AllocKind::Array)
134 return Region;
135 else
136 return nullptr;
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))
141 return Region;
143 // No AllocKind stored and not symbolic, assume that it points to a single
144 // object.
145 return nullptr;
148 void PointerArithChecker::reportPointerArithMisuse(const Expr *E,
149 CheckerContext &C,
150 bool PointedNeeded) const {
151 SourceRange SR = E->getSourceRange();
152 if (SR.isInvalid())
153 return;
155 ProgramStateRef State = C.getState();
156 const MemRegion *Region = C.getSVal(E).getAsRegion();
157 if (!Region)
158 return;
159 if (PointedNeeded)
160 Region = getPointedRegion(Region, C);
161 if (!Region)
162 return;
164 bool IsPolymorphic = false;
165 AllocKind Kind = AllocKind::Unknown;
166 if (const MemRegion *ArrayRegion =
167 getArrayRegion(Region, IsPolymorphic, Kind, C)) {
168 if (!IsPolymorphic)
169 return;
170 if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
171 if (!BT_polyArray)
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));
181 return;
184 if (Kind == AllocKind::Reinterpreted)
185 return;
187 // We might not have enough information about symbolic regions.
188 if (Kind != AllocKind::SingleObject &&
189 Region->getKind() == MemRegion::Kind::SymbolicRegionKind)
190 return;
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);
199 R->addRange(SR);
200 R->markInteresting(Region);
201 C.emitReport(std::move(R));
205 void PointerArithChecker::initAllocIdentifiers(ASTContext &C) const {
206 if (!AllocFunctions.empty())
207 return;
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);
219 if (!FD)
220 return;
221 IdentifierInfo *FunI = FD->getIdentifier();
222 initAllocIdentifiers(C.getASTContext());
223 if (AllocFunctions.count(FunI) == 0)
224 return;
226 SVal SV = C.getSVal(CE);
227 const MemRegion *Region = SV.getAsRegion();
228 if (!Region)
229 return;
230 // Assume that C allocation functions allocate arrays to avoid false
231 // positives.
232 // TODO: Add heuristics to distinguish alloc calls that allocates single
233 // objecs.
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();
241 if (!FD)
242 return;
244 AllocKind Kind = getKindOfNewOp(NE, FD);
246 ProgramStateRef State = C.getState();
247 SVal AllocedVal = C.getSVal(NE);
248 const MemRegion *Region = AllocedVal.getAsRegion();
249 if (!Region)
250 return;
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)
258 return;
260 const Expr *CastedExpr = CE->getSubExpr();
261 ProgramStateRef State = C.getState();
262 SVal CastedVal = C.getSVal(CastedExpr);
264 const MemRegion *Region = CastedVal.getAsRegion();
265 if (!Region)
266 return;
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)
276 return;
278 const Expr *CastedExpr = CE->getSubExpr();
279 ProgramStateRef State = C.getState();
280 SVal CastedVal = C.getSVal(CastedExpr);
282 const MemRegion *Region = CastedVal.getAsRegion();
283 if (!Region)
284 return;
286 if (const AllocKind *Kind = State->get<RegionState>(Region)) {
287 if (*Kind == AllocKind::Array || *Kind == AllocKind::Reinterpreted)
288 return;
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())
297 return;
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())
307 return;
309 // Indexing vector-type expressions is also OK.
310 if (SubsExpr->getBase()->getType()->isVectorType())
311 return;
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)
319 return;
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())
328 return;
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())
335 return;
336 reportPointerArithMisuse(Rhs, C);
340 void ento::registerPointerArithChecker(CheckerManager &mgr) {
341 mgr.registerChecker<PointerArithChecker>();
344 bool ento::shouldRegisterPointerArithChecker(const CheckerManager &mgr) {
345 return true;