Coverage Report

Created: 2023-11-11 10:31

/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<BugType> BT;
30
31
public:
32
    void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
33
};
34
}
35
36
void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
37
282
                                             CheckerContext &C) const {
38
282
  ProgramStateRef state = C.getState();
39
40
282
  const Expr *RetE = RS->getRetValue();
41
282
  if (!RetE)
42
14
    return;
43
44
  // Skip "body farmed" functions.
45
268
  if (RetE->getSourceRange().isInvalid())
46
161
    return;
47
48
107
  SVal V = C.getSVal(RetE);
49
107
  const MemRegion *R = V.getAsRegion();
50
51
107
  const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(R);
52
107
  if (!ER)
53
94
    return;
54
55
13
  DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
56
  // Zero index is always in bound, this also passes ElementRegions created for
57
  // pointer casts.
58
13
  if (Idx.isZeroConstant())
59
4
    return;
60
61
  // FIXME: All of this out-of-bounds checking should eventually be refactored
62
  // into a common place.
63
9
  DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
64
9
      state, ER->getSuperRegion(), C.getSValBuilder(), ER->getValueType());
65
66
  // We assume that the location after the last element in the array is used as
67
  // end() iterator. Reporting on these would return too many false positives.
68
9
  if (Idx == ElementCount)
69
2
    return;
70
71
7
  ProgramStateRef StInBound, StOutBound;
72
7
  std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, ElementCount);
73
7
  if (StOutBound && !StInBound) {
74
7
    ExplodedNode *N = C.generateErrorNode(StOutBound);
75
76
7
    if (!N)
77
0
      return;
78
79
    // FIXME: This bug correspond to CWE-466.  Eventually we should have bug
80
    // types explicitly reference such exploit categories (when applicable).
81
7
    if (!BT)
82
3
      BT.reset(new BugType(this, "Buffer overflow"));
83
7
    constexpr llvm::StringLiteral Msg =
84
7
        "Returned pointer value points outside the original object "
85
7
        "(potential buffer overflow)";
86
87
    // Generate a report for this bug.
88
7
    auto Report = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
89
7
    Report->addRange(RetE->getSourceRange());
90
91
7
    const auto ConcreteElementCount = ElementCount.getAs<nonloc::ConcreteInt>();
92
7
    const auto ConcreteIdx = Idx.getAs<nonloc::ConcreteInt>();
93
94
7
    const auto *DeclR = ER->getSuperRegion()->getAs<DeclRegion>();
95
96
7
    if (DeclR)
97
7
      Report->addNote("Original object declared here",
98
7
                      {DeclR->getDecl(), C.getSourceManager()});
99
100
7
    if (ConcreteElementCount) {
101
7
      SmallString<128> SBuf;
102
7
      llvm::raw_svector_ostream OS(SBuf);
103
7
      OS << "Original object ";
104
7
      if (DeclR) {
105
7
        OS << "'";
106
7
        DeclR->getDecl()->printName(OS);
107
7
        OS << "' ";
108
7
      }
109
7
      OS << "is an array of " << ConcreteElementCount->getValue() << " '";
110
7
      ER->getValueType().print(OS,
111
7
                               PrintingPolicy(C.getASTContext().getLangOpts()));
112
7
      OS << "' objects";
113
7
      if (ConcreteIdx) {
114
5
        OS << ", returned pointer points at index " << ConcreteIdx->getValue();
115
5
      }
116
117
7
      Report->addNote(SBuf,
118
7
                      {RetE, C.getSourceManager(), C.getLocationContext()});
119
7
    }
120
121
7
    bugreporter::trackExpressionValue(N, RetE, *Report);
122
123
7
    C.emitReport(std::move(Report));
124
7
  }
125
7
}
126
127
3
void ento::registerReturnPointerRangeChecker(CheckerManager &mgr) {
128
3
  mgr.registerChecker<ReturnPointerRangeChecker>();
129
3
}
130
131
6
bool ento::shouldRegisterReturnPointerRangeChecker(const CheckerManager &mgr) {
132
6
  return true;
133
6
}