1 //== ReturnPointerRangeChecker.cpp ------------------------------*- C++ -*--==//
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
7 //===----------------------------------------------------------------------===//
9 // This file defines ReturnPointerRangeChecker, which is a path-sensitive check
10 // which looks for an out-of-bound pointer being returned to callers.
12 //===----------------------------------------------------------------------===//
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"
23 using namespace clang
;
27 class ReturnPointerRangeChecker
:
28 public Checker
< check::PreStmt
<ReturnStmt
> > {
29 mutable std::unique_ptr
<BugType
> BT
;
32 void checkPreStmt(const ReturnStmt
*RS
, CheckerContext
&C
) const;
36 void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt
*RS
,
37 CheckerContext
&C
) const {
38 ProgramStateRef state
= C
.getState();
40 const Expr
*RetE
= RS
->getRetValue();
44 // Skip "body farmed" functions.
45 if (RetE
->getSourceRange().isInvalid())
48 SVal V
= C
.getSVal(RetE
);
49 const MemRegion
*R
= V
.getAsRegion();
51 const ElementRegion
*ER
= dyn_cast_or_null
<ElementRegion
>(R
);
55 DefinedOrUnknownSVal Idx
= ER
->getIndex().castAs
<DefinedOrUnknownSVal
>();
56 // Zero index is always in bound, this also passes ElementRegions created for
58 if (Idx
.isZeroConstant())
61 // FIXME: All of this out-of-bounds checking should eventually be refactored
62 // into a common place.
63 DefinedOrUnknownSVal ElementCount
= getDynamicElementCount(
64 state
, ER
->getSuperRegion(), C
.getSValBuilder(), ER
->getValueType());
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 if (Idx
== ElementCount
)
71 ProgramStateRef StInBound
, StOutBound
;
72 std::tie(StInBound
, StOutBound
) = state
->assumeInBoundDual(Idx
, ElementCount
);
73 if (StOutBound
&& !StInBound
) {
74 ExplodedNode
*N
= C
.generateErrorNode(StOutBound
);
79 // FIXME: This bug correspond to CWE-466. Eventually we should have bug
80 // types explicitly reference such exploit categories (when applicable).
82 BT
.reset(new BugType(this, "Buffer overflow"));
83 constexpr llvm::StringLiteral Msg
=
84 "Returned pointer value points outside the original object "
85 "(potential buffer overflow)";
87 // Generate a report for this bug.
88 auto Report
= std::make_unique
<PathSensitiveBugReport
>(*BT
, Msg
, N
);
89 Report
->addRange(RetE
->getSourceRange());
91 const auto ConcreteElementCount
= ElementCount
.getAs
<nonloc::ConcreteInt
>();
92 const auto ConcreteIdx
= Idx
.getAs
<nonloc::ConcreteInt
>();
94 const auto *DeclR
= ER
->getSuperRegion()->getAs
<DeclRegion
>();
97 Report
->addNote("Original object declared here",
98 {DeclR
->getDecl(), C
.getSourceManager()});
100 if (ConcreteElementCount
) {
101 SmallString
<128> SBuf
;
102 llvm::raw_svector_ostream
OS(SBuf
);
103 OS
<< "Original object ";
106 DeclR
->getDecl()->printName(OS
);
109 OS
<< "is an array of " << ConcreteElementCount
->getValue() << " '";
110 ER
->getValueType().print(OS
,
111 PrintingPolicy(C
.getASTContext().getLangOpts()));
114 OS
<< ", returned pointer points at index " << ConcreteIdx
->getValue();
117 Report
->addNote(SBuf
,
118 {RetE
, C
.getSourceManager(), C
.getLocationContext()});
121 bugreporter::trackExpressionValue(N
, RetE
, *Report
);
123 C
.emitReport(std::move(Report
));
127 void ento::registerReturnPointerRangeChecker(CheckerManager
&mgr
) {
128 mgr
.registerChecker
<ReturnPointerRangeChecker
>();
131 bool ento::shouldRegisterReturnPointerRangeChecker(const CheckerManager
&mgr
) {