xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp (revision a1580d7b59b65b17f2ce7fdb95f46379e7df4089)
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/Optional.h"
21 #include "llvm/ADT/SmallVector.h"
22 #include <optional>
23 
24 using namespace clang;
25 using namespace ento;
26 
27 namespace {
28 class ReturnValueChecker : public Checker<check::PostCall, check::EndFunction> {
29 public:
30   // It sets the predefined invariant ('CDM') if the current call not break it.
31   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
32 
33   // It reports whether a predefined invariant ('CDM') is broken.
34   void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
35 
36 private:
37   // The pairs are in the following form: {{{class, call}}, return value}
38   const CallDescriptionMap<bool> CDM = {
39       // These are known in the LLVM project: 'Error()'
40       {{{"ARMAsmParser", "Error"}}, true},
41       {{{"HexagonAsmParser", "Error"}}, true},
42       {{{"LLLexer", "Error"}}, true},
43       {{{"LLParser", "Error"}}, true},
44       {{{"MCAsmParser", "Error"}}, true},
45       {{{"MCAsmParserExtension", "Error"}}, true},
46       {{{"TGParser", "Error"}}, true},
47       {{{"X86AsmParser", "Error"}}, true},
48       // 'TokError()'
49       {{{"LLParser", "TokError"}}, true},
50       {{{"MCAsmParser", "TokError"}}, true},
51       {{{"MCAsmParserExtension", "TokError"}}, true},
52       {{{"TGParser", "TokError"}}, true},
53       // 'error()'
54       {{{"MIParser", "error"}}, true},
55       {{{"WasmAsmParser", "error"}}, true},
56       {{{"WebAssemblyAsmParser", "error"}}, true},
57       // Other
58       {{{"AsmParser", "printError"}}, true}};
59 };
60 } // namespace
61 
62 static std::string getName(const CallEvent &Call) {
63   std::string Name;
64   if (const auto *MD = dyn_cast<CXXMethodDecl>(Call.getDecl()))
65     if (const CXXRecordDecl *RD = MD->getParent())
66       Name += RD->getNameAsString() + "::";
67 
68   Name += Call.getCalleeIdentifier()->getName();
69   return Name;
70 }
71 
72 // The predefinitions ('CDM') could break due to the ever growing code base.
73 // Check for the expected invariants and see whether they apply.
74 static Optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV,
75                                        CheckerContext &C) {
76   auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>();
77   if (!ReturnDV)
78     return std::nullopt;
79 
80   if (ExpectedValue)
81     return C.getState()->isNull(*ReturnDV).isConstrainedTrue();
82 
83   return C.getState()->isNull(*ReturnDV).isConstrainedFalse();
84 }
85 
86 void ReturnValueChecker::checkPostCall(const CallEvent &Call,
87                                        CheckerContext &C) const {
88   const bool *RawExpectedValue = CDM.lookup(Call);
89   if (!RawExpectedValue)
90     return;
91 
92   SVal ReturnV = Call.getReturnValue();
93   bool ExpectedValue = *RawExpectedValue;
94   Optional<bool> IsInvariantBreak = 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 {
105         SmallString<128> Msg;
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 
119 void 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   Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
142   if (!IsInvariantBreak)
143     return;
144 
145   // If the invariant is appropriate it is reported by 'checkPostCall()'.
146   if (!*IsInvariantBreak)
147     return;
148 
149   std::string Name = getName(*Call);
150   const NoteTag *CallTag = C.getNoteTag(
151       [Name, ExpectedValue](BugReport &BR) -> std::string {
152         SmallString<128> Msg;
153         llvm::raw_svector_ostream Out(Msg);
154 
155         // The following is swapped because the invariant is broken.
156         Out << '\'' << Name << "' returns "
157             << (ExpectedValue ? "false" : "true");
158 
159         return std::string(Out.str());
160       },
161       /*IsPrunable=*/false);
162 
163   C.addTransition(State, CallTag);
164 }
165 
166 void ento::registerReturnValueChecker(CheckerManager &Mgr) {
167   Mgr.registerChecker<ReturnValueChecker>();
168 }
169 
170 bool ento::shouldRegisterReturnValueChecker(const CheckerManager &mgr) {
171   return true;
172 }
173