//===- ReturnValueChecker - Applies guaranteed return values ----*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This defines ReturnValueChecker, which checks for calls with guaranteed // boolean return value. It ensures the return value of each function call. // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" using namespace clang; using namespace ento; namespace { class ReturnValueChecker : public Checker { public: // It sets the predefined invariant ('CDM') if the current call not break it. void checkPostCall(const CallEvent &Call, CheckerContext &C) const; // It reports whether a predefined invariant ('CDM') is broken. void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; private: // The pairs are in the following form: {{{class, call}}, return value} const CallDescriptionMap CDM = { // These are known in the LLVM project: 'Error()' {{{"ARMAsmParser", "Error"}}, true}, {{{"HexagonAsmParser", "Error"}}, true}, {{{"LLLexer", "Error"}}, true}, {{{"LLParser", "Error"}}, true}, {{{"MCAsmParser", "Error"}}, true}, {{{"MCAsmParserExtension", "Error"}}, true}, {{{"TGParser", "Error"}}, true}, {{{"X86AsmParser", "Error"}}, true}, // 'TokError()' {{{"LLParser", "TokError"}}, true}, {{{"MCAsmParser", "TokError"}}, true}, {{{"MCAsmParserExtension", "TokError"}}, true}, {{{"TGParser", "TokError"}}, true}, // 'error()' {{{"MIParser", "error"}}, true}, {{{"WasmAsmParser", "error"}}, true}, {{{"WebAssemblyAsmParser", "error"}}, true}, // Other {{{"AsmParser", "printError"}}, true}}; }; } // namespace static std::string getName(const CallEvent &Call) { std::string Name = ""; if (const auto *MD = dyn_cast(Call.getDecl())) if (const CXXRecordDecl *RD = MD->getParent()) Name += RD->getNameAsString() + "::"; Name += Call.getCalleeIdentifier()->getName(); return Name; } // The predefinitions ('CDM') could break due to the ever growing code base. // Check for the expected invariants and see whether they apply. static Optional isInvariantBreak(bool ExpectedValue, SVal ReturnV, CheckerContext &C) { auto ReturnDV = ReturnV.getAs(); if (!ReturnDV) return None; if (ExpectedValue) return C.getState()->isNull(*ReturnDV).isConstrainedTrue(); return C.getState()->isNull(*ReturnDV).isConstrainedFalse(); } void ReturnValueChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { const bool *RawExpectedValue = CDM.lookup(Call); if (!RawExpectedValue) return; SVal ReturnV = Call.getReturnValue(); bool ExpectedValue = *RawExpectedValue; Optional IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C); if (!IsInvariantBreak) return; // If the invariant is broken it is reported by 'checkEndFunction()'. if (*IsInvariantBreak) return; std::string Name = getName(Call); const NoteTag *CallTag = C.getNoteTag( [Name, ExpectedValue](BugReport &) -> std::string { SmallString<128> Msg; llvm::raw_svector_ostream Out(Msg); Out << '\'' << Name << "' returns " << (ExpectedValue ? "true" : "false"); return Out.str(); }, /*IsPrunable=*/true); ProgramStateRef State = C.getState(); State = State->assume(ReturnV.castAs(), ExpectedValue); C.addTransition(State, CallTag); } void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const { if (!RS || !RS->getRetValue()) return; // We cannot get the caller in the top-frame. const StackFrameContext *SFC = C.getStackFrame(); if (C.getStackFrame()->inTopFrame()) return; ProgramStateRef State = C.getState(); CallEventManager &CMgr = C.getStateManager().getCallEventManager(); CallEventRef<> Call = CMgr.getCaller(SFC, State); if (!Call) return; const bool *RawExpectedValue = CDM.lookup(*Call); if (!RawExpectedValue) return; SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext()); bool ExpectedValue = *RawExpectedValue; Optional IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C); if (!IsInvariantBreak) return; // If the invariant is appropriate it is reported by 'checkPostCall()'. if (!*IsInvariantBreak) return; std::string Name = getName(*Call); const NoteTag *CallTag = C.getNoteTag( [Name, ExpectedValue](BugReport &BR) -> std::string { SmallString<128> Msg; llvm::raw_svector_ostream Out(Msg); // The following is swapped because the invariant is broken. Out << '\'' << Name << "' returns " << (ExpectedValue ? "false" : "true"); return Out.str(); }, /*IsPrunable=*/false); C.addTransition(State, CallTag); } void ento::registerReturnValueChecker(CheckerManager &Mgr) { Mgr.registerChecker(); } bool ento::shouldRegisterReturnValueChecker(const LangOptions &LO) { return true; }