1d99bd55aSTed Kremenek //== ArrayBoundChecker.cpp ------------------------------*- C++ -*--==//
2d99bd55aSTed Kremenek //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6d99bd55aSTed Kremenek //
7d99bd55aSTed Kremenek //===----------------------------------------------------------------------===//
8d99bd55aSTed Kremenek //
9d99bd55aSTed Kremenek // This file defines ArrayBoundChecker, which is a path-sensitive check
10d99bd55aSTed Kremenek // which looks for an out-of-bound array element access.
11d99bd55aSTed Kremenek //
12d99bd55aSTed Kremenek //===----------------------------------------------------------------------===//
13d99bd55aSTed Kremenek 
1476a21502SKristof Umann #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
153a02247dSChandler Carruth #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
166a5674ffSArgyrios Kyrtzidis #include "clang/StaticAnalyzer/Core/Checker.h"
17dd407f42SArgyrios Kyrtzidis #include "clang/StaticAnalyzer/Core/CheckerManager.h"
18dd407f42SArgyrios Kyrtzidis #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
199b3df78bSCharusso #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
20f8cbac4bSTed Kremenek #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
21d99bd55aSTed Kremenek 
22d99bd55aSTed Kremenek using namespace clang;
23d99bd55aSTed Kremenek using namespace ento;
24d99bd55aSTed Kremenek 
25d99bd55aSTed Kremenek namespace {
26d99bd55aSTed Kremenek class ArrayBoundChecker :
276a5674ffSArgyrios Kyrtzidis     public Checker<check::Location> {
28b8984329SAhmed Charles   mutable std::unique_ptr<BuiltinBug> BT;
29b8984329SAhmed Charles 
30d99bd55aSTed Kremenek public:
313e0f415dSAnna Zaks   void checkLocation(SVal l, bool isLoad, const Stmt* S,
323e0f415dSAnna Zaks                      CheckerContext &C) const;
33d99bd55aSTed Kremenek };
34d99bd55aSTed Kremenek }
35d99bd55aSTed Kremenek 
checkLocation(SVal l,bool isLoad,const Stmt * LoadS,CheckerContext & C) const363e0f415dSAnna Zaks void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS,
37dd407f42SArgyrios Kyrtzidis                                       CheckerContext &C) const {
38d99bd55aSTed Kremenek   // Check for out of bound array element access.
39d99bd55aSTed Kremenek   const MemRegion *R = l.getAsRegion();
40d99bd55aSTed Kremenek   if (!R)
41d99bd55aSTed Kremenek     return;
42d99bd55aSTed Kremenek 
43d99bd55aSTed Kremenek   const ElementRegion *ER = dyn_cast<ElementRegion>(R);
44d99bd55aSTed Kremenek   if (!ER)
45d99bd55aSTed Kremenek     return;
46d99bd55aSTed Kremenek 
47d99bd55aSTed Kremenek   // Get the index of the accessed element.
4887396b9bSDavid Blaikie   DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
49d99bd55aSTed Kremenek 
50d99bd55aSTed Kremenek   // Zero index is always in bound, this also passes ElementRegions created for
51d99bd55aSTed Kremenek   // pointer casts.
52d99bd55aSTed Kremenek   if (Idx.isZeroConstant())
53d99bd55aSTed Kremenek     return;
54d99bd55aSTed Kremenek 
5549b1e38eSTed Kremenek   ProgramStateRef state = C.getState();
56d99bd55aSTed Kremenek 
57d99bd55aSTed Kremenek   // Get the size of the array.
58af3d0d16SCharusso   DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
59af3d0d16SCharusso       state, ER->getSuperRegion(), C.getSValBuilder(), ER->getValueType());
60d99bd55aSTed Kremenek 
61*34ac048aSGabor Marton   ProgramStateRef StInBound, StOutBound;
62*34ac048aSGabor Marton   std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, ElementCount);
63d99bd55aSTed Kremenek   if (StOutBound && !StInBound) {
64e39bd407SDevin Coughlin     ExplodedNode *N = C.generateErrorNode(StOutBound);
65d99bd55aSTed Kremenek     if (!N)
66d99bd55aSTed Kremenek       return;
67d99bd55aSTed Kremenek 
68d99bd55aSTed Kremenek     if (!BT)
694aca9b1cSAlexander Kornienko       BT.reset(new BuiltinBug(
704aca9b1cSAlexander Kornienko           this, "Out-of-bound array access",
71dd407f42SArgyrios Kyrtzidis           "Access out-of-bound array element (buffer overflow)"));
72d99bd55aSTed Kremenek 
73d99bd55aSTed Kremenek     // FIXME: It would be nice to eventually make this diagnostic more clear,
74d99bd55aSTed Kremenek     // e.g., by referencing the original declaration or by saying *why* this
75d99bd55aSTed Kremenek     // reference is outside the range.
76d99bd55aSTed Kremenek 
77d99bd55aSTed Kremenek     // Generate a report for this bug.
782f169e7cSArtem Dergachev     auto report =
792f169e7cSArtem Dergachev         std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
80d99bd55aSTed Kremenek 
813e0f415dSAnna Zaks     report->addRange(LoadS->getSourceRange());
828d3a7a56SAaron Ballman     C.emitReport(std::move(report));
83d99bd55aSTed Kremenek     return;
84d99bd55aSTed Kremenek   }
85d99bd55aSTed Kremenek 
86d99bd55aSTed Kremenek   // Array bound check succeeded.  From this point forward the array bound
87d99bd55aSTed Kremenek   // should always succeed.
88da4c8d68SAnna Zaks   C.addTransition(StInBound);
89d99bd55aSTed Kremenek }
90dd407f42SArgyrios Kyrtzidis 
registerArrayBoundChecker(CheckerManager & mgr)91dd407f42SArgyrios Kyrtzidis void ento::registerArrayBoundChecker(CheckerManager &mgr) {
92dd407f42SArgyrios Kyrtzidis   mgr.registerChecker<ArrayBoundChecker>();
93dd407f42SArgyrios Kyrtzidis }
94058a7a45SKristof Umann 
shouldRegisterArrayBoundChecker(const CheckerManager & mgr)95bda3dd0dSKirstóf Umann bool ento::shouldRegisterArrayBoundChecker(const CheckerManager &mgr) {
96058a7a45SKristof Umann   return true;
97058a7a45SKristof Umann }
98