xref: /aosp_15_r20/external/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp (revision 67e74705e28f6214e480b399dd47ea732279e315)
1*67e74705SXin Li //=== PointerArithChecker.cpp - Pointer arithmetic checker -----*- C++ -*--===//
2*67e74705SXin Li //
3*67e74705SXin Li //                     The LLVM Compiler Infrastructure
4*67e74705SXin Li //
5*67e74705SXin Li // This file is distributed under the University of Illinois Open Source
6*67e74705SXin Li // License. See LICENSE.TXT for details.
7*67e74705SXin Li //
8*67e74705SXin Li //===----------------------------------------------------------------------===//
9*67e74705SXin Li //
10*67e74705SXin Li // This files defines PointerArithChecker, a builtin checker that checks for
11*67e74705SXin Li // pointer arithmetic on locations other than array elements.
12*67e74705SXin Li //
13*67e74705SXin Li //===----------------------------------------------------------------------===//
14*67e74705SXin Li 
15*67e74705SXin Li #include "ClangSACheckers.h"
16*67e74705SXin Li #include "clang/AST/DeclCXX.h"
17*67e74705SXin Li #include "clang/AST/ExprCXX.h"
18*67e74705SXin Li #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19*67e74705SXin Li #include "clang/StaticAnalyzer/Core/Checker.h"
20*67e74705SXin Li #include "clang/StaticAnalyzer/Core/CheckerManager.h"
21*67e74705SXin Li #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22*67e74705SXin Li #include "llvm/ADT/SmallVector.h"
23*67e74705SXin Li 
24*67e74705SXin Li using namespace clang;
25*67e74705SXin Li using namespace ento;
26*67e74705SXin Li 
27*67e74705SXin Li namespace {
28*67e74705SXin Li enum class AllocKind {
29*67e74705SXin Li   SingleObject,
30*67e74705SXin Li   Array,
31*67e74705SXin Li   Unknown,
32*67e74705SXin Li   Reinterpreted // Single object interpreted as an array.
33*67e74705SXin Li };
34*67e74705SXin Li } // end namespace
35*67e74705SXin Li 
36*67e74705SXin Li namespace llvm {
37*67e74705SXin Li template <> struct FoldingSetTrait<AllocKind> {
Profilellvm::FoldingSetTrait38*67e74705SXin Li   static inline void Profile(AllocKind X, FoldingSetNodeID &ID) {
39*67e74705SXin Li     ID.AddInteger(static_cast<int>(X));
40*67e74705SXin Li   }
41*67e74705SXin Li };
42*67e74705SXin Li } // end namespace llvm
43*67e74705SXin Li 
44*67e74705SXin Li namespace {
45*67e74705SXin Li class PointerArithChecker
46*67e74705SXin Li     : public Checker<
47*67e74705SXin Li           check::PreStmt<BinaryOperator>, check::PreStmt<UnaryOperator>,
48*67e74705SXin Li           check::PreStmt<ArraySubscriptExpr>, check::PreStmt<CastExpr>,
49*67e74705SXin Li           check::PostStmt<CastExpr>, check::PostStmt<CXXNewExpr>,
50*67e74705SXin Li           check::PostStmt<CallExpr>, check::DeadSymbols> {
51*67e74705SXin Li   AllocKind getKindOfNewOp(const CXXNewExpr *NE, const FunctionDecl *FD) const;
52*67e74705SXin Li   const MemRegion *getArrayRegion(const MemRegion *Region, bool &Polymorphic,
53*67e74705SXin Li                                   AllocKind &AKind, CheckerContext &C) const;
54*67e74705SXin Li   const MemRegion *getPointedRegion(const MemRegion *Region,
55*67e74705SXin Li                                     CheckerContext &C) const;
56*67e74705SXin Li   void reportPointerArithMisuse(const Expr *E, CheckerContext &C,
57*67e74705SXin Li                                 bool PointedNeeded = false) const;
58*67e74705SXin Li   void initAllocIdentifiers(ASTContext &C) const;
59*67e74705SXin Li 
60*67e74705SXin Li   mutable std::unique_ptr<BuiltinBug> BT_pointerArith;
61*67e74705SXin Li   mutable std::unique_ptr<BuiltinBug> BT_polyArray;
62*67e74705SXin Li   mutable llvm::SmallSet<IdentifierInfo *, 8> AllocFunctions;
63*67e74705SXin Li 
64*67e74705SXin Li public:
65*67e74705SXin Li   void checkPreStmt(const UnaryOperator *UOp, CheckerContext &C) const;
66*67e74705SXin Li   void checkPreStmt(const BinaryOperator *BOp, CheckerContext &C) const;
67*67e74705SXin Li   void checkPreStmt(const ArraySubscriptExpr *SubExpr, CheckerContext &C) const;
68*67e74705SXin Li   void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
69*67e74705SXin Li   void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
70*67e74705SXin Li   void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
71*67e74705SXin Li   void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
72*67e74705SXin Li   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
73*67e74705SXin Li };
74*67e74705SXin Li } // end namespace
75*67e74705SXin Li 
REGISTER_MAP_WITH_PROGRAMSTATE(RegionState,const MemRegion *,AllocKind)76*67e74705SXin Li REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, const MemRegion *, AllocKind)
77*67e74705SXin Li 
78*67e74705SXin Li void PointerArithChecker::checkDeadSymbols(SymbolReaper &SR,
79*67e74705SXin Li                                            CheckerContext &C) const {
80*67e74705SXin Li   // TODO: intentional leak. Some information is garbage collected too early,
81*67e74705SXin Li   // see http://reviews.llvm.org/D14203 for further information.
82*67e74705SXin Li   /*ProgramStateRef State = C.getState();
83*67e74705SXin Li   RegionStateTy RegionStates = State->get<RegionState>();
84*67e74705SXin Li   for (RegionStateTy::iterator I = RegionStates.begin(), E = RegionStates.end();
85*67e74705SXin Li        I != E; ++I) {
86*67e74705SXin Li     if (!SR.isLiveRegion(I->first))
87*67e74705SXin Li       State = State->remove<RegionState>(I->first);
88*67e74705SXin Li   }
89*67e74705SXin Li   C.addTransition(State);*/
90*67e74705SXin Li }
91*67e74705SXin Li 
getKindOfNewOp(const CXXNewExpr * NE,const FunctionDecl * FD) const92*67e74705SXin Li AllocKind PointerArithChecker::getKindOfNewOp(const CXXNewExpr *NE,
93*67e74705SXin Li                                               const FunctionDecl *FD) const {
94*67e74705SXin Li   // This checker try not to assume anything about placement and overloaded
95*67e74705SXin Li   // new to avoid false positives.
96*67e74705SXin Li   if (isa<CXXMethodDecl>(FD))
97*67e74705SXin Li     return AllocKind::Unknown;
98*67e74705SXin Li   if (FD->getNumParams() != 1 || FD->isVariadic())
99*67e74705SXin Li     return AllocKind::Unknown;
100*67e74705SXin Li   if (NE->isArray())
101*67e74705SXin Li     return AllocKind::Array;
102*67e74705SXin Li 
103*67e74705SXin Li   return AllocKind::SingleObject;
104*67e74705SXin Li }
105*67e74705SXin Li 
106*67e74705SXin Li const MemRegion *
getPointedRegion(const MemRegion * Region,CheckerContext & C) const107*67e74705SXin Li PointerArithChecker::getPointedRegion(const MemRegion *Region,
108*67e74705SXin Li                                       CheckerContext &C) const {
109*67e74705SXin Li   assert(Region);
110*67e74705SXin Li   ProgramStateRef State = C.getState();
111*67e74705SXin Li   SVal S = State->getSVal(Region);
112*67e74705SXin Li   return S.getAsRegion();
113*67e74705SXin Li }
114*67e74705SXin Li 
115*67e74705SXin Li /// Checks whether a region is the part of an array.
116*67e74705SXin Li /// In case there is a dericed to base cast above the array element, the
117*67e74705SXin Li /// Polymorphic output value is set to true. AKind output value is set to the
118*67e74705SXin Li /// allocation kind of the inspected region.
getArrayRegion(const MemRegion * Region,bool & Polymorphic,AllocKind & AKind,CheckerContext & C) const119*67e74705SXin Li const MemRegion *PointerArithChecker::getArrayRegion(const MemRegion *Region,
120*67e74705SXin Li                                                      bool &Polymorphic,
121*67e74705SXin Li                                                      AllocKind &AKind,
122*67e74705SXin Li                                                      CheckerContext &C) const {
123*67e74705SXin Li   assert(Region);
124*67e74705SXin Li   while (Region->getKind() == MemRegion::Kind::CXXBaseObjectRegionKind) {
125*67e74705SXin Li     Region = Region->getAs<CXXBaseObjectRegion>()->getSuperRegion();
126*67e74705SXin Li     Polymorphic = true;
127*67e74705SXin Li   }
128*67e74705SXin Li   if (Region->getKind() == MemRegion::Kind::ElementRegionKind) {
129*67e74705SXin Li     Region = Region->getAs<ElementRegion>()->getSuperRegion();
130*67e74705SXin Li   }
131*67e74705SXin Li 
132*67e74705SXin Li   ProgramStateRef State = C.getState();
133*67e74705SXin Li   if (const AllocKind *Kind = State->get<RegionState>(Region)) {
134*67e74705SXin Li     AKind = *Kind;
135*67e74705SXin Li     if (*Kind == AllocKind::Array)
136*67e74705SXin Li       return Region;
137*67e74705SXin Li     else
138*67e74705SXin Li       return nullptr;
139*67e74705SXin Li   }
140*67e74705SXin Li   // When the region is symbolic and we do not have any information about it,
141*67e74705SXin Li   // assume that this is an array to avoid false positives.
142*67e74705SXin Li   if (Region->getKind() == MemRegion::Kind::SymbolicRegionKind)
143*67e74705SXin Li     return Region;
144*67e74705SXin Li 
145*67e74705SXin Li   // No AllocKind stored and not symbolic, assume that it points to a single
146*67e74705SXin Li   // object.
147*67e74705SXin Li   return nullptr;
148*67e74705SXin Li }
149*67e74705SXin Li 
reportPointerArithMisuse(const Expr * E,CheckerContext & C,bool PointedNeeded) const150*67e74705SXin Li void PointerArithChecker::reportPointerArithMisuse(const Expr *E,
151*67e74705SXin Li                                                    CheckerContext &C,
152*67e74705SXin Li                                                    bool PointedNeeded) const {
153*67e74705SXin Li   SourceRange SR = E->getSourceRange();
154*67e74705SXin Li   if (SR.isInvalid())
155*67e74705SXin Li     return;
156*67e74705SXin Li 
157*67e74705SXin Li   ProgramStateRef State = C.getState();
158*67e74705SXin Li   const MemRegion *Region =
159*67e74705SXin Li       State->getSVal(E, C.getLocationContext()).getAsRegion();
160*67e74705SXin Li   if (!Region)
161*67e74705SXin Li     return;
162*67e74705SXin Li   if (PointedNeeded)
163*67e74705SXin Li     Region = getPointedRegion(Region, C);
164*67e74705SXin Li   if (!Region)
165*67e74705SXin Li     return;
166*67e74705SXin Li 
167*67e74705SXin Li   bool IsPolymorphic = false;
168*67e74705SXin Li   AllocKind Kind = AllocKind::Unknown;
169*67e74705SXin Li   if (const MemRegion *ArrayRegion =
170*67e74705SXin Li           getArrayRegion(Region, IsPolymorphic, Kind, C)) {
171*67e74705SXin Li     if (!IsPolymorphic)
172*67e74705SXin Li       return;
173*67e74705SXin Li     if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
174*67e74705SXin Li       if (!BT_polyArray)
175*67e74705SXin Li         BT_polyArray.reset(new BuiltinBug(
176*67e74705SXin Li             this, "Dangerous pointer arithmetic",
177*67e74705SXin Li             "Pointer arithmetic on a pointer to base class is dangerous "
178*67e74705SXin Li             "because derived and base class may have different size."));
179*67e74705SXin Li       auto R = llvm::make_unique<BugReport>(*BT_polyArray,
180*67e74705SXin Li                                             BT_polyArray->getDescription(), N);
181*67e74705SXin Li       R->addRange(E->getSourceRange());
182*67e74705SXin Li       R->markInteresting(ArrayRegion);
183*67e74705SXin Li       C.emitReport(std::move(R));
184*67e74705SXin Li     }
185*67e74705SXin Li     return;
186*67e74705SXin Li   }
187*67e74705SXin Li 
188*67e74705SXin Li   if (Kind == AllocKind::Reinterpreted)
189*67e74705SXin Li     return;
190*67e74705SXin Li 
191*67e74705SXin Li   // We might not have enough information about symbolic regions.
192*67e74705SXin Li   if (Kind != AllocKind::SingleObject &&
193*67e74705SXin Li       Region->getKind() == MemRegion::Kind::SymbolicRegionKind)
194*67e74705SXin Li     return;
195*67e74705SXin Li 
196*67e74705SXin Li   if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
197*67e74705SXin Li     if (!BT_pointerArith)
198*67e74705SXin Li       BT_pointerArith.reset(new BuiltinBug(this, "Dangerous pointer arithmetic",
199*67e74705SXin Li                                            "Pointer arithmetic on non-array "
200*67e74705SXin Li                                            "variables relies on memory layout, "
201*67e74705SXin Li                                            "which is dangerous."));
202*67e74705SXin Li     auto R = llvm::make_unique<BugReport>(*BT_pointerArith,
203*67e74705SXin Li                                           BT_pointerArith->getDescription(), N);
204*67e74705SXin Li     R->addRange(SR);
205*67e74705SXin Li     R->markInteresting(Region);
206*67e74705SXin Li     C.emitReport(std::move(R));
207*67e74705SXin Li   }
208*67e74705SXin Li }
209*67e74705SXin Li 
initAllocIdentifiers(ASTContext & C) const210*67e74705SXin Li void PointerArithChecker::initAllocIdentifiers(ASTContext &C) const {
211*67e74705SXin Li   if (!AllocFunctions.empty())
212*67e74705SXin Li     return;
213*67e74705SXin Li   AllocFunctions.insert(&C.Idents.get("alloca"));
214*67e74705SXin Li   AllocFunctions.insert(&C.Idents.get("malloc"));
215*67e74705SXin Li   AllocFunctions.insert(&C.Idents.get("realloc"));
216*67e74705SXin Li   AllocFunctions.insert(&C.Idents.get("calloc"));
217*67e74705SXin Li   AllocFunctions.insert(&C.Idents.get("valloc"));
218*67e74705SXin Li }
219*67e74705SXin Li 
checkPostStmt(const CallExpr * CE,CheckerContext & C) const220*67e74705SXin Li void PointerArithChecker::checkPostStmt(const CallExpr *CE,
221*67e74705SXin Li                                         CheckerContext &C) const {
222*67e74705SXin Li   ProgramStateRef State = C.getState();
223*67e74705SXin Li   const FunctionDecl *FD = C.getCalleeDecl(CE);
224*67e74705SXin Li   if (!FD)
225*67e74705SXin Li     return;
226*67e74705SXin Li   IdentifierInfo *FunI = FD->getIdentifier();
227*67e74705SXin Li   initAllocIdentifiers(C.getASTContext());
228*67e74705SXin Li   if (AllocFunctions.count(FunI) == 0)
229*67e74705SXin Li     return;
230*67e74705SXin Li 
231*67e74705SXin Li   SVal SV = State->getSVal(CE, C.getLocationContext());
232*67e74705SXin Li   const MemRegion *Region = SV.getAsRegion();
233*67e74705SXin Li   if (!Region)
234*67e74705SXin Li     return;
235*67e74705SXin Li   // Assume that C allocation functions allocate arrays to avoid false
236*67e74705SXin Li   // positives.
237*67e74705SXin Li   // TODO: Add heuristics to distinguish alloc calls that allocates single
238*67e74705SXin Li   // objecs.
239*67e74705SXin Li   State = State->set<RegionState>(Region, AllocKind::Array);
240*67e74705SXin Li   C.addTransition(State);
241*67e74705SXin Li }
242*67e74705SXin Li 
checkPostStmt(const CXXNewExpr * NE,CheckerContext & C) const243*67e74705SXin Li void PointerArithChecker::checkPostStmt(const CXXNewExpr *NE,
244*67e74705SXin Li                                         CheckerContext &C) const {
245*67e74705SXin Li   const FunctionDecl *FD = NE->getOperatorNew();
246*67e74705SXin Li   if (!FD)
247*67e74705SXin Li     return;
248*67e74705SXin Li 
249*67e74705SXin Li   AllocKind Kind = getKindOfNewOp(NE, FD);
250*67e74705SXin Li 
251*67e74705SXin Li   ProgramStateRef State = C.getState();
252*67e74705SXin Li   SVal AllocedVal = State->getSVal(NE, C.getLocationContext());
253*67e74705SXin Li   const MemRegion *Region = AllocedVal.getAsRegion();
254*67e74705SXin Li   if (!Region)
255*67e74705SXin Li     return;
256*67e74705SXin Li   State = State->set<RegionState>(Region, Kind);
257*67e74705SXin Li   C.addTransition(State);
258*67e74705SXin Li }
259*67e74705SXin Li 
checkPostStmt(const CastExpr * CE,CheckerContext & C) const260*67e74705SXin Li void PointerArithChecker::checkPostStmt(const CastExpr *CE,
261*67e74705SXin Li                                         CheckerContext &C) const {
262*67e74705SXin Li   if (CE->getCastKind() != CastKind::CK_BitCast)
263*67e74705SXin Li     return;
264*67e74705SXin Li 
265*67e74705SXin Li   const Expr *CastedExpr = CE->getSubExpr();
266*67e74705SXin Li   ProgramStateRef State = C.getState();
267*67e74705SXin Li   SVal CastedVal = State->getSVal(CastedExpr, C.getLocationContext());
268*67e74705SXin Li 
269*67e74705SXin Li   const MemRegion *Region = CastedVal.getAsRegion();
270*67e74705SXin Li   if (!Region)
271*67e74705SXin Li     return;
272*67e74705SXin Li 
273*67e74705SXin Li   // Suppress reinterpret casted hits.
274*67e74705SXin Li   State = State->set<RegionState>(Region, AllocKind::Reinterpreted);
275*67e74705SXin Li   C.addTransition(State);
276*67e74705SXin Li }
277*67e74705SXin Li 
checkPreStmt(const CastExpr * CE,CheckerContext & C) const278*67e74705SXin Li void PointerArithChecker::checkPreStmt(const CastExpr *CE,
279*67e74705SXin Li                                        CheckerContext &C) const {
280*67e74705SXin Li   if (CE->getCastKind() != CastKind::CK_ArrayToPointerDecay)
281*67e74705SXin Li     return;
282*67e74705SXin Li 
283*67e74705SXin Li   const Expr *CastedExpr = CE->getSubExpr();
284*67e74705SXin Li   ProgramStateRef State = C.getState();
285*67e74705SXin Li   SVal CastedVal = State->getSVal(CastedExpr, C.getLocationContext());
286*67e74705SXin Li 
287*67e74705SXin Li   const MemRegion *Region = CastedVal.getAsRegion();
288*67e74705SXin Li   if (!Region)
289*67e74705SXin Li     return;
290*67e74705SXin Li 
291*67e74705SXin Li   if (const AllocKind *Kind = State->get<RegionState>(Region)) {
292*67e74705SXin Li     if (*Kind == AllocKind::Array || *Kind == AllocKind::Reinterpreted)
293*67e74705SXin Li       return;
294*67e74705SXin Li   }
295*67e74705SXin Li   State = State->set<RegionState>(Region, AllocKind::Array);
296*67e74705SXin Li   C.addTransition(State);
297*67e74705SXin Li }
298*67e74705SXin Li 
checkPreStmt(const UnaryOperator * UOp,CheckerContext & C) const299*67e74705SXin Li void PointerArithChecker::checkPreStmt(const UnaryOperator *UOp,
300*67e74705SXin Li                                        CheckerContext &C) const {
301*67e74705SXin Li   if (!UOp->isIncrementDecrementOp() || !UOp->getType()->isPointerType())
302*67e74705SXin Li     return;
303*67e74705SXin Li   reportPointerArithMisuse(UOp->getSubExpr(), C, true);
304*67e74705SXin Li }
305*67e74705SXin Li 
checkPreStmt(const ArraySubscriptExpr * SubsExpr,CheckerContext & C) const306*67e74705SXin Li void PointerArithChecker::checkPreStmt(const ArraySubscriptExpr *SubsExpr,
307*67e74705SXin Li                                        CheckerContext &C) const {
308*67e74705SXin Li   ProgramStateRef State = C.getState();
309*67e74705SXin Li   SVal Idx = State->getSVal(SubsExpr->getIdx(), C.getLocationContext());
310*67e74705SXin Li 
311*67e74705SXin Li   // Indexing with 0 is OK.
312*67e74705SXin Li   if (Idx.isZeroConstant())
313*67e74705SXin Li     return;
314*67e74705SXin Li   reportPointerArithMisuse(SubsExpr->getBase(), C);
315*67e74705SXin Li }
316*67e74705SXin Li 
checkPreStmt(const BinaryOperator * BOp,CheckerContext & C) const317*67e74705SXin Li void PointerArithChecker::checkPreStmt(const BinaryOperator *BOp,
318*67e74705SXin Li                                        CheckerContext &C) const {
319*67e74705SXin Li   BinaryOperatorKind OpKind = BOp->getOpcode();
320*67e74705SXin Li   if (!BOp->isAdditiveOp() && OpKind != BO_AddAssign && OpKind != BO_SubAssign)
321*67e74705SXin Li     return;
322*67e74705SXin Li 
323*67e74705SXin Li   const Expr *Lhs = BOp->getLHS();
324*67e74705SXin Li   const Expr *Rhs = BOp->getRHS();
325*67e74705SXin Li   ProgramStateRef State = C.getState();
326*67e74705SXin Li 
327*67e74705SXin Li   if (Rhs->getType()->isIntegerType() && Lhs->getType()->isPointerType()) {
328*67e74705SXin Li     SVal RHSVal = State->getSVal(Rhs, C.getLocationContext());
329*67e74705SXin Li     if (State->isNull(RHSVal).isConstrainedTrue())
330*67e74705SXin Li       return;
331*67e74705SXin Li     reportPointerArithMisuse(Lhs, C, !BOp->isAdditiveOp());
332*67e74705SXin Li   }
333*67e74705SXin Li   // The int += ptr; case is not valid C++.
334*67e74705SXin Li   if (Lhs->getType()->isIntegerType() && Rhs->getType()->isPointerType()) {
335*67e74705SXin Li     SVal LHSVal = State->getSVal(Lhs, C.getLocationContext());
336*67e74705SXin Li     if (State->isNull(LHSVal).isConstrainedTrue())
337*67e74705SXin Li       return;
338*67e74705SXin Li     reportPointerArithMisuse(Rhs, C);
339*67e74705SXin Li   }
340*67e74705SXin Li }
341*67e74705SXin Li 
registerPointerArithChecker(CheckerManager & mgr)342*67e74705SXin Li void ento::registerPointerArithChecker(CheckerManager &mgr) {
343*67e74705SXin Li   mgr.registerChecker<PointerArithChecker>();
344*67e74705SXin Li }
345