/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- ReturnValueChecker - Applies guaranteed return values ----*- 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 defines ReturnValueChecker, which checks for calls with guaranteed |
10 | | // boolean return value. It ensures the return value of each function call. |
11 | | // |
12 | | //===----------------------------------------------------------------------===// |
13 | | |
14 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
15 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
16 | | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
17 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" |
18 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
19 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
20 | | #include "llvm/ADT/SmallVector.h" |
21 | | #include <optional> |
22 | | |
23 | | using namespace clang; |
24 | | using namespace ento; |
25 | | |
26 | | namespace { |
27 | | class ReturnValueChecker : public Checker<check::PostCall, check::EndFunction> { |
28 | | public: |
29 | | // It sets the predefined invariant ('CDM') if the current call not break it. |
30 | | void checkPostCall(const CallEvent &Call, CheckerContext &C) const; |
31 | | |
32 | | // It reports whether a predefined invariant ('CDM') is broken. |
33 | | void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; |
34 | | |
35 | | private: |
36 | | // The pairs are in the following form: {{{class, call}}, return value} |
37 | | const CallDescriptionMap<bool> CDM = { |
38 | | // These are known in the LLVM project: 'Error()' |
39 | | {{{"ARMAsmParser", "Error"}}, true}, |
40 | | {{{"HexagonAsmParser", "Error"}}, true}, |
41 | | {{{"LLLexer", "Error"}}, true}, |
42 | | {{{"LLParser", "Error"}}, true}, |
43 | | {{{"MCAsmParser", "Error"}}, true}, |
44 | | {{{"MCAsmParserExtension", "Error"}}, true}, |
45 | | {{{"TGParser", "Error"}}, true}, |
46 | | {{{"X86AsmParser", "Error"}}, true}, |
47 | | // 'TokError()' |
48 | | {{{"LLParser", "TokError"}}, true}, |
49 | | {{{"MCAsmParser", "TokError"}}, true}, |
50 | | {{{"MCAsmParserExtension", "TokError"}}, true}, |
51 | | {{{"TGParser", "TokError"}}, true}, |
52 | | // 'error()' |
53 | | {{{"MIParser", "error"}}, true}, |
54 | | {{{"WasmAsmParser", "error"}}, true}, |
55 | | {{{"WebAssemblyAsmParser", "error"}}, true}, |
56 | | // Other |
57 | | {{{"AsmParser", "printError"}}, true}}; |
58 | | }; |
59 | | } // namespace |
60 | | |
61 | 4 | static std::string getName(const CallEvent &Call) { |
62 | 4 | std::string Name; |
63 | 4 | if (const auto *MD = dyn_cast<CXXMethodDecl>(Call.getDecl())) |
64 | 4 | if (const CXXRecordDecl *RD = MD->getParent()) |
65 | 4 | Name += RD->getNameAsString() + "::"; |
66 | | |
67 | 4 | Name += Call.getCalleeIdentifier()->getName(); |
68 | 4 | return Name; |
69 | 4 | } |
70 | | |
71 | | // The predefinitions ('CDM') could break due to the ever growing code base. |
72 | | // Check for the expected invariants and see whether they apply. |
73 | | static std::optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV, |
74 | 6 | CheckerContext &C) { |
75 | 6 | auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>(); |
76 | 6 | if (!ReturnDV) |
77 | 0 | return std::nullopt; |
78 | | |
79 | 6 | if (ExpectedValue) |
80 | 6 | return C.getState()->isNull(*ReturnDV).isConstrainedTrue(); |
81 | | |
82 | 0 | return C.getState()->isNull(*ReturnDV).isConstrainedFalse(); |
83 | 6 | } |
84 | | |
85 | | void ReturnValueChecker::checkPostCall(const CallEvent &Call, |
86 | 219 | CheckerContext &C) const { |
87 | 219 | const bool *RawExpectedValue = CDM.lookup(Call); |
88 | 219 | if (!RawExpectedValue) |
89 | 215 | return; |
90 | | |
91 | 4 | SVal ReturnV = Call.getReturnValue(); |
92 | 4 | bool ExpectedValue = *RawExpectedValue; |
93 | 4 | std::optional<bool> IsInvariantBreak = |
94 | 4 | isInvariantBreak(ExpectedValue, ReturnV, C); |
95 | 4 | if (!IsInvariantBreak) |
96 | 0 | return; |
97 | | |
98 | | // If the invariant is broken it is reported by 'checkEndFunction()'. |
99 | 4 | if (*IsInvariantBreak) |
100 | 2 | return; |
101 | | |
102 | 2 | std::string Name = getName(Call); |
103 | 2 | const NoteTag *CallTag = C.getNoteTag( |
104 | 2 | [Name, ExpectedValue](PathSensitiveBugReport &) -> std::string { |
105 | 1 | SmallString<128> Msg; |
106 | 1 | llvm::raw_svector_ostream Out(Msg); |
107 | | |
108 | 1 | Out << '\'' << Name << "' returns " |
109 | 1 | << (ExpectedValue ? "true" : "false"0 ); |
110 | 1 | return std::string(Out.str()); |
111 | 1 | }, |
112 | 2 | /*IsPrunable=*/true); |
113 | | |
114 | 2 | ProgramStateRef State = C.getState(); |
115 | 2 | State = State->assume(ReturnV.castAs<DefinedOrUnknownSVal>(), ExpectedValue); |
116 | 2 | C.addTransition(State, CallTag); |
117 | 2 | } |
118 | | |
119 | | void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS, |
120 | 125 | CheckerContext &C) const { |
121 | 125 | if (!RS || !RS->getRetValue()34 ) |
122 | 91 | return; |
123 | | |
124 | | // We cannot get the caller in the top-frame. |
125 | 34 | const StackFrameContext *SFC = C.getStackFrame(); |
126 | 34 | if (C.getStackFrame()->inTopFrame()) |
127 | 25 | return; |
128 | | |
129 | 9 | ProgramStateRef State = C.getState(); |
130 | 9 | CallEventManager &CMgr = C.getStateManager().getCallEventManager(); |
131 | 9 | CallEventRef<> Call = CMgr.getCaller(SFC, State); |
132 | 9 | if (!Call) |
133 | 0 | return; |
134 | | |
135 | 9 | const bool *RawExpectedValue = CDM.lookup(*Call); |
136 | 9 | if (!RawExpectedValue) |
137 | 7 | return; |
138 | | |
139 | 2 | SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext()); |
140 | 2 | bool ExpectedValue = *RawExpectedValue; |
141 | 2 | std::optional<bool> IsInvariantBreak = |
142 | 2 | isInvariantBreak(ExpectedValue, ReturnV, C); |
143 | 2 | if (!IsInvariantBreak) |
144 | 0 | return; |
145 | | |
146 | | // If the invariant is appropriate it is reported by 'checkPostCall()'. |
147 | 2 | if (!*IsInvariantBreak) |
148 | 0 | return; |
149 | | |
150 | 2 | std::string Name = getName(*Call); |
151 | 2 | const NoteTag *CallTag = C.getNoteTag( |
152 | 2 | [Name, ExpectedValue](BugReport &BR) -> std::string { |
153 | 1 | SmallString<128> Msg; |
154 | 1 | llvm::raw_svector_ostream Out(Msg); |
155 | | |
156 | | // The following is swapped because the invariant is broken. |
157 | 1 | Out << '\'' << Name << "' returns " |
158 | 1 | << (ExpectedValue ? "false" : "true"0 ); |
159 | | |
160 | 1 | return std::string(Out.str()); |
161 | 1 | }, |
162 | 2 | /*IsPrunable=*/false); |
163 | | |
164 | 2 | C.addTransition(State, CallTag); |
165 | 2 | } |
166 | | |
167 | 42 | void ento::registerReturnValueChecker(CheckerManager &Mgr) { |
168 | 42 | Mgr.registerChecker<ReturnValueChecker>(); |
169 | 42 | } |
170 | | |
171 | 88 | bool ento::shouldRegisterReturnValueChecker(const CheckerManager &mgr) { |
172 | 88 | return true; |
173 | 88 | } |