/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 | } |