ArrayBoundCheckerV2.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. //== ArrayBoundCheckerV2.cpp ------------------------------------*- 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 file defines ArrayBoundCheckerV2, which is a path-sensitive check
  10. // which looks for an out-of-bound array element access.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "clang/AST/CharUnits.h"
  14. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  15. #include "clang/StaticAnalyzer/Checkers/Taint.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/APSIntType.h"
  20. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  21. #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
  22. #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
  23. #include "llvm/ADT/SmallString.h"
  24. #include "llvm/Support/raw_ostream.h"
  25. #include <optional>
  26. using namespace clang;
  27. using namespace ento;
  28. using namespace taint;
  29. namespace {
  30. class ArrayBoundCheckerV2 :
  31. public Checker<check::Location> {
  32. mutable std::unique_ptr<BuiltinBug> BT;
  33. enum OOB_Kind { OOB_Precedes, OOB_Excedes, OOB_Tainted };
  34. void reportOOB(CheckerContext &C, ProgramStateRef errorState, OOB_Kind kind,
  35. std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const;
  36. public:
  37. void checkLocation(SVal l, bool isLoad, const Stmt*S,
  38. CheckerContext &C) const;
  39. };
  40. // FIXME: Eventually replace RegionRawOffset with this class.
  41. class RegionRawOffsetV2 {
  42. private:
  43. const SubRegion *baseRegion;
  44. SVal byteOffset;
  45. RegionRawOffsetV2()
  46. : baseRegion(nullptr), byteOffset(UnknownVal()) {}
  47. public:
  48. RegionRawOffsetV2(const SubRegion* base, SVal offset)
  49. : baseRegion(base), byteOffset(offset) {}
  50. NonLoc getByteOffset() const { return byteOffset.castAs<NonLoc>(); }
  51. const SubRegion *getRegion() const { return baseRegion; }
  52. static RegionRawOffsetV2 computeOffset(ProgramStateRef state,
  53. SValBuilder &svalBuilder,
  54. SVal location);
  55. void dump() const;
  56. void dumpToStream(raw_ostream &os) const;
  57. };
  58. }
  59. static SVal computeExtentBegin(SValBuilder &svalBuilder,
  60. const MemRegion *region) {
  61. const MemSpaceRegion *SR = region->getMemorySpace();
  62. if (SR->getKind() == MemRegion::UnknownSpaceRegionKind)
  63. return UnknownVal();
  64. else
  65. return svalBuilder.makeZeroArrayIndex();
  66. }
  67. // TODO: once the constraint manager is smart enough to handle non simplified
  68. // symbolic expressions remove this function. Note that this can not be used in
  69. // the constraint manager as is, since this does not handle overflows. It is
  70. // safe to assume, however, that memory offsets will not overflow.
  71. static std::pair<NonLoc, nonloc::ConcreteInt>
  72. getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent,
  73. SValBuilder &svalBuilder) {
  74. std::optional<nonloc::SymbolVal> SymVal = offset.getAs<nonloc::SymbolVal>();
  75. if (SymVal && SymVal->isExpression()) {
  76. if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SymVal->getSymbol())) {
  77. llvm::APSInt constant =
  78. APSIntType(extent.getValue()).convert(SIE->getRHS());
  79. switch (SIE->getOpcode()) {
  80. case BO_Mul:
  81. // The constant should never be 0 here, since it the result of scaling
  82. // based on the size of a type which is never 0.
  83. if ((extent.getValue() % constant) != 0)
  84. return std::pair<NonLoc, nonloc::ConcreteInt>(offset, extent);
  85. else
  86. return getSimplifiedOffsets(
  87. nonloc::SymbolVal(SIE->getLHS()),
  88. svalBuilder.makeIntVal(extent.getValue() / constant),
  89. svalBuilder);
  90. case BO_Add:
  91. return getSimplifiedOffsets(
  92. nonloc::SymbolVal(SIE->getLHS()),
  93. svalBuilder.makeIntVal(extent.getValue() - constant), svalBuilder);
  94. default:
  95. break;
  96. }
  97. }
  98. }
  99. return std::pair<NonLoc, nonloc::ConcreteInt>(offset, extent);
  100. }
  101. void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
  102. const Stmt* LoadS,
  103. CheckerContext &checkerContext) const {
  104. // NOTE: Instead of using ProgramState::assumeInBound(), we are prototyping
  105. // some new logic here that reasons directly about memory region extents.
  106. // Once that logic is more mature, we can bring it back to assumeInBound()
  107. // for all clients to use.
  108. //
  109. // The algorithm we are using here for bounds checking is to see if the
  110. // memory access is within the extent of the base region. Since we
  111. // have some flexibility in defining the base region, we can achieve
  112. // various levels of conservatism in our buffer overflow checking.
  113. ProgramStateRef state = checkerContext.getState();
  114. SValBuilder &svalBuilder = checkerContext.getSValBuilder();
  115. const RegionRawOffsetV2 &rawOffset =
  116. RegionRawOffsetV2::computeOffset(state, svalBuilder, location);
  117. if (!rawOffset.getRegion())
  118. return;
  119. NonLoc rawOffsetVal = rawOffset.getByteOffset();
  120. // CHECK LOWER BOUND: Is byteOffset < extent begin?
  121. // If so, we are doing a load/store
  122. // before the first valid offset in the memory region.
  123. SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion());
  124. if (std::optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) {
  125. if (auto ConcreteNV = NV->getAs<nonloc::ConcreteInt>()) {
  126. std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets =
  127. getSimplifiedOffsets(rawOffset.getByteOffset(), *ConcreteNV,
  128. svalBuilder);
  129. rawOffsetVal = simplifiedOffsets.first;
  130. *NV = simplifiedOffsets.second;
  131. }
  132. SVal lowerBound = svalBuilder.evalBinOpNN(state, BO_LT, rawOffsetVal, *NV,
  133. svalBuilder.getConditionType());
  134. std::optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>();
  135. if (!lowerBoundToCheck)
  136. return;
  137. ProgramStateRef state_precedesLowerBound, state_withinLowerBound;
  138. std::tie(state_precedesLowerBound, state_withinLowerBound) =
  139. state->assume(*lowerBoundToCheck);
  140. // Are we constrained enough to definitely precede the lower bound?
  141. if (state_precedesLowerBound && !state_withinLowerBound) {
  142. reportOOB(checkerContext, state_precedesLowerBound, OOB_Precedes);
  143. return;
  144. }
  145. // Otherwise, assume the constraint of the lower bound.
  146. assert(state_withinLowerBound);
  147. state = state_withinLowerBound;
  148. }
  149. do {
  150. // CHECK UPPER BOUND: Is byteOffset >= size(baseRegion)? If so,
  151. // we are doing a load/store after the last valid offset.
  152. const MemRegion *MR = rawOffset.getRegion();
  153. DefinedOrUnknownSVal Size = getDynamicExtent(state, MR, svalBuilder);
  154. if (!isa<NonLoc>(Size))
  155. break;
  156. if (auto ConcreteSize = Size.getAs<nonloc::ConcreteInt>()) {
  157. std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets =
  158. getSimplifiedOffsets(rawOffset.getByteOffset(), *ConcreteSize,
  159. svalBuilder);
  160. rawOffsetVal = simplifiedOffsets.first;
  161. Size = simplifiedOffsets.second;
  162. }
  163. SVal upperbound = svalBuilder.evalBinOpNN(state, BO_GE, rawOffsetVal,
  164. Size.castAs<NonLoc>(),
  165. svalBuilder.getConditionType());
  166. std::optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>();
  167. if (!upperboundToCheck)
  168. break;
  169. ProgramStateRef state_exceedsUpperBound, state_withinUpperBound;
  170. std::tie(state_exceedsUpperBound, state_withinUpperBound) =
  171. state->assume(*upperboundToCheck);
  172. // If we are under constrained and the index variables are tainted, report.
  173. if (state_exceedsUpperBound && state_withinUpperBound) {
  174. SVal ByteOffset = rawOffset.getByteOffset();
  175. if (isTainted(state, ByteOffset)) {
  176. reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted,
  177. std::make_unique<TaintBugVisitor>(ByteOffset));
  178. return;
  179. }
  180. } else if (state_exceedsUpperBound) {
  181. // If we are constrained enough to definitely exceed the upper bound,
  182. // report.
  183. assert(!state_withinUpperBound);
  184. reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes);
  185. return;
  186. }
  187. assert(state_withinUpperBound);
  188. state = state_withinUpperBound;
  189. }
  190. while (false);
  191. checkerContext.addTransition(state);
  192. }
  193. void ArrayBoundCheckerV2::reportOOB(
  194. CheckerContext &checkerContext, ProgramStateRef errorState, OOB_Kind kind,
  195. std::unique_ptr<BugReporterVisitor> Visitor) const {
  196. ExplodedNode *errorNode = checkerContext.generateErrorNode(errorState);
  197. if (!errorNode)
  198. return;
  199. if (!BT)
  200. BT.reset(new BuiltinBug(this, "Out-of-bound access"));
  201. // FIXME: This diagnostics are preliminary. We should get far better
  202. // diagnostics for explaining buffer overruns.
  203. SmallString<256> buf;
  204. llvm::raw_svector_ostream os(buf);
  205. os << "Out of bound memory access ";
  206. switch (kind) {
  207. case OOB_Precedes:
  208. os << "(accessed memory precedes memory block)";
  209. break;
  210. case OOB_Excedes:
  211. os << "(access exceeds upper limit of memory block)";
  212. break;
  213. case OOB_Tainted:
  214. os << "(index is tainted)";
  215. break;
  216. }
  217. auto BR = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), errorNode);
  218. BR->addVisitor(std::move(Visitor));
  219. checkerContext.emitReport(std::move(BR));
  220. }
  221. #ifndef NDEBUG
  222. LLVM_DUMP_METHOD void RegionRawOffsetV2::dump() const {
  223. dumpToStream(llvm::errs());
  224. }
  225. void RegionRawOffsetV2::dumpToStream(raw_ostream &os) const {
  226. os << "raw_offset_v2{" << getRegion() << ',' << getByteOffset() << '}';
  227. }
  228. #endif
  229. // Lazily computes a value to be used by 'computeOffset'. If 'val'
  230. // is unknown or undefined, we lazily substitute '0'. Otherwise,
  231. // return 'val'.
  232. static inline SVal getValue(SVal val, SValBuilder &svalBuilder) {
  233. return val.isUndef() ? svalBuilder.makeZeroArrayIndex() : val;
  234. }
  235. // Scale a base value by a scaling factor, and return the scaled
  236. // value as an SVal. Used by 'computeOffset'.
  237. static inline SVal scaleValue(ProgramStateRef state,
  238. NonLoc baseVal, CharUnits scaling,
  239. SValBuilder &sb) {
  240. return sb.evalBinOpNN(state, BO_Mul, baseVal,
  241. sb.makeArrayIndex(scaling.getQuantity()),
  242. sb.getArrayIndexType());
  243. }
  244. // Add an SVal to another, treating unknown and undefined values as
  245. // summing to UnknownVal. Used by 'computeOffset'.
  246. static SVal addValue(ProgramStateRef state, SVal x, SVal y,
  247. SValBuilder &svalBuilder) {
  248. // We treat UnknownVals and UndefinedVals the same here because we
  249. // only care about computing offsets.
  250. if (x.isUnknownOrUndef() || y.isUnknownOrUndef())
  251. return UnknownVal();
  252. return svalBuilder.evalBinOpNN(state, BO_Add, x.castAs<NonLoc>(),
  253. y.castAs<NonLoc>(),
  254. svalBuilder.getArrayIndexType());
  255. }
  256. /// Compute a raw byte offset from a base region. Used for array bounds
  257. /// checking.
  258. RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state,
  259. SValBuilder &svalBuilder,
  260. SVal location)
  261. {
  262. const MemRegion *region = location.getAsRegion();
  263. SVal offset = UndefinedVal();
  264. while (region) {
  265. switch (region->getKind()) {
  266. default: {
  267. if (const SubRegion *subReg = dyn_cast<SubRegion>(region)) {
  268. offset = getValue(offset, svalBuilder);
  269. if (!offset.isUnknownOrUndef())
  270. return RegionRawOffsetV2(subReg, offset);
  271. }
  272. return RegionRawOffsetV2();
  273. }
  274. case MemRegion::ElementRegionKind: {
  275. const ElementRegion *elemReg = cast<ElementRegion>(region);
  276. SVal index = elemReg->getIndex();
  277. if (!isa<NonLoc>(index))
  278. return RegionRawOffsetV2();
  279. QualType elemType = elemReg->getElementType();
  280. // If the element is an incomplete type, go no further.
  281. ASTContext &astContext = svalBuilder.getContext();
  282. if (elemType->isIncompleteType())
  283. return RegionRawOffsetV2();
  284. // Update the offset.
  285. offset = addValue(state,
  286. getValue(offset, svalBuilder),
  287. scaleValue(state,
  288. index.castAs<NonLoc>(),
  289. astContext.getTypeSizeInChars(elemType),
  290. svalBuilder),
  291. svalBuilder);
  292. if (offset.isUnknownOrUndef())
  293. return RegionRawOffsetV2();
  294. region = elemReg->getSuperRegion();
  295. continue;
  296. }
  297. }
  298. }
  299. return RegionRawOffsetV2();
  300. }
  301. void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) {
  302. mgr.registerChecker<ArrayBoundCheckerV2>();
  303. }
  304. bool ento::shouldRegisterArrayBoundCheckerV2(const CheckerManager &mgr) {
  305. return true;
  306. }