xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //=== PointerSubChecker.cpp - Pointer subtraction checker ------*- C++ -*--===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This files defines PointerSubChecker, a builtin checker that checks for
100b57cec5SDimitry Andric // pointer subtractions on two pointers pointing to different memory chunks.
110b57cec5SDimitry Andric // This check corresponds to CWE-469.
120b57cec5SDimitry Andric //
130b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
140b57cec5SDimitry Andric 
150b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
160b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20*0fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
215f757f3fSDimitry Andric #include "llvm/ADT/StringRef.h"
220b57cec5SDimitry Andric 
230b57cec5SDimitry Andric using namespace clang;
240b57cec5SDimitry Andric using namespace ento;
250b57cec5SDimitry Andric 
260b57cec5SDimitry Andric namespace {
270b57cec5SDimitry Andric class PointerSubChecker
280b57cec5SDimitry Andric   : public Checker< check::PreStmt<BinaryOperator> > {
29647cbc5dSDimitry Andric   const BugType BT{this, "Pointer subtraction"};
30*0fca6ea1SDimitry Andric   const llvm::StringLiteral Msg_MemRegionDifferent =
31*0fca6ea1SDimitry Andric       "Subtraction of two pointers that do not point into the same array "
32*0fca6ea1SDimitry Andric       "is undefined behavior.";
33*0fca6ea1SDimitry Andric   const llvm::StringLiteral Msg_LargeArrayIndex =
34*0fca6ea1SDimitry Andric       "Using an array index greater than the array size at pointer subtraction "
35*0fca6ea1SDimitry Andric       "is undefined behavior.";
36*0fca6ea1SDimitry Andric   const llvm::StringLiteral Msg_NegativeArrayIndex =
37*0fca6ea1SDimitry Andric       "Using a negative array index at pointer subtraction "
38*0fca6ea1SDimitry Andric       "is undefined behavior.";
39*0fca6ea1SDimitry Andric   const llvm::StringLiteral Msg_BadVarIndex =
40*0fca6ea1SDimitry Andric       "Indexing the address of a variable with other than 1 at this place "
41*0fca6ea1SDimitry Andric       "is undefined behavior.";
42*0fca6ea1SDimitry Andric 
43*0fca6ea1SDimitry Andric   bool checkArrayBounds(CheckerContext &C, const Expr *E,
44*0fca6ea1SDimitry Andric                         const ElementRegion *ElemReg,
45*0fca6ea1SDimitry Andric                         const MemRegion *Reg) const;
460b57cec5SDimitry Andric 
470b57cec5SDimitry Andric public:
480b57cec5SDimitry Andric   void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
490b57cec5SDimitry Andric };
500b57cec5SDimitry Andric }
510b57cec5SDimitry Andric 
checkArrayBounds(CheckerContext & C,const Expr * E,const ElementRegion * ElemReg,const MemRegion * Reg) const52*0fca6ea1SDimitry Andric bool PointerSubChecker::checkArrayBounds(CheckerContext &C, const Expr *E,
53*0fca6ea1SDimitry Andric                                          const ElementRegion *ElemReg,
54*0fca6ea1SDimitry Andric                                          const MemRegion *Reg) const {
55*0fca6ea1SDimitry Andric   if (!ElemReg)
56*0fca6ea1SDimitry Andric     return true;
57*0fca6ea1SDimitry Andric 
58*0fca6ea1SDimitry Andric   auto ReportBug = [&](const llvm::StringLiteral &Msg) {
59*0fca6ea1SDimitry Andric     if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
60*0fca6ea1SDimitry Andric       auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
61*0fca6ea1SDimitry Andric       R->addRange(E->getSourceRange());
62*0fca6ea1SDimitry Andric       C.emitReport(std::move(R));
63*0fca6ea1SDimitry Andric     }
64*0fca6ea1SDimitry Andric   };
65*0fca6ea1SDimitry Andric 
66*0fca6ea1SDimitry Andric   ProgramStateRef State = C.getState();
67*0fca6ea1SDimitry Andric   const MemRegion *SuperReg = ElemReg->getSuperRegion();
68*0fca6ea1SDimitry Andric   SValBuilder &SVB = C.getSValBuilder();
69*0fca6ea1SDimitry Andric 
70*0fca6ea1SDimitry Andric   if (SuperReg == Reg) {
71*0fca6ea1SDimitry Andric     if (const llvm::APSInt *I = SVB.getKnownValue(State, ElemReg->getIndex());
72*0fca6ea1SDimitry Andric         I && (!I->isOne() && !I->isZero()))
73*0fca6ea1SDimitry Andric       ReportBug(Msg_BadVarIndex);
74*0fca6ea1SDimitry Andric     return false;
75*0fca6ea1SDimitry Andric   }
76*0fca6ea1SDimitry Andric 
77*0fca6ea1SDimitry Andric   DefinedOrUnknownSVal ElemCount =
78*0fca6ea1SDimitry Andric       getDynamicElementCount(State, SuperReg, SVB, ElemReg->getElementType());
79*0fca6ea1SDimitry Andric   auto IndexTooLarge = SVB.evalBinOp(C.getState(), BO_GT, ElemReg->getIndex(),
80*0fca6ea1SDimitry Andric                                      ElemCount, SVB.getConditionType())
81*0fca6ea1SDimitry Andric                            .getAs<DefinedOrUnknownSVal>();
82*0fca6ea1SDimitry Andric   if (IndexTooLarge) {
83*0fca6ea1SDimitry Andric     ProgramStateRef S1, S2;
84*0fca6ea1SDimitry Andric     std::tie(S1, S2) = C.getState()->assume(*IndexTooLarge);
85*0fca6ea1SDimitry Andric     if (S1 && !S2) {
86*0fca6ea1SDimitry Andric       ReportBug(Msg_LargeArrayIndex);
87*0fca6ea1SDimitry Andric       return false;
88*0fca6ea1SDimitry Andric     }
89*0fca6ea1SDimitry Andric   }
90*0fca6ea1SDimitry Andric   auto IndexTooSmall = SVB.evalBinOp(State, BO_LT, ElemReg->getIndex(),
91*0fca6ea1SDimitry Andric                                      SVB.makeZeroVal(SVB.getArrayIndexType()),
92*0fca6ea1SDimitry Andric                                      SVB.getConditionType())
93*0fca6ea1SDimitry Andric                            .getAs<DefinedOrUnknownSVal>();
94*0fca6ea1SDimitry Andric   if (IndexTooSmall) {
95*0fca6ea1SDimitry Andric     ProgramStateRef S1, S2;
96*0fca6ea1SDimitry Andric     std::tie(S1, S2) = State->assume(*IndexTooSmall);
97*0fca6ea1SDimitry Andric     if (S1 && !S2) {
98*0fca6ea1SDimitry Andric       ReportBug(Msg_NegativeArrayIndex);
99*0fca6ea1SDimitry Andric       return false;
100*0fca6ea1SDimitry Andric     }
101*0fca6ea1SDimitry Andric   }
102*0fca6ea1SDimitry Andric   return true;
103*0fca6ea1SDimitry Andric }
104*0fca6ea1SDimitry Andric 
checkPreStmt(const BinaryOperator * B,CheckerContext & C) const1050b57cec5SDimitry Andric void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
1060b57cec5SDimitry Andric                                      CheckerContext &C) const {
1070b57cec5SDimitry Andric   // When doing pointer subtraction, if the two pointers do not point to the
108*0fca6ea1SDimitry Andric   // same array, emit a warning.
1090b57cec5SDimitry Andric   if (B->getOpcode() != BO_Sub)
1100b57cec5SDimitry Andric     return;
1110b57cec5SDimitry Andric 
1120b57cec5SDimitry Andric   SVal LV = C.getSVal(B->getLHS());
1130b57cec5SDimitry Andric   SVal RV = C.getSVal(B->getRHS());
1140b57cec5SDimitry Andric 
1150b57cec5SDimitry Andric   const MemRegion *LR = LV.getAsRegion();
1160b57cec5SDimitry Andric   const MemRegion *RR = RV.getAsRegion();
117*0fca6ea1SDimitry Andric   if (!LR || !RR)
1180b57cec5SDimitry Andric     return;
1190b57cec5SDimitry Andric 
120*0fca6ea1SDimitry Andric   // Allow subtraction of identical pointers.
121*0fca6ea1SDimitry Andric   if (LR == RR)
1220b57cec5SDimitry Andric     return;
1230b57cec5SDimitry Andric 
124*0fca6ea1SDimitry Andric   // No warning if one operand is unknown.
125*0fca6ea1SDimitry Andric   if (isa<SymbolicRegion>(LR) || isa<SymbolicRegion>(RR))
126*0fca6ea1SDimitry Andric     return;
127*0fca6ea1SDimitry Andric 
128*0fca6ea1SDimitry Andric   const auto *ElemLR = dyn_cast<ElementRegion>(LR);
129*0fca6ea1SDimitry Andric   const auto *ElemRR = dyn_cast<ElementRegion>(RR);
130*0fca6ea1SDimitry Andric 
131*0fca6ea1SDimitry Andric   if (!checkArrayBounds(C, B->getLHS(), ElemLR, RR))
132*0fca6ea1SDimitry Andric     return;
133*0fca6ea1SDimitry Andric   if (!checkArrayBounds(C, B->getRHS(), ElemRR, LR))
134*0fca6ea1SDimitry Andric     return;
135*0fca6ea1SDimitry Andric 
136*0fca6ea1SDimitry Andric   const ValueDecl *DiffDeclL = nullptr;
137*0fca6ea1SDimitry Andric   const ValueDecl *DiffDeclR = nullptr;
138*0fca6ea1SDimitry Andric 
139*0fca6ea1SDimitry Andric   if (ElemLR && ElemRR) {
140*0fca6ea1SDimitry Andric     const MemRegion *SuperLR = ElemLR->getSuperRegion();
141*0fca6ea1SDimitry Andric     const MemRegion *SuperRR = ElemRR->getSuperRegion();
142*0fca6ea1SDimitry Andric     if (SuperLR == SuperRR)
143*0fca6ea1SDimitry Andric       return;
1440b57cec5SDimitry Andric     // Allow arithmetic on different symbolic regions.
145*0fca6ea1SDimitry Andric     if (isa<SymbolicRegion>(SuperLR) || isa<SymbolicRegion>(SuperRR))
1460b57cec5SDimitry Andric       return;
147*0fca6ea1SDimitry Andric     if (const auto *SuperDLR = dyn_cast<DeclRegion>(SuperLR))
148*0fca6ea1SDimitry Andric       DiffDeclL = SuperDLR->getDecl();
149*0fca6ea1SDimitry Andric     if (const auto *SuperDRR = dyn_cast<DeclRegion>(SuperRR))
150*0fca6ea1SDimitry Andric       DiffDeclR = SuperDRR->getDecl();
151*0fca6ea1SDimitry Andric   }
1520b57cec5SDimitry Andric 
1530b57cec5SDimitry Andric   if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
154*0fca6ea1SDimitry Andric     auto R =
155*0fca6ea1SDimitry Andric         std::make_unique<PathSensitiveBugReport>(BT, Msg_MemRegionDifferent, N);
1560b57cec5SDimitry Andric     R->addRange(B->getSourceRange());
157*0fca6ea1SDimitry Andric     // The declarations may be identical even if the regions are different:
158*0fca6ea1SDimitry Andric     //   struct { int array[10]; } a, b;
159*0fca6ea1SDimitry Andric     //   do_something(&a.array[5] - &b.array[5]);
160*0fca6ea1SDimitry Andric     // In this case don't emit notes.
161*0fca6ea1SDimitry Andric     if (DiffDeclL != DiffDeclR) {
162*0fca6ea1SDimitry Andric       if (DiffDeclL)
163*0fca6ea1SDimitry Andric         R->addNote("Array at the left-hand side of subtraction",
164*0fca6ea1SDimitry Andric                    {DiffDeclL, C.getSourceManager()});
165*0fca6ea1SDimitry Andric       if (DiffDeclR)
166*0fca6ea1SDimitry Andric         R->addNote("Array at the right-hand side of subtraction",
167*0fca6ea1SDimitry Andric                    {DiffDeclR, C.getSourceManager()});
168*0fca6ea1SDimitry Andric     }
1690b57cec5SDimitry Andric     C.emitReport(std::move(R));
1700b57cec5SDimitry Andric   }
1710b57cec5SDimitry Andric }
1720b57cec5SDimitry Andric 
registerPointerSubChecker(CheckerManager & mgr)1730b57cec5SDimitry Andric void ento::registerPointerSubChecker(CheckerManager &mgr) {
1740b57cec5SDimitry Andric   mgr.registerChecker<PointerSubChecker>();
1750b57cec5SDimitry Andric }
1760b57cec5SDimitry Andric 
shouldRegisterPointerSubChecker(const CheckerManager & mgr)1775ffd83dbSDimitry Andric bool ento::shouldRegisterPointerSubChecker(const CheckerManager &mgr) {
1780b57cec5SDimitry Andric   return true;
1790b57cec5SDimitry Andric }
180