clang 18.0.0git
ReturnValueChecker.cpp
Go to the documentation of this file.
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
20#include "llvm/ADT/SmallVector.h"
21#include <optional>
22
23using namespace clang;
24using namespace ento;
25
26namespace {
27class ReturnValueChecker : public Checker<check::PostCall, check::EndFunction> {
28public:
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
35private:
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
61static std::string getName(const CallEvent &Call) {
62 std::string Name;
63 if (const auto *MD = dyn_cast<CXXMethodDecl>(Call.getDecl()))
64 if (const CXXRecordDecl *RD = MD->getParent())
65 Name += RD->getNameAsString() + "::";
66
67 Name += Call.getCalleeIdentifier()->getName();
68 return Name;
69}
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.
73static std::optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV,
75 auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>();
76 if (!ReturnDV)
77 return std::nullopt;
78
79 if (ExpectedValue)
80 return C.getState()->isNull(*ReturnDV).isConstrainedTrue();
81
82 return C.getState()->isNull(*ReturnDV).isConstrainedFalse();
83}
84
85void ReturnValueChecker::checkPostCall(const CallEvent &Call,
86 CheckerContext &C) const {
87 const bool *RawExpectedValue = CDM.lookup(Call);
88 if (!RawExpectedValue)
89 return;
90
91 SVal ReturnV = Call.getReturnValue();
92 bool ExpectedValue = *RawExpectedValue;
93 std::optional<bool> IsInvariantBreak =
94 isInvariantBreak(ExpectedValue, ReturnV, C);
95 if (!IsInvariantBreak)
96 return;
97
98 // If the invariant is broken it is reported by 'checkEndFunction()'.
99 if (*IsInvariantBreak)
100 return;
101
102 std::string Name = getName(Call);
103 const NoteTag *CallTag = C.getNoteTag(
104 [Name, ExpectedValue](PathSensitiveBugReport &) -> std::string {
106 llvm::raw_svector_ostream Out(Msg);
107
108 Out << '\'' << Name << "' returns "
109 << (ExpectedValue ? "true" : "false");
110 return std::string(Out.str());
111 },
112 /*IsPrunable=*/true);
113
114 ProgramStateRef State = C.getState();
115 State = State->assume(ReturnV.castAs<DefinedOrUnknownSVal>(), ExpectedValue);
116 C.addTransition(State, CallTag);
117}
118
119void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS,
120 CheckerContext &C) const {
121 if (!RS || !RS->getRetValue())
122 return;
123
124 // We cannot get the caller in the top-frame.
125 const StackFrameContext *SFC = C.getStackFrame();
126 if (C.getStackFrame()->inTopFrame())
127 return;
128
129 ProgramStateRef State = C.getState();
130 CallEventManager &CMgr = C.getStateManager().getCallEventManager();
131 CallEventRef<> Call = CMgr.getCaller(SFC, State);
132 if (!Call)
133 return;
134
135 const bool *RawExpectedValue = CDM.lookup(*Call);
136 if (!RawExpectedValue)
137 return;
138
139 SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext());
140 bool ExpectedValue = *RawExpectedValue;
141 std::optional<bool> IsInvariantBreak =
142 isInvariantBreak(ExpectedValue, ReturnV, C);
143 if (!IsInvariantBreak)
144 return;
145
146 // If the invariant is appropriate it is reported by 'checkPostCall()'.
147 if (!*IsInvariantBreak)
148 return;
149
150 std::string Name = getName(*Call);
151 const NoteTag *CallTag = C.getNoteTag(
152 [Name, ExpectedValue](BugReport &BR) -> std::string {
154 llvm::raw_svector_ostream Out(Msg);
155
156 // The following is swapped because the invariant is broken.
157 Out << '\'' << Name << "' returns "
158 << (ExpectedValue ? "false" : "true");
159
160 return std::string(Out.str());
161 },
162 /*IsPrunable=*/false);
163
164 C.addTransition(State, CallTag);
165}
166
167void ento::registerReturnValueChecker(CheckerManager &Mgr) {
168 Mgr.registerChecker<ReturnValueChecker>();
169}
170
171bool ento::shouldRegisterReturnValueChecker(const CheckerManager &mgr) {
172 return true;
173}
static std::string getName(const CallEvent &Call)
static std::optional< bool > isInvariantBreak(bool ExpectedValue, SVal ReturnV, CheckerContext &C)
Represents a C++ struct/union/class.
Definition: DeclCXX.h:258
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition: DeclBase.h:2065
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
Definition: Stmt.h:3013
Expr * getRetValue()
Definition: Stmt.h:3044
It represents a stack frame of the call stack (based on CallEvent).
This class provides an interface through which checkers can create individual bug reports.
Definition: BugReporter.h:118
An immutable map from CallDescriptions to arbitrary data.
Manages the lifetime of CallEvent objects.
Definition: CallEvent.h:1284
CallEventRef getCaller(const StackFrameContext *CalleeCtx, ProgramStateRef State)
Gets an outside caller given a callee context.
Definition: CallEvent.cpp:1425
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:152
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
The tag upon which the TagVisitor reacts.
Definition: BugReporter.h:763
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition: SVals.h:55
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Definition: SVals.h:86
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition: SVals.h:82