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