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