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