1 //=== PointerSubChecker.cpp - Pointer subtraction 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 PointerSubChecker, a builtin checker that checks for 10 // pointer subtractions on two pointers pointing to different memory chunks. 11 // This check corresponds to CWE-469. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 17 #include "clang/StaticAnalyzer/Core/Checker.h" 18 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" 21 #include "llvm/ADT/StringRef.h" 22 23 using namespace clang; 24 using namespace ento; 25 26 namespace { 27 class PointerSubChecker 28 : public Checker< check::PreStmt<BinaryOperator> > { 29 const BugType BT{this, "Pointer subtraction"}; 30 const llvm::StringLiteral Msg_MemRegionDifferent = 31 "Subtraction of two pointers that do not point into the same array " 32 "is undefined behavior."; 33 const llvm::StringLiteral Msg_LargeArrayIndex = 34 "Using an array index greater than the array size at pointer subtraction " 35 "is undefined behavior."; 36 const llvm::StringLiteral Msg_NegativeArrayIndex = 37 "Using a negative array index at pointer subtraction " 38 "is undefined behavior."; 39 const llvm::StringLiteral Msg_BadVarIndex = 40 "Indexing the address of a variable with other than 1 at this place " 41 "is undefined behavior."; 42 43 bool checkArrayBounds(CheckerContext &C, const Expr *E, 44 const ElementRegion *ElemReg, 45 const MemRegion *Reg) const; 46 void reportBug(CheckerContext &C, const Expr *E, 47 const llvm::StringLiteral &Msg) const; 48 49 public: 50 void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; 51 }; 52 } 53 54 bool PointerSubChecker::checkArrayBounds(CheckerContext &C, const Expr *E, 55 const ElementRegion *ElemReg, 56 const MemRegion *Reg) const { 57 if (!ElemReg) 58 return true; 59 60 ProgramStateRef State = C.getState(); 61 const MemRegion *SuperReg = ElemReg->getSuperRegion(); 62 SValBuilder &SVB = C.getSValBuilder(); 63 64 if (SuperReg == Reg) { 65 if (const llvm::APSInt *I = SVB.getKnownValue(State, ElemReg->getIndex()); 66 I && (!I->isOne() && !I->isZero())) 67 reportBug(C, E, Msg_BadVarIndex); 68 return false; 69 } 70 71 DefinedOrUnknownSVal ElemCount = 72 getDynamicElementCount(State, SuperReg, SVB, ElemReg->getElementType()); 73 auto IndexTooLarge = SVB.evalBinOp(C.getState(), BO_GT, ElemReg->getIndex(), 74 ElemCount, SVB.getConditionType()) 75 .getAs<DefinedOrUnknownSVal>(); 76 if (IndexTooLarge) { 77 ProgramStateRef S1, S2; 78 std::tie(S1, S2) = C.getState()->assume(*IndexTooLarge); 79 if (S1 && !S2) { 80 reportBug(C, E, Msg_LargeArrayIndex); 81 return false; 82 } 83 } 84 auto IndexTooSmall = SVB.evalBinOp(State, BO_LT, ElemReg->getIndex(), 85 SVB.makeZeroVal(SVB.getArrayIndexType()), 86 SVB.getConditionType()) 87 .getAs<DefinedOrUnknownSVal>(); 88 if (IndexTooSmall) { 89 ProgramStateRef S1, S2; 90 std::tie(S1, S2) = State->assume(*IndexTooSmall); 91 if (S1 && !S2) { 92 reportBug(C, E, Msg_NegativeArrayIndex); 93 return false; 94 } 95 } 96 return true; 97 } 98 99 void PointerSubChecker::reportBug(CheckerContext &C, const Expr *E, 100 const llvm::StringLiteral &Msg) const { 101 if (ExplodedNode *N = C.generateNonFatalErrorNode()) { 102 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N); 103 R->addRange(E->getSourceRange()); 104 C.emitReport(std::move(R)); 105 } 106 } 107 108 void PointerSubChecker::checkPreStmt(const BinaryOperator *B, 109 CheckerContext &C) const { 110 // When doing pointer subtraction, if the two pointers do not point to the 111 // same array, emit a warning. 112 if (B->getOpcode() != BO_Sub) 113 return; 114 115 SVal LV = C.getSVal(B->getLHS()); 116 SVal RV = C.getSVal(B->getRHS()); 117 118 const MemRegion *LR = LV.getAsRegion(); 119 const MemRegion *RR = RV.getAsRegion(); 120 if (!LR || !RR) 121 return; 122 123 // Allow subtraction of identical pointers. 124 if (LR == RR) 125 return; 126 127 // No warning if one operand is unknown. 128 if (isa<SymbolicRegion>(LR) || isa<SymbolicRegion>(RR)) 129 return; 130 131 const auto *ElemLR = dyn_cast<ElementRegion>(LR); 132 const auto *ElemRR = dyn_cast<ElementRegion>(RR); 133 134 if (!checkArrayBounds(C, B->getLHS(), ElemLR, RR)) 135 return; 136 if (!checkArrayBounds(C, B->getRHS(), ElemRR, LR)) 137 return; 138 139 if (ElemLR && ElemRR) { 140 const MemRegion *SuperLR = ElemLR->getSuperRegion(); 141 const MemRegion *SuperRR = ElemRR->getSuperRegion(); 142 if (SuperLR == SuperRR) 143 return; 144 // Allow arithmetic on different symbolic regions. 145 if (isa<SymbolicRegion>(SuperLR) || isa<SymbolicRegion>(SuperRR)) 146 return; 147 } 148 149 reportBug(C, B, Msg_MemRegionDifferent); 150 } 151 152 void ento::registerPointerSubChecker(CheckerManager &mgr) { 153 mgr.registerChecker<PointerSubChecker>(); 154 } 155 156 bool ento::shouldRegisterPointerSubChecker(const CheckerManager &mgr) { 157 return true; 158 } 159