157835bcfSCsaba Dabis //===- ReturnValueChecker - Applies guaranteed return values ----*- C++ -*-===//
257835bcfSCsaba Dabis //
357835bcfSCsaba Dabis // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
457835bcfSCsaba Dabis // See https://llvm.org/LICENSE.txt for license information.
557835bcfSCsaba Dabis // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
657835bcfSCsaba Dabis //
757835bcfSCsaba Dabis //===----------------------------------------------------------------------===//
857835bcfSCsaba Dabis //
957835bcfSCsaba Dabis // This defines ReturnValueChecker, which checks for calls with guaranteed
1057835bcfSCsaba Dabis // boolean return value. It ensures the return value of each function call.
1157835bcfSCsaba Dabis //
1257835bcfSCsaba Dabis //===----------------------------------------------------------------------===//
1357835bcfSCsaba Dabis
1457835bcfSCsaba Dabis #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
1557835bcfSCsaba Dabis #include "clang/StaticAnalyzer/Core/Checker.h"
1657835bcfSCsaba Dabis #include "clang/StaticAnalyzer/Core/CheckerManager.h"
170b9d3a6eSBalazs Benics #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
1857835bcfSCsaba Dabis #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
1957835bcfSCsaba Dabis #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
2057835bcfSCsaba Dabis #include "llvm/ADT/Optional.h"
2157835bcfSCsaba Dabis #include "llvm/ADT/SmallVector.h"
2257835bcfSCsaba Dabis
2357835bcfSCsaba Dabis using namespace clang;
2457835bcfSCsaba Dabis using namespace ento;
2557835bcfSCsaba Dabis
2657835bcfSCsaba Dabis namespace {
2757835bcfSCsaba Dabis class ReturnValueChecker : public Checker<check::PostCall, check::EndFunction> {
2857835bcfSCsaba Dabis public:
2957835bcfSCsaba Dabis // It sets the predefined invariant ('CDM') if the current call not break it.
3057835bcfSCsaba Dabis void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
3157835bcfSCsaba Dabis
3257835bcfSCsaba Dabis // It reports whether a predefined invariant ('CDM') is broken.
3357835bcfSCsaba Dabis void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
3457835bcfSCsaba Dabis
3557835bcfSCsaba Dabis private:
3657835bcfSCsaba Dabis // The pairs are in the following form: {{{class, call}}, return value}
3757835bcfSCsaba Dabis const CallDescriptionMap<bool> CDM = {
3857835bcfSCsaba Dabis // These are known in the LLVM project: 'Error()'
3957835bcfSCsaba Dabis {{{"ARMAsmParser", "Error"}}, true},
4057835bcfSCsaba Dabis {{{"HexagonAsmParser", "Error"}}, true},
4157835bcfSCsaba Dabis {{{"LLLexer", "Error"}}, true},
4257835bcfSCsaba Dabis {{{"LLParser", "Error"}}, true},
4357835bcfSCsaba Dabis {{{"MCAsmParser", "Error"}}, true},
4457835bcfSCsaba Dabis {{{"MCAsmParserExtension", "Error"}}, true},
4557835bcfSCsaba Dabis {{{"TGParser", "Error"}}, true},
4657835bcfSCsaba Dabis {{{"X86AsmParser", "Error"}}, true},
4757835bcfSCsaba Dabis // 'TokError()'
4857835bcfSCsaba Dabis {{{"LLParser", "TokError"}}, true},
4957835bcfSCsaba Dabis {{{"MCAsmParser", "TokError"}}, true},
5057835bcfSCsaba Dabis {{{"MCAsmParserExtension", "TokError"}}, true},
5157835bcfSCsaba Dabis {{{"TGParser", "TokError"}}, true},
5257835bcfSCsaba Dabis // 'error()'
5357835bcfSCsaba Dabis {{{"MIParser", "error"}}, true},
5457835bcfSCsaba Dabis {{{"WasmAsmParser", "error"}}, true},
5557835bcfSCsaba Dabis {{{"WebAssemblyAsmParser", "error"}}, true},
5657835bcfSCsaba Dabis // Other
5757835bcfSCsaba Dabis {{{"AsmParser", "printError"}}, true}};
5857835bcfSCsaba Dabis };
5957835bcfSCsaba Dabis } // namespace
6057835bcfSCsaba Dabis
getName(const CallEvent & Call)6157835bcfSCsaba Dabis static std::string getName(const CallEvent &Call) {
62*0542d152SKazu Hirata std::string Name;
6357835bcfSCsaba Dabis if (const auto *MD = dyn_cast<CXXMethodDecl>(Call.getDecl()))
6457835bcfSCsaba Dabis if (const CXXRecordDecl *RD = MD->getParent())
6557835bcfSCsaba Dabis Name += RD->getNameAsString() + "::";
6657835bcfSCsaba Dabis
6757835bcfSCsaba Dabis Name += Call.getCalleeIdentifier()->getName();
6857835bcfSCsaba Dabis return Name;
6957835bcfSCsaba Dabis }
7057835bcfSCsaba Dabis
7157835bcfSCsaba Dabis // The predefinitions ('CDM') could break due to the ever growing code base.
7257835bcfSCsaba Dabis // Check for the expected invariants and see whether they apply.
isInvariantBreak(bool ExpectedValue,SVal ReturnV,CheckerContext & C)7357835bcfSCsaba Dabis static Optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV,
7457835bcfSCsaba Dabis CheckerContext &C) {
7557835bcfSCsaba Dabis auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>();
7657835bcfSCsaba Dabis if (!ReturnDV)
7757835bcfSCsaba Dabis return None;
7857835bcfSCsaba Dabis
7957835bcfSCsaba Dabis if (ExpectedValue)
8057835bcfSCsaba Dabis return C.getState()->isNull(*ReturnDV).isConstrainedTrue();
8157835bcfSCsaba Dabis
8257835bcfSCsaba Dabis return C.getState()->isNull(*ReturnDV).isConstrainedFalse();
8357835bcfSCsaba Dabis }
8457835bcfSCsaba Dabis
checkPostCall(const CallEvent & Call,CheckerContext & C) const8557835bcfSCsaba Dabis void ReturnValueChecker::checkPostCall(const CallEvent &Call,
8657835bcfSCsaba Dabis CheckerContext &C) const {
8757835bcfSCsaba Dabis const bool *RawExpectedValue = CDM.lookup(Call);
8857835bcfSCsaba Dabis if (!RawExpectedValue)
8957835bcfSCsaba Dabis return;
9057835bcfSCsaba Dabis
9157835bcfSCsaba Dabis SVal ReturnV = Call.getReturnValue();
9257835bcfSCsaba Dabis bool ExpectedValue = *RawExpectedValue;
9357835bcfSCsaba Dabis Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
9457835bcfSCsaba Dabis if (!IsInvariantBreak)
9557835bcfSCsaba Dabis return;
9657835bcfSCsaba Dabis
9757835bcfSCsaba Dabis // If the invariant is broken it is reported by 'checkEndFunction()'.
9857835bcfSCsaba Dabis if (*IsInvariantBreak)
9957835bcfSCsaba Dabis return;
10057835bcfSCsaba Dabis
10157835bcfSCsaba Dabis std::string Name = getName(Call);
10257835bcfSCsaba Dabis const NoteTag *CallTag = C.getNoteTag(
10320a3d64cSAdam Balogh [Name, ExpectedValue](PathSensitiveBugReport &) -> std::string {
10457835bcfSCsaba Dabis SmallString<128> Msg;
10557835bcfSCsaba Dabis llvm::raw_svector_ostream Out(Msg);
10657835bcfSCsaba Dabis
10757835bcfSCsaba Dabis Out << '\'' << Name << "' returns "
10857835bcfSCsaba Dabis << (ExpectedValue ? "true" : "false");
109adcd0268SBenjamin Kramer return std::string(Out.str());
11057835bcfSCsaba Dabis },
11157835bcfSCsaba Dabis /*IsPrunable=*/true);
11257835bcfSCsaba Dabis
11357835bcfSCsaba Dabis ProgramStateRef State = C.getState();
11457835bcfSCsaba Dabis State = State->assume(ReturnV.castAs<DefinedOrUnknownSVal>(), ExpectedValue);
11557835bcfSCsaba Dabis C.addTransition(State, CallTag);
11657835bcfSCsaba Dabis }
11757835bcfSCsaba Dabis
checkEndFunction(const ReturnStmt * RS,CheckerContext & C) const11857835bcfSCsaba Dabis void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS,
11957835bcfSCsaba Dabis CheckerContext &C) const {
12057835bcfSCsaba Dabis if (!RS || !RS->getRetValue())
12157835bcfSCsaba Dabis return;
12257835bcfSCsaba Dabis
12357835bcfSCsaba Dabis // We cannot get the caller in the top-frame.
12457835bcfSCsaba Dabis const StackFrameContext *SFC = C.getStackFrame();
12557835bcfSCsaba Dabis if (C.getStackFrame()->inTopFrame())
12657835bcfSCsaba Dabis return;
12757835bcfSCsaba Dabis
12857835bcfSCsaba Dabis ProgramStateRef State = C.getState();
12957835bcfSCsaba Dabis CallEventManager &CMgr = C.getStateManager().getCallEventManager();
13057835bcfSCsaba Dabis CallEventRef<> Call = CMgr.getCaller(SFC, State);
13157835bcfSCsaba Dabis if (!Call)
13257835bcfSCsaba Dabis return;
13357835bcfSCsaba Dabis
13457835bcfSCsaba Dabis const bool *RawExpectedValue = CDM.lookup(*Call);
13557835bcfSCsaba Dabis if (!RawExpectedValue)
13657835bcfSCsaba Dabis return;
13757835bcfSCsaba Dabis
13857835bcfSCsaba Dabis SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext());
13957835bcfSCsaba Dabis bool ExpectedValue = *RawExpectedValue;
14057835bcfSCsaba Dabis Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
14157835bcfSCsaba Dabis if (!IsInvariantBreak)
14257835bcfSCsaba Dabis return;
14357835bcfSCsaba Dabis
14457835bcfSCsaba Dabis // If the invariant is appropriate it is reported by 'checkPostCall()'.
14557835bcfSCsaba Dabis if (!*IsInvariantBreak)
14657835bcfSCsaba Dabis return;
14757835bcfSCsaba Dabis
14857835bcfSCsaba Dabis std::string Name = getName(*Call);
14957835bcfSCsaba Dabis const NoteTag *CallTag = C.getNoteTag(
15057835bcfSCsaba Dabis [Name, ExpectedValue](BugReport &BR) -> std::string {
15157835bcfSCsaba Dabis SmallString<128> Msg;
15257835bcfSCsaba Dabis llvm::raw_svector_ostream Out(Msg);
15357835bcfSCsaba Dabis
15457835bcfSCsaba Dabis // The following is swapped because the invariant is broken.
15557835bcfSCsaba Dabis Out << '\'' << Name << "' returns "
15657835bcfSCsaba Dabis << (ExpectedValue ? "false" : "true");
15757835bcfSCsaba Dabis
158adcd0268SBenjamin Kramer return std::string(Out.str());
15957835bcfSCsaba Dabis },
16057835bcfSCsaba Dabis /*IsPrunable=*/false);
16157835bcfSCsaba Dabis
16257835bcfSCsaba Dabis C.addTransition(State, CallTag);
16357835bcfSCsaba Dabis }
16457835bcfSCsaba Dabis
registerReturnValueChecker(CheckerManager & Mgr)16557835bcfSCsaba Dabis void ento::registerReturnValueChecker(CheckerManager &Mgr) {
16657835bcfSCsaba Dabis Mgr.registerChecker<ReturnValueChecker>();
16757835bcfSCsaba Dabis }
16857835bcfSCsaba Dabis
shouldRegisterReturnValueChecker(const CheckerManager & mgr)169bda3dd0dSKirstóf Umann bool ento::shouldRegisterReturnValueChecker(const CheckerManager &mgr) {
17057835bcfSCsaba Dabis return true;
17157835bcfSCsaba Dabis }
172