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