Coverage Report

Created: 2022-01-18 06:27

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
Line
Count
Source (jump to first uncovered line)
1
//== ReturnPointerRangeChecker.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 ReturnPointerRangeChecker, which is a path-sensitive check
10
// which looks for an out-of-bound pointer being returned to callers.
11
//
12
//===----------------------------------------------------------------------===//
13
14
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.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 "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
22
23
using namespace clang;
24
using namespace ento;
25
26
namespace {
27
class ReturnPointerRangeChecker :
28
    public Checker< check::PreStmt<ReturnStmt> > {
29
  mutable std::unique_ptr<BuiltinBug> BT;
30
31
public:
32
    void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
33
};
34
}
35
36
void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
37
281
                                             CheckerContext &C) const {
38
281
  ProgramStateRef state = C.getState();
39
40
281
  const Expr *RetE = RS->getRetValue();
41
281
  if (!RetE)
42
14
    return;
43
44
267
  SVal V = C.getSVal(RetE);
45
267
  const MemRegion *R = V.getAsRegion();
46
47
267
  const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(R);
48
267
  if (!ER)
49
254
    return;
50
51
13
  DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
52
  // Zero index is always in bound, this also passes ElementRegions created for
53
  // pointer casts.
54
13
  if (Idx.isZeroConstant())
55
4
    return;
56
57
  // FIXME: All of this out-of-bounds checking should eventually be refactored
58
  // into a common place.
59
9
  DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
60
9
      state, ER->getSuperRegion(), C.getSValBuilder(), ER->getValueType());
61
62
  // We assume that the location after the last element in the array is used as
63
  // end() iterator. Reporting on these would return too many false positives.
64
9
  if (Idx == ElementCount)
65
2
    return;
66
67
7
  ProgramStateRef StInBound = state->assumeInBound(Idx, ElementCount, true);
68
7
  ProgramStateRef StOutBound = state->assumeInBound(Idx, ElementCount, false);
69
7
  if (StOutBound && !StInBound) {
70
7
    ExplodedNode *N = C.generateErrorNode(StOutBound);
71
72
7
    if (!N)
73
0
      return;
74
75
    // FIXME: This bug correspond to CWE-466.  Eventually we should have bug
76
    // types explicitly reference such exploit categories (when applicable).
77
7
    if (!BT)
78
3
      BT.reset(new BuiltinBug(
79
3
          this, "Buffer overflow",
80
3
          "Returned pointer value points outside the original object "
81
3
          "(potential buffer overflow)"));
82
83
    // Generate a report for this bug.
84
7
    auto Report =
85
7
        std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
86
7
    Report->addRange(RetE->getSourceRange());
87
88
7
    const auto ConcreteElementCount = ElementCount.getAs<nonloc::ConcreteInt>();
89
7
    const auto ConcreteIdx = Idx.getAs<nonloc::ConcreteInt>();
90
91
7
    const auto *DeclR = ER->getSuperRegion()->getAs<DeclRegion>();
92
93
7
    if (DeclR)
94
7
      Report->addNote("Original object declared here",
95
7
                      {DeclR->getDecl(), C.getSourceManager()});
96
97
7
    if (ConcreteElementCount) {
98
7
      SmallString<128> SBuf;
99
7
      llvm::raw_svector_ostream OS(SBuf);
100
7
      OS << "Original object ";
101
7
      if (DeclR) {
102
7
        OS << "'";
103
7
        DeclR->getDecl()->printName(OS);
104
7
        OS << "' ";
105
7
      }
106
7
      OS << "is an array of " << ConcreteElementCount->getValue() << " '";
107
7
      ER->getValueType().print(OS,
108
7
                               PrintingPolicy(C.getASTContext().getLangOpts()));
109
7
      OS << "' objects";
110
7
      if (ConcreteIdx) {
111
5
        OS << ", returned pointer points at index " << ConcreteIdx->getValue();
112
5
      }
113
114
7
      Report->addNote(SBuf,
115
7
                      {RetE, C.getSourceManager(), C.getLocationContext()});
116
7
    }
117
118
7
    bugreporter::trackExpressionValue(N, RetE, *Report);
119
120
7
    C.emitReport(std::move(Report));
121
7
  }
122
7
}
123
124
3
void ento::registerReturnPointerRangeChecker(CheckerManager &mgr) {
125
3
  mgr.registerChecker<ReturnPointerRangeChecker>();
126
3
}
127
128
6
bool ento::shouldRegisterReturnPointerRangeChecker(const CheckerManager &mgr) {
129
6
  return true;
130
6
}