Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
35269 views
//==- ExprInspectionChecker.cpp - Used for regression tests ------*- C++ -*-==//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "clang/Analysis/IssueHash.h"9#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"10#include "clang/StaticAnalyzer/Checkers/SValExplainer.h"11#include "clang/StaticAnalyzer/Checkers/Taint.h"12#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"13#include "clang/StaticAnalyzer/Core/Checker.h"14#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"15#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"16#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"17#include "llvm/ADT/StringSwitch.h"18#include "llvm/Support/ScopedPrinter.h"19#include <optional>2021using namespace clang;22using namespace ento;2324namespace {25class ExprInspectionChecker26: public Checker<eval::Call, check::DeadSymbols, check::EndAnalysis> {27const BugType BT{this, "Checking analyzer assumptions", "debug"};2829// These stats are per-analysis, not per-branch, hence they shouldn't30// stay inside the program state.31struct ReachedStat {32ExplodedNode *ExampleNode;33unsigned NumTimesReached;34};35mutable llvm::DenseMap<const CallExpr *, ReachedStat> ReachedStats;3637void analyzerEval(const CallExpr *CE, CheckerContext &C) const;38void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const;39void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const;40void analyzerNumTimesReached(const CallExpr *CE, CheckerContext &C) const;41void analyzerCrash(const CallExpr *CE, CheckerContext &C) const;42void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const;43void analyzerValue(const CallExpr *CE, CheckerContext &C) const;44void analyzerDumpSValType(const CallExpr *CE, CheckerContext &C) const;45void analyzerDump(const CallExpr *CE, CheckerContext &C) const;46void analyzerExplain(const CallExpr *CE, CheckerContext &C) const;47void analyzerPrintState(const CallExpr *CE, CheckerContext &C) const;48void analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const;49void analyzerDumpExtent(const CallExpr *CE, CheckerContext &C) const;50void analyzerDumpElementCount(const CallExpr *CE, CheckerContext &C) const;51void analyzerHashDump(const CallExpr *CE, CheckerContext &C) const;52void analyzerDenote(const CallExpr *CE, CheckerContext &C) const;53void analyzerExpress(const CallExpr *CE, CheckerContext &C) const;54void analyzerIsTainted(const CallExpr *CE, CheckerContext &C) const;5556typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,57CheckerContext &C) const;5859// Optional parameter `ExprVal` for expression value to be marked interesting.60ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C,61std::optional<SVal> ExprVal = std::nullopt) const;62ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, ExplodedNode *N,63std::optional<SVal> ExprVal = std::nullopt) const;64template <typename T> void printAndReport(CheckerContext &C, T What) const;6566const Expr *getArgExpr(const CallExpr *CE, CheckerContext &C) const;67const MemRegion *getArgRegion(const CallExpr *CE, CheckerContext &C) const;6869public:70bool evalCall(const CallEvent &Call, CheckerContext &C) const;71void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;72void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,73ExprEngine &Eng) const;74};75} // namespace7677REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef)78REGISTER_MAP_WITH_PROGRAMSTATE(DenotedSymbols, SymbolRef, const StringLiteral *)7980bool ExprInspectionChecker::evalCall(const CallEvent &Call,81CheckerContext &C) const {82const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());83if (!CE)84return false;8586// These checks should have no effect on the surrounding environment87// (globals should not be invalidated, etc), hence the use of evalCall.88FnCheck Handler =89llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))90.Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)91.Case("clang_analyzer_checkInlined",92&ExprInspectionChecker::analyzerCheckInlined)93.Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)94.Case("clang_analyzer_warnIfReached",95&ExprInspectionChecker::analyzerWarnIfReached)96.Case("clang_analyzer_warnOnDeadSymbol",97&ExprInspectionChecker::analyzerWarnOnDeadSymbol)98.StartsWith("clang_analyzer_explain",99&ExprInspectionChecker::analyzerExplain)100.Case("clang_analyzer_dumpExtent",101&ExprInspectionChecker::analyzerDumpExtent)102.Case("clang_analyzer_dumpElementCount",103&ExprInspectionChecker::analyzerDumpElementCount)104.Case("clang_analyzer_value", &ExprInspectionChecker::analyzerValue)105.StartsWith("clang_analyzer_dumpSvalType",106&ExprInspectionChecker::analyzerDumpSValType)107.StartsWith("clang_analyzer_dump",108&ExprInspectionChecker::analyzerDump)109.Case("clang_analyzer_getExtent",110&ExprInspectionChecker::analyzerGetExtent)111.Case("clang_analyzer_printState",112&ExprInspectionChecker::analyzerPrintState)113.Case("clang_analyzer_numTimesReached",114&ExprInspectionChecker::analyzerNumTimesReached)115.Case("clang_analyzer_hashDump",116&ExprInspectionChecker::analyzerHashDump)117.Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote)118.Case("clang_analyzer_express", // This also marks the argument as119// interesting.120&ExprInspectionChecker::analyzerExpress)121.StartsWith("clang_analyzer_isTainted",122&ExprInspectionChecker::analyzerIsTainted)123.Default(nullptr);124125if (!Handler)126return false;127128(this->*Handler)(CE, C);129return true;130}131132static const char *getArgumentValueString(const CallExpr *CE,133CheckerContext &C) {134if (CE->getNumArgs() == 0)135return "Missing assertion argument";136137ExplodedNode *N = C.getPredecessor();138const LocationContext *LC = N->getLocationContext();139ProgramStateRef State = N->getState();140141const Expr *Assertion = CE->getArg(0);142SVal AssertionVal = State->getSVal(Assertion, LC);143144if (AssertionVal.isUndef())145return "UNDEFINED";146147ProgramStateRef StTrue, StFalse;148std::tie(StTrue, StFalse) =149State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>());150151if (StTrue) {152if (StFalse)153return "UNKNOWN";154else155return "TRUE";156} else {157if (StFalse)158return "FALSE";159else160llvm_unreachable("Invalid constraint; neither true or false.");161}162}163164ExplodedNode *165ExprInspectionChecker::reportBug(llvm::StringRef Msg, CheckerContext &C,166std::optional<SVal> ExprVal) const {167ExplodedNode *N = C.generateNonFatalErrorNode();168reportBug(Msg, C.getBugReporter(), N, ExprVal);169return N;170}171172ExplodedNode *173ExprInspectionChecker::reportBug(llvm::StringRef Msg, BugReporter &BR,174ExplodedNode *N,175std::optional<SVal> ExprVal) const {176if (!N)177return nullptr;178auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);179if (ExprVal) {180R->markInteresting(*ExprVal);181}182BR.emitReport(std::move(R));183return N;184}185186const Expr *ExprInspectionChecker::getArgExpr(const CallExpr *CE,187CheckerContext &C) const {188if (CE->getNumArgs() == 0) {189reportBug("Missing argument", C);190return nullptr;191}192return CE->getArg(0);193}194195const MemRegion *ExprInspectionChecker::getArgRegion(const CallExpr *CE,196CheckerContext &C) const {197const Expr *Arg = getArgExpr(CE, C);198if (!Arg)199return nullptr;200201const MemRegion *MR = C.getSVal(Arg).getAsRegion();202if (!MR) {203reportBug("Cannot obtain the region", C);204return nullptr;205}206207return MR;208}209210void ExprInspectionChecker::analyzerEval(const CallExpr *CE,211CheckerContext &C) const {212const LocationContext *LC = C.getPredecessor()->getLocationContext();213214// A specific instantiation of an inlined function may have more constrained215// values than can generally be assumed. Skip the check.216if (LC->getStackFrame()->getParent() != nullptr)217return;218219reportBug(getArgumentValueString(CE, C), C);220}221222void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE,223CheckerContext &C) const {224reportBug("REACHABLE", C);225}226227void ExprInspectionChecker::analyzerNumTimesReached(const CallExpr *CE,228CheckerContext &C) const {229++ReachedStats[CE].NumTimesReached;230if (!ReachedStats[CE].ExampleNode) {231// Later, in checkEndAnalysis, we'd throw a report against it.232ReachedStats[CE].ExampleNode = C.generateNonFatalErrorNode();233}234}235236void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,237CheckerContext &C) const {238const LocationContext *LC = C.getPredecessor()->getLocationContext();239240// An inlined function could conceivably also be analyzed as a top-level241// function. We ignore this case and only emit a message (TRUE or FALSE)242// when we are analyzing it as an inlined function. This means that243// clang_analyzer_checkInlined(true) should always print TRUE, but244// clang_analyzer_checkInlined(false) should never actually print anything.245if (LC->getStackFrame()->getParent() == nullptr)246return;247248reportBug(getArgumentValueString(CE, C), C);249}250251void ExprInspectionChecker::analyzerExplain(const CallExpr *CE,252CheckerContext &C) const {253const Expr *Arg = getArgExpr(CE, C);254if (!Arg)255return;256257SVal V = C.getSVal(Arg);258SValExplainer Ex(C.getASTContext());259reportBug(Ex.Visit(V), C);260}261262static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C,263const llvm::APSInt &I) {264Out << I.getBitWidth() << (I.isUnsigned() ? "u:" : "s:");265Out << I;266}267268static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C,269SymbolRef Sym) {270C.getConstraintManager().printValue(Out, C.getState(), Sym);271}272273static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C,274SVal V) {275Out << V;276}277278template <typename T>279void ExprInspectionChecker::printAndReport(CheckerContext &C, T What) const {280llvm::SmallString<64> Str;281llvm::raw_svector_ostream OS(Str);282printHelper(OS, C, What);283reportBug(OS.str(), C);284}285286void ExprInspectionChecker::analyzerValue(const CallExpr *CE,287CheckerContext &C) const {288const Expr *Arg = getArgExpr(CE, C);289if (!Arg)290return;291292SVal V = C.getSVal(Arg);293if (const SymbolRef Sym = V.getAsSymbol())294printAndReport(C, Sym);295else if (const llvm::APSInt *I = V.getAsInteger())296printAndReport(C, *I);297else298reportBug("n/a", C);299}300301void ExprInspectionChecker::analyzerDumpSValType(const CallExpr *CE,302CheckerContext &C) const {303const Expr *Arg = getArgExpr(CE, C);304if (!Arg)305return;306307QualType Ty = C.getSVal(Arg).getType(C.getASTContext());308reportBug(Ty.getAsString(), C);309}310311void ExprInspectionChecker::analyzerDump(const CallExpr *CE,312CheckerContext &C) const {313const Expr *Arg = getArgExpr(CE, C);314if (!Arg)315return;316317SVal V = C.getSVal(Arg);318printAndReport(C, V);319}320321void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE,322CheckerContext &C) const {323const Expr *Arg = getArgExpr(CE, C);324if (!Arg)325return;326327ProgramStateRef State = C.getState();328SVal Size = getDynamicExtentWithOffset(State, C.getSVal(Arg));329330State = State->BindExpr(CE, C.getLocationContext(), Size);331C.addTransition(State);332}333334void ExprInspectionChecker::analyzerDumpExtent(const CallExpr *CE,335CheckerContext &C) const {336const Expr *Arg = getArgExpr(CE, C);337if (!Arg)338return;339340ProgramStateRef State = C.getState();341SVal Size = getDynamicExtentWithOffset(State, C.getSVal(Arg));342printAndReport(C, Size);343}344345void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE,346CheckerContext &C) const {347const MemRegion *MR = getArgRegion(CE, C);348if (!MR)349return;350351QualType ElementTy;352if (const auto *TVR = MR->getAs<TypedValueRegion>()) {353ElementTy = TVR->getValueType();354} else {355ElementTy = MR->castAs<SymbolicRegion>()->getPointeeStaticType();356}357358assert(!ElementTy->isPointerType());359360DefinedOrUnknownSVal ElementCount = getDynamicElementCountWithOffset(361C.getState(), C.getSVal(getArgExpr(CE, C)), ElementTy);362printAndReport(C, ElementCount);363}364365void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE,366CheckerContext &C) const {367C.getState()->dump();368}369370void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE,371CheckerContext &C) const {372const Expr *Arg = getArgExpr(CE, C);373if (!Arg)374return;375376SVal Val = C.getSVal(Arg);377SymbolRef Sym = Val.getAsSymbol();378if (!Sym)379return;380381ProgramStateRef State = C.getState();382State = State->add<MarkedSymbols>(Sym);383C.addTransition(State);384}385386void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper,387CheckerContext &C) const {388ProgramStateRef State = C.getState();389const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>();390ExplodedNode *N = C.getPredecessor();391for (SymbolRef Sym : Syms) {392if (!SymReaper.isDead(Sym))393continue;394395// The non-fatal error node should be the same for all reports.396if (ExplodedNode *BugNode = reportBug("SYMBOL DEAD", C))397N = BugNode;398State = State->remove<MarkedSymbols>(Sym);399}400401for (auto I : State->get<DenotedSymbols>()) {402SymbolRef Sym = I.first;403if (!SymReaper.isLive(Sym))404State = State->remove<DenotedSymbols>(Sym);405}406407C.addTransition(State, N);408}409410void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,411ExprEngine &Eng) const {412for (auto Item : ReachedStats) {413unsigned NumTimesReached = Item.second.NumTimesReached;414ExplodedNode *N = Item.second.ExampleNode;415416reportBug(llvm::to_string(NumTimesReached), BR, N);417}418ReachedStats.clear();419}420421void ExprInspectionChecker::analyzerCrash(const CallExpr *CE,422CheckerContext &C) const {423LLVM_BUILTIN_TRAP;424}425426void ExprInspectionChecker::analyzerHashDump(const CallExpr *CE,427CheckerContext &C) const {428const LangOptions &Opts = C.getLangOpts();429const SourceManager &SM = C.getSourceManager();430FullSourceLoc FL(CE->getArg(0)->getBeginLoc(), SM);431std::string HashContent =432getIssueString(FL, getCheckerName().getName(), "Category",433C.getLocationContext()->getDecl(), Opts);434435reportBug(HashContent, C);436}437438void ExprInspectionChecker::analyzerDenote(const CallExpr *CE,439CheckerContext &C) const {440if (CE->getNumArgs() < 2) {441reportBug("clang_analyzer_denote() requires a symbol and a string literal",442C);443return;444}445446SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol();447if (!Sym) {448reportBug("Not a symbol", C);449return;450}451452const auto *E = dyn_cast<StringLiteral>(CE->getArg(1)->IgnoreParenCasts());453if (!E) {454reportBug("Not a string literal", C);455return;456}457458ProgramStateRef State = C.getState();459460C.addTransition(C.getState()->set<DenotedSymbols>(Sym, E));461}462463namespace {464class SymbolExpressor465: public SymExprVisitor<SymbolExpressor, std::optional<std::string>> {466ProgramStateRef State;467468public:469SymbolExpressor(ProgramStateRef State) : State(State) {}470471std::optional<std::string> lookup(const SymExpr *S) {472if (const StringLiteral *const *SLPtr = State->get<DenotedSymbols>(S)) {473const StringLiteral *SL = *SLPtr;474return std::string(SL->getBytes());475}476return std::nullopt;477}478479std::optional<std::string> VisitSymExpr(const SymExpr *S) {480return lookup(S);481}482483std::optional<std::string> VisitSymIntExpr(const SymIntExpr *S) {484if (std::optional<std::string> Str = lookup(S))485return Str;486if (std::optional<std::string> Str = Visit(S->getLHS()))487return (*Str + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " +488std::to_string(S->getRHS().getLimitedValue()) +489(S->getRHS().isUnsigned() ? "U" : ""))490.str();491return std::nullopt;492}493494std::optional<std::string> VisitSymSymExpr(const SymSymExpr *S) {495if (std::optional<std::string> Str = lookup(S))496return Str;497if (std::optional<std::string> Str1 = Visit(S->getLHS()))498if (std::optional<std::string> Str2 = Visit(S->getRHS()))499return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) +500" " + *Str2)501.str();502return std::nullopt;503}504505std::optional<std::string> VisitUnarySymExpr(const UnarySymExpr *S) {506if (std::optional<std::string> Str = lookup(S))507return Str;508if (std::optional<std::string> Str = Visit(S->getOperand()))509return (UnaryOperator::getOpcodeStr(S->getOpcode()) + *Str).str();510return std::nullopt;511}512513std::optional<std::string> VisitSymbolCast(const SymbolCast *S) {514if (std::optional<std::string> Str = lookup(S))515return Str;516if (std::optional<std::string> Str = Visit(S->getOperand()))517return (Twine("(") + S->getType().getAsString() + ")" + *Str).str();518return std::nullopt;519}520};521} // namespace522523void ExprInspectionChecker::analyzerExpress(const CallExpr *CE,524CheckerContext &C) const {525const Expr *Arg = getArgExpr(CE, C);526if (!Arg)527return;528529SVal ArgVal = C.getSVal(CE->getArg(0));530SymbolRef Sym = ArgVal.getAsSymbol();531if (!Sym) {532reportBug("Not a symbol", C, ArgVal);533return;534}535536SymbolExpressor V(C.getState());537auto Str = V.Visit(Sym);538if (!Str) {539reportBug("Unable to express", C, ArgVal);540return;541}542543reportBug(*Str, C, ArgVal);544}545546void ExprInspectionChecker::analyzerIsTainted(const CallExpr *CE,547CheckerContext &C) const {548if (CE->getNumArgs() != 1) {549reportBug("clang_analyzer_isTainted() requires exactly one argument", C);550return;551}552const bool IsTainted =553taint::isTainted(C.getState(), CE->getArg(0), C.getLocationContext());554reportBug(IsTainted ? "YES" : "NO", C);555}556557void ento::registerExprInspectionChecker(CheckerManager &Mgr) {558Mgr.registerChecker<ExprInspectionChecker>();559}560561bool ento::shouldRegisterExprInspectionChecker(const CheckerManager &mgr) {562return true;563}564565566