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