clang 10.0.0git

PointerArithChecker.cpp
Go to the documentation of this file.
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.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15 #include "clang/AST/DeclCXX.h"
16 #include "clang/AST/ExprCXX.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
22 using namespace clang;
23 using namespace ento;
24
25 namespace {
26 enum class AllocKind {
27 SingleObject,
28 Array,
29 Unknown,
30 Reinterpreted // Single object interpreted as an array.
31 };
32 } // end namespace
33
34 namespace llvm {
35 template <> struct FoldingSetTrait<AllocKind> {
36 static inline void Profile(AllocKind X, FoldingSetNodeID &ID) {
37 ID.AddInteger(static_cast<int>(X));
38 }
39 };
40 } // end namespace llvm
41
42 namespace {
43 class PointerArithChecker
44 : public Checker<
45 check::PreStmt<BinaryOperator>, check::PreStmt<UnaryOperator>,
46 check::PreStmt<ArraySubscriptExpr>, check::PreStmt<CastExpr>,
47 check::PostStmt<CastExpr>, check::PostStmt<CXXNewExpr>,
48 check::PostStmt<CallExpr>, check::DeadSymbols> {
49 AllocKind getKindOfNewOp(const CXXNewExpr *NE, const FunctionDecl *FD) const;
50 const MemRegion *getArrayRegion(const MemRegion *Region, bool &Polymorphic,
51 AllocKind &AKind, CheckerContext &C) const;
52 const MemRegion *getPointedRegion(const MemRegion *Region,
53 CheckerContext &C) const;
54 void reportPointerArithMisuse(const Expr *E, CheckerContext &C,
55 bool PointedNeeded = false) const;
56 void initAllocIdentifiers(ASTContext &C) const;
57
58 mutable std::unique_ptr<BuiltinBug> BT_pointerArith;
59 mutable std::unique_ptr<BuiltinBug> BT_polyArray;
60 mutable llvm::SmallSet<IdentifierInfo *, 8> AllocFunctions;
61
62 public:
63 void checkPreStmt(const UnaryOperator *UOp, CheckerContext &C) const;
64 void checkPreStmt(const BinaryOperator *BOp, CheckerContext &C) const;
65 void checkPreStmt(const ArraySubscriptExpr *SubExpr, CheckerContext &C) const;
66 void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
67 void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
68 void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
69 void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
70 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
71 };
72 } // end namespace
73
74 REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, const MemRegion *, AllocKind)
75
76 void PointerArithChecker::checkDeadSymbols(SymbolReaper &SR,
77 CheckerContext &C) const {
78 // TODO: intentional leak. Some information is garbage collected too early,
79 // see http://reviews.llvm.org/D14203 for further information.
80 /*ProgramStateRef State = C.getState();
81 RegionStateTy RegionStates = State->get<RegionState>();
82 for (RegionStateTy::iterator I = RegionStates.begin(), E = RegionStates.end();
83 I != E; ++I) {
84 if (!SR.isLiveRegion(I->first))
85 State = State->remove<RegionState>(I->first);
86 }
87 C.addTransition(State);*/
88 }
89
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;
100
101 return AllocKind::SingleObject;
102 }
103
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();
111 }
112
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;
125 }
126 if (const auto *ElemRegion = dyn_cast<ElementRegion>(Region)) {
127 Region = ElemRegion->getSuperRegion();
128 }
129
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;
137 }
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;
142
143 // No AllocKind stored and not symbolic, assume that it points to a single
144 // object.
145 return nullptr;
146 }
147
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;
154
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;
163
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 BuiltinBug(
173 this, "Dangerous pointer arithmetic",
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>(
177 *BT_polyArray, BT_polyArray->getDescription(), N);
178 R->addRange(E->getSourceRange());
179 R->markInteresting(ArrayRegion);
180 C.emitReport(std::move(R));
181 }
182 return;
183 }
184
185 if (Kind == AllocKind::Reinterpreted)
186 return;
187
188 // We might not have enough information about symbolic regions.
189 if (Kind != AllocKind::SingleObject &&
190 Region->getKind() == MemRegion::Kind::SymbolicRegionKind)
191 return;
192
193 if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
194 if (!BT_pointerArith)
195 BT_pointerArith.reset(new BuiltinBug(this, "Dangerous pointer arithmetic",
196 "Pointer arithmetic on non-array "
197 "variables relies on memory layout, "
198 "which is dangerous."));
199 auto R = std::make_unique<PathSensitiveBugReport>(
200 *BT_pointerArith, BT_pointerArith->getDescription(), N);
201 R->addRange(SR);
202 R->markInteresting(Region);
203 C.emitReport(std::move(R));
204 }
205 }
206
207 void PointerArithChecker::initAllocIdentifiers(ASTContext &C) const {
208 if (!AllocFunctions.empty())
209 return;
210 AllocFunctions.insert(&C.Idents.get("alloca"));
211 AllocFunctions.insert(&C.Idents.get("malloc"));
212 AllocFunctions.insert(&C.Idents.get("realloc"));
213 AllocFunctions.insert(&C.Idents.get("calloc"));
214 AllocFunctions.insert(&C.Idents.get("valloc"));
215 }
216
217 void PointerArithChecker::checkPostStmt(const CallExpr *CE,
218 CheckerContext &C) const {
219 ProgramStateRef State = C.getState();
220 const FunctionDecl *FD = C.getCalleeDecl(CE);
221 if (!FD)
222 return;
223 IdentifierInfo *FunI = FD->getIdentifier();
224 initAllocIdentifiers(C.getASTContext());
225 if (AllocFunctions.count(FunI) == 0)
226 return;
227
228 SVal SV = C.getSVal(CE);
229 const MemRegion *Region = SV.getAsRegion();
230 if (!Region)
231 return;
232 // Assume that C allocation functions allocate arrays to avoid false
233 // positives.
234 // TODO: Add heuristics to distinguish alloc calls that allocates single
235 // objecs.
236 State = State->set<RegionState>(Region, AllocKind::Array);
237 C.addTransition(State);
238 }
239
240 void PointerArithChecker::checkPostStmt(const CXXNewExpr *NE,
241 CheckerContext &C) const {
242 const FunctionDecl *FD = NE->getOperatorNew();
243 if (!FD)
244 return;
245
246 AllocKind Kind = getKindOfNewOp(NE, FD);
247
248 ProgramStateRef State = C.getState();
249 SVal AllocedVal = C.getSVal(NE);
250 const MemRegion *Region = AllocedVal.getAsRegion();
251 if (!Region)
252 return;
253 State = State->set<RegionState>(Region, Kind);
254 C.addTransition(State);
255 }
256
257 void PointerArithChecker::checkPostStmt(const CastExpr *CE,
258 CheckerContext &C) const {
259 if (CE->getCastKind() != CastKind::CK_BitCast)
260 return;
261
262 const Expr *CastedExpr = CE->getSubExpr();
263 ProgramStateRef State = C.getState();
264 SVal CastedVal = C.getSVal(CastedExpr);
265
266 const MemRegion *Region = CastedVal.getAsRegion();
267 if (!Region)
268 return;
269
270 // Suppress reinterpret casted hits.
271 State = State->set<RegionState>(Region, AllocKind::Reinterpreted);
272 C.addTransition(State);
273 }
274
275 void PointerArithChecker::checkPreStmt(const CastExpr *CE,
276 CheckerContext &C) const {
277 if (CE->getCastKind() != CastKind::CK_ArrayToPointerDecay)
278 return;
279
280 const Expr *CastedExpr = CE->getSubExpr();
281 ProgramStateRef State = C.getState();
282 SVal CastedVal = C.getSVal(CastedExpr);
283
284 const MemRegion *Region = CastedVal.getAsRegion();
285 if (!Region)
286 return;
287
288 if (const AllocKind *Kind = State->get<RegionState>(Region)) {
289 if (*Kind == AllocKind::Array || *Kind == AllocKind::Reinterpreted)
290 return;
291 }
292 State = State->set<RegionState>(Region, AllocKind::Array);
293 C.addTransition(State);
294 }
295
296 void PointerArithChecker::checkPreStmt(const UnaryOperator *UOp,
297 CheckerContext &C) const {
298 if (!UOp->isIncrementDecrementOp() || !UOp->getType()->isPointerType())
299 return;
300 reportPointerArithMisuse(UOp->getSubExpr(), C, true);
301 }
302
303 void PointerArithChecker::checkPreStmt(const ArraySubscriptExpr *SubsExpr,
304 CheckerContext &C) const {
305 SVal Idx = C.getSVal(SubsExpr->getIdx());
306
307 // Indexing with 0 is OK.
308 if (Idx.isZeroConstant())
309 return;
310
311 // Indexing vector-type expressions is also OK.
312 if (SubsExpr->getBase()->getType()->isVectorType())
313 return;
314 reportPointerArithMisuse(SubsExpr->getBase(), C);
315 }
316
317 void PointerArithChecker::checkPreStmt(const BinaryOperator *BOp,
318 CheckerContext &C) const {
319 BinaryOperatorKind OpKind = BOp->getOpcode();
320 if (!BOp->isAdditiveOp() && OpKind != BO_AddAssign && OpKind != BO_SubAssign)
321 return;
322
323 const Expr *Lhs = BOp->getLHS();
324 const Expr *Rhs = BOp->getRHS();
325 ProgramStateRef State = C.getState();
326
327 if (Rhs->getType()->isIntegerType() && Lhs->getType()->isPointerType()) {
328 SVal RHSVal = C.getSVal(Rhs);
329 if (State->isNull(RHSVal).isConstrainedTrue())
330 return;
331 reportPointerArithMisuse(Lhs, C, !BOp->isAdditiveOp());
332 }
333 // The int += ptr; case is not valid C++.
334 if (Lhs->getType()->isIntegerType() && Rhs->getType()->isPointerType()) {
335 SVal LHSVal = C.getSVal(Lhs);
336 if (State->isNull(LHSVal).isConstrainedTrue())
337 return;
338 reportPointerArithMisuse(Rhs, C);
339 }
340 }
341
342 void ento::registerPointerArithChecker(CheckerManager &mgr) {
343 mgr.registerChecker<PointerArithChecker>();
344 }
345
346 bool ento::shouldRegisterPointerArithChecker(const LangOptions &LO) {
347 return true;
348 }
clang::Builtin::ID
ID
Definition: Builtins.h:46
ento
clang::FunctionDecl
Represents a function declaration or definition.
Definition: Decl.h:1783
clang::ArraySubscriptExpr::getBase
Expr * getBase()
Definition: Expr.h:2505
llvm
Specialize PointerLikeTypeTraits to allow LazyGenerationalUpdatePtr to be placed into a PointerUnion...
Definition: Dominators.h:30
clang::CXXNewExpr::getOperatorNew
FunctionDecl * getOperatorNew() const
Definition: ExprCXX.h:2218
clang::ento::ProgramStateRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Definition: ProgramState_Fwd.h:37
clang::BinaryOperator::getOpcode
Opcode getOpcode() const
Definition: Expr.h:3469
BugType.h
ExprCXX.h
Defines the clang::Expr interface and subclasses for C++ expressions.
clang::NamedDecl::getIdentifier
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition: Decl.h:244
clang::IdentifierInfo
One of these records is kept for each identifier that is lexed.
Definition: IdentifierTable.h:57
clang::ASTContext
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:168
State
LineState State
Definition: UnwrappedLineFormatter.cpp:932
llvm::FoldingSetTrait< AllocKind >::Profile
static void Profile(AllocKind X, FoldingSetNodeID &ID)
Definition: PointerArithChecker.cpp:36
clang::UnaryOperator::isIncrementDecrementOp
static bool isIncrementDecrementOp(Opcode Op)
Definition: Expr.h:2119
Checker.h
CheckerManager.h
clang::CastExpr::getSubExpr
Expr * getSubExpr()
Definition: Expr.h:3202
clang::LangOptions
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:53
clang::ASTContext::Idents
IdentifierTable & Idents
Definition: ASTContext.h:580
clang::BinaryOperatorKind
BinaryOperatorKind
Definition: OperationKinds.h:25
clang::BinaryOperator
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3434
clang::CastExpr
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
Definition: Expr.h:3150
clang::ArraySubscriptExpr::getIdx
Expr * getIdx()
Definition: Expr.h:2508
clang::Expr
This represents one expression.
Definition: Expr.h:108
clang::FunctionDecl::isVariadic
bool isVariadic() const
Whether this function is variadic.
Definition: Decl.cpp:2827
clang::Expr::getType
QualType getType() const
Definition: Expr.h:137
clang::SourceRange::isInvalid
bool isInvalid() const
Definition: SourceLocation.h:223
clang::UnaryOperator
UnaryOperator - This represents the unary-expression&#39;s (except sizeof and alignof), the postinc/postdec operators from postfix-expression, and various extensions.
Definition: Expr.h:2046
BuiltinCheckerRegistration.h
Kind
Kind
Definition: ChrootChecker.cpp:29
clang::IdentifierTable::get
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
Definition: IdentifierTable.h:529
clang::UnaryOperator::getSubExpr
Expr * getSubExpr() const
Definition: Expr.h:2076
REGISTER_MAP_WITH_PROGRAMSTATE
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
Definition: ProgramStateTrait.h:84
clang::CastExpr::getCastKind
CastKind getCastKind() const
Definition: Expr.h:3196
clang::CXXNewExpr
Represents a new-expression for memory allocation and constructor calls, e.g: "new CXXNewExpr(foo)"...
Definition: ExprCXX.h:2100
clang::CXXNewExpr::isArray
bool isArray() const
Definition: ExprCXX.h:2223
AllocKind
AllocKind
Definition: PointerArithChecker.cpp:26
clang::Type::isVectorType
bool isVectorType() const
Definition: Type.h:6606
clang::BinaryOperator::getLHS
Expr * getLHS() const
Definition: Expr.h:3474
clang
Dataflow Directional Tag Classes.
Definition: CFGReachabilityAnalysis.h:21
clang::interp::NE
bool NE(InterpState &S, CodePtr OpPC)
Definition: Interp.h:223
clang::Language::Unknown
clang::ArraySubscriptExpr
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
Definition: Expr.h:2462
clang::Type::isIntegerType
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition: Type.h:6811
clang::BinaryOperator::isAdditiveOp
static bool isAdditiveOp(Opcode Opc)
Definition: Expr.h:3510
X
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
Definition: SemaDecl.cpp:14780
DeclCXX.h
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate.h) and friends (in DeclFriend.h).
clang::Stmt::getSourceRange
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:263
clang::CallExpr
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2546
clang::BinaryOperator::getRHS
Expr * getRHS() const
Definition: Expr.h:3476
clang::Type::isPointerType
bool isPointerType() const
Definition: Type.h:6504
clang::SourceRange
A trivial tuple used to represent a source range.
Definition: SourceLocation.h:207
clang::FunctionDecl::getNumParams
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
Definition: Decl.cpp:3242
CheckerContext.h