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/SmallVector.h" 21 #include <optional> 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 std::optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV, 74 CheckerContext &C) { 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 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 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 { 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 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 { 153 SmallString<128> Msg; 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 167 void ento::registerReturnValueChecker(CheckerManager &Mgr) { 168 Mgr.registerChecker<ReturnValueChecker>(); 169 } 170 171 bool ento::shouldRegisterReturnValueChecker(const CheckerManager &mgr) { 172 return true; 173 } 174