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 
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 static 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.
73 static Optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV,
74                                        CheckerContext &C) {
75   auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>();
76   if (!ReturnDV)
77     return None;
78 
79   if (ExpectedValue)
80     return C.getState()->isNull(*ReturnDV).isConstrainedTrue();
81 
82   return C.getState()->isNull(*ReturnDV).isConstrainedFalse();
83 }
84 
85 void 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   Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
94   if (!IsInvariantBreak)
95     return;
96 
97   // If the invariant is broken it is reported by 'checkEndFunction()'.
98   if (*IsInvariantBreak)
99     return;
100 
101   std::string Name = getName(Call);
102   const NoteTag *CallTag = C.getNoteTag(
103       [Name, ExpectedValue](PathSensitiveBugReport &) -> std::string {
104         SmallString<128> Msg;
105         llvm::raw_svector_ostream Out(Msg);
106 
107         Out << '\'' << Name << "' returns "
108             << (ExpectedValue ? "true" : "false");
109         return std::string(Out.str());
110       },
111       /*IsPrunable=*/true);
112 
113   ProgramStateRef State = C.getState();
114   State = State->assume(ReturnV.castAs<DefinedOrUnknownSVal>(), ExpectedValue);
115   C.addTransition(State, CallTag);
116 }
117 
118 void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS,
119                                           CheckerContext &C) const {
120   if (!RS || !RS->getRetValue())
121     return;
122 
123   // We cannot get the caller in the top-frame.
124   const StackFrameContext *SFC = C.getStackFrame();
125   if (C.getStackFrame()->inTopFrame())
126     return;
127 
128   ProgramStateRef State = C.getState();
129   CallEventManager &CMgr = C.getStateManager().getCallEventManager();
130   CallEventRef<> Call = CMgr.getCaller(SFC, State);
131   if (!Call)
132     return;
133 
134   const bool *RawExpectedValue = CDM.lookup(*Call);
135   if (!RawExpectedValue)
136     return;
137 
138   SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext());
139   bool ExpectedValue = *RawExpectedValue;
140   Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
141   if (!IsInvariantBreak)
142     return;
143 
144   // If the invariant is appropriate it is reported by 'checkPostCall()'.
145   if (!*IsInvariantBreak)
146     return;
147 
148   std::string Name = getName(*Call);
149   const NoteTag *CallTag = C.getNoteTag(
150       [Name, ExpectedValue](BugReport &BR) -> std::string {
151         SmallString<128> Msg;
152         llvm::raw_svector_ostream Out(Msg);
153 
154         // The following is swapped because the invariant is broken.
155         Out << '\'' << Name << "' returns "
156             << (ExpectedValue ? "false" : "true");
157 
158         return std::string(Out.str());
159       },
160       /*IsPrunable=*/false);
161 
162   C.addTransition(State, CallTag);
163 }
164 
165 void ento::registerReturnValueChecker(CheckerManager &Mgr) {
166   Mgr.registerChecker<ReturnValueChecker>();
167 }
168 
169 bool ento::shouldRegisterReturnValueChecker(const CheckerManager &mgr) {
170   return true;
171 }
172