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