diff options
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp new file mode 100644 index 000000000000..103208d8b5a5 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp @@ -0,0 +1,170 @@ +//===- 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<check::PostCall, check::EndFunction> { +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<bool> 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<CXXMethodDecl>(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<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV, + CheckerContext &C) { + auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>(); + 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<bool> 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<DefinedOrUnknownSVal>(), 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<bool> 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<ReturnValueChecker>(); +} + +bool ento::shouldRegisterReturnValueChecker(const LangOptions &LO) { + return true; +} |