aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp')
-rw-r--r--lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp170
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;
+}