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/CallEvent.h" 18 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19 #include "llvm/ADT/Optional.h" 20 #include "llvm/ADT/SmallVector.h" 21 22 using namespace clang; 23 using namespace ento; 24 25 namespace { 26 class ReturnValueChecker : public Checker<check::PostCall, check::EndFunction> { 27 public: 28 // It sets the predefined invariant ('CDM') if the current call not break it. 29 void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 30 31 // It reports whether a predefined invariant ('CDM') is broken. 32 void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; 33 34 private: 35 // The pairs are in the following form: {{{class, call}}, return value} 36 const CallDescriptionMap<bool> CDM = { 37 // These are known in the LLVM project: 'Error()' 38 {{{"ARMAsmParser", "Error"}}, true}, 39 {{{"HexagonAsmParser", "Error"}}, true}, 40 {{{"LLLexer", "Error"}}, true}, 41 {{{"LLParser", "Error"}}, true}, 42 {{{"MCAsmParser", "Error"}}, true}, 43 {{{"MCAsmParserExtension", "Error"}}, true}, 44 {{{"TGParser", "Error"}}, true}, 45 {{{"X86AsmParser", "Error"}}, true}, 46 // 'TokError()' 47 {{{"LLParser", "TokError"}}, true}, 48 {{{"MCAsmParser", "TokError"}}, true}, 49 {{{"MCAsmParserExtension", "TokError"}}, true}, 50 {{{"TGParser", "TokError"}}, true}, 51 // 'error()' 52 {{{"MIParser", "error"}}, true}, 53 {{{"WasmAsmParser", "error"}}, true}, 54 {{{"WebAssemblyAsmParser", "error"}}, true}, 55 // Other 56 {{{"AsmParser", "printError"}}, true}}; 57 }; 58 } // namespace 59 60 static std::string getName(const CallEvent &Call) { 61 std::string Name = ""; 62 if (const auto *MD = dyn_cast<CXXMethodDecl>(Call.getDecl())) 63 if (const CXXRecordDecl *RD = MD->getParent()) 64 Name += RD->getNameAsString() + "::"; 65 66 Name += Call.getCalleeIdentifier()->getName(); 67 return Name; 68 } 69 70 // The predefinitions ('CDM') could break due to the ever growing code base. 71 // Check for the expected invariants and see whether they apply. 72 static Optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV, 73 CheckerContext &C) { 74 auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>(); 75 if (!ReturnDV) 76 return None; 77 78 if (ExpectedValue) 79 return C.getState()->isNull(*ReturnDV).isConstrainedTrue(); 80 81 return C.getState()->isNull(*ReturnDV).isConstrainedFalse(); 82 } 83 84 void ReturnValueChecker::checkPostCall(const CallEvent &Call, 85 CheckerContext &C) const { 86 const bool *RawExpectedValue = CDM.lookup(Call); 87 if (!RawExpectedValue) 88 return; 89 90 SVal ReturnV = Call.getReturnValue(); 91 bool ExpectedValue = *RawExpectedValue; 92 Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C); 93 if (!IsInvariantBreak) 94 return; 95 96 // If the invariant is broken it is reported by 'checkEndFunction()'. 97 if (*IsInvariantBreak) 98 return; 99 100 std::string Name = getName(Call); 101 const NoteTag *CallTag = C.getNoteTag( 102 [Name, ExpectedValue](BugReport &) -> std::string { 103 SmallString<128> Msg; 104 llvm::raw_svector_ostream Out(Msg); 105 106 Out << '\'' << Name << "' returns " 107 << (ExpectedValue ? "true" : "false"); 108 return Out.str(); 109 }, 110 /*IsPrunable=*/true); 111 112 ProgramStateRef State = C.getState(); 113 State = State->assume(ReturnV.castAs<DefinedOrUnknownSVal>(), ExpectedValue); 114 C.addTransition(State, CallTag); 115 } 116 117 void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS, 118 CheckerContext &C) const { 119 if (!RS || !RS->getRetValue()) 120 return; 121 122 // We cannot get the caller in the top-frame. 123 const StackFrameContext *SFC = C.getStackFrame(); 124 if (C.getStackFrame()->inTopFrame()) 125 return; 126 127 ProgramStateRef State = C.getState(); 128 CallEventManager &CMgr = C.getStateManager().getCallEventManager(); 129 CallEventRef<> Call = CMgr.getCaller(SFC, State); 130 if (!Call) 131 return; 132 133 const bool *RawExpectedValue = CDM.lookup(*Call); 134 if (!RawExpectedValue) 135 return; 136 137 SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext()); 138 bool ExpectedValue = *RawExpectedValue; 139 Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C); 140 if (!IsInvariantBreak) 141 return; 142 143 // If the invariant is appropriate it is reported by 'checkPostCall()'. 144 if (!*IsInvariantBreak) 145 return; 146 147 std::string Name = getName(*Call); 148 const NoteTag *CallTag = C.getNoteTag( 149 [Name, ExpectedValue](BugReport &BR) -> std::string { 150 SmallString<128> Msg; 151 llvm::raw_svector_ostream Out(Msg); 152 153 // The following is swapped because the invariant is broken. 154 Out << '\'' << Name << "' returns " 155 << (ExpectedValue ? "false" : "true"); 156 157 return Out.str(); 158 }, 159 /*IsPrunable=*/false); 160 161 C.addTransition(State, CallTag); 162 } 163 164 void ento::registerReturnValueChecker(CheckerManager &Mgr) { 165 Mgr.registerChecker<ReturnValueChecker>(); 166 } 167 168 bool ento::shouldRegisterReturnValueChecker(const LangOptions &LO) { 169 return true; 170 } 171