1e5dd7070Spatrick //== ArrayBoundChecker.cpp ------------------------------*- C++ -*--==//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // This file defines ArrayBoundChecker, which is a path-sensitive check
10e5dd7070Spatrick // which looks for an out-of-bound array element access.
11e5dd7070Spatrick //
12e5dd7070Spatrick //===----------------------------------------------------------------------===//
13e5dd7070Spatrick 
14e5dd7070Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
16e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h"
17e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/CheckerManager.h"
18e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19a9ac8606Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
20e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
21e5dd7070Spatrick 
22e5dd7070Spatrick using namespace clang;
23e5dd7070Spatrick using namespace ento;
24e5dd7070Spatrick 
25e5dd7070Spatrick namespace {
26e5dd7070Spatrick class ArrayBoundChecker :
27e5dd7070Spatrick     public Checker<check::Location> {
28e5dd7070Spatrick   mutable std::unique_ptr<BuiltinBug> BT;
29e5dd7070Spatrick 
30e5dd7070Spatrick public:
31e5dd7070Spatrick   void checkLocation(SVal l, bool isLoad, const Stmt* S,
32e5dd7070Spatrick                      CheckerContext &C) const;
33e5dd7070Spatrick };
34e5dd7070Spatrick }
35e5dd7070Spatrick 
checkLocation(SVal l,bool isLoad,const Stmt * LoadS,CheckerContext & C) const36e5dd7070Spatrick void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS,
37e5dd7070Spatrick                                       CheckerContext &C) const {
38e5dd7070Spatrick   // Check for out of bound array element access.
39e5dd7070Spatrick   const MemRegion *R = l.getAsRegion();
40e5dd7070Spatrick   if (!R)
41e5dd7070Spatrick     return;
42e5dd7070Spatrick 
43e5dd7070Spatrick   const ElementRegion *ER = dyn_cast<ElementRegion>(R);
44e5dd7070Spatrick   if (!ER)
45e5dd7070Spatrick     return;
46e5dd7070Spatrick 
47e5dd7070Spatrick   // Get the index of the accessed element.
48e5dd7070Spatrick   DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
49e5dd7070Spatrick 
50e5dd7070Spatrick   // Zero index is always in bound, this also passes ElementRegions created for
51e5dd7070Spatrick   // pointer casts.
52e5dd7070Spatrick   if (Idx.isZeroConstant())
53e5dd7070Spatrick     return;
54e5dd7070Spatrick 
55e5dd7070Spatrick   ProgramStateRef state = C.getState();
56e5dd7070Spatrick 
57e5dd7070Spatrick   // Get the size of the array.
58ec727ea7Spatrick   DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
59ec727ea7Spatrick       state, ER->getSuperRegion(), C.getSValBuilder(), ER->getValueType());
60e5dd7070Spatrick 
61*12c85518Srobert   ProgramStateRef StInBound, StOutBound;
62*12c85518Srobert   std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, ElementCount);
63e5dd7070Spatrick   if (StOutBound && !StInBound) {
64e5dd7070Spatrick     ExplodedNode *N = C.generateErrorNode(StOutBound);
65e5dd7070Spatrick     if (!N)
66e5dd7070Spatrick       return;
67e5dd7070Spatrick 
68e5dd7070Spatrick     if (!BT)
69e5dd7070Spatrick       BT.reset(new BuiltinBug(
70e5dd7070Spatrick           this, "Out-of-bound array access",
71e5dd7070Spatrick           "Access out-of-bound array element (buffer overflow)"));
72e5dd7070Spatrick 
73e5dd7070Spatrick     // FIXME: It would be nice to eventually make this diagnostic more clear,
74e5dd7070Spatrick     // e.g., by referencing the original declaration or by saying *why* this
75e5dd7070Spatrick     // reference is outside the range.
76e5dd7070Spatrick 
77e5dd7070Spatrick     // Generate a report for this bug.
78e5dd7070Spatrick     auto report =
79e5dd7070Spatrick         std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
80e5dd7070Spatrick 
81e5dd7070Spatrick     report->addRange(LoadS->getSourceRange());
82e5dd7070Spatrick     C.emitReport(std::move(report));
83e5dd7070Spatrick     return;
84e5dd7070Spatrick   }
85e5dd7070Spatrick 
86e5dd7070Spatrick   // Array bound check succeeded.  From this point forward the array bound
87e5dd7070Spatrick   // should always succeed.
88e5dd7070Spatrick   C.addTransition(StInBound);
89e5dd7070Spatrick }
90e5dd7070Spatrick 
registerArrayBoundChecker(CheckerManager & mgr)91e5dd7070Spatrick void ento::registerArrayBoundChecker(CheckerManager &mgr) {
92e5dd7070Spatrick   mgr.registerChecker<ArrayBoundChecker>();
93e5dd7070Spatrick }
94e5dd7070Spatrick 
shouldRegisterArrayBoundChecker(const CheckerManager & mgr)95ec727ea7Spatrick bool ento::shouldRegisterArrayBoundChecker(const CheckerManager &mgr) {
96e5dd7070Spatrick   return true;
97e5dd7070Spatrick }
98