Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
35266 views
//===- CastValueChecker - Model implementation of custom RTTIs --*- 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//===----------------------------------------------------------------------===//7//8// This defines CastValueChecker which models casts of custom RTTIs.9//10// TODO list:11// - It only allows one succesful cast between two types however in the wild12// the object could be casted to multiple types.13// - It needs to check the most likely type information from the dynamic type14// map to increase precision of dynamic casting.15//16//===----------------------------------------------------------------------===//1718#include "clang/AST/DeclTemplate.h"19#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"20#include "clang/StaticAnalyzer/Core/Checker.h"21#include "clang/StaticAnalyzer/Core/CheckerManager.h"22#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"23#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"24#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"25#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"26#include <optional>27#include <utility>2829using namespace clang;30using namespace ento;3132namespace {33class CastValueChecker : public Checker<check::DeadSymbols, eval::Call> {34enum class CallKind { Function, Method, InstanceOf };3536using CastCheck =37std::function<void(const CastValueChecker *, const CallEvent &Call,38DefinedOrUnknownSVal, CheckerContext &)>;3940public:41// We have five cases to evaluate a cast:42// 1) The parameter is non-null, the return value is non-null.43// 2) The parameter is non-null, the return value is null.44// 3) The parameter is null, the return value is null.45// cast: 1; dyn_cast: 1, 2; cast_or_null: 1, 3; dyn_cast_or_null: 1, 2, 3.46//47// 4) castAs: Has no parameter, the return value is non-null.48// 5) getAs: Has no parameter, the return value is null or non-null.49//50// We have two cases to check the parameter is an instance of the given type.51// 1) isa: The parameter is non-null, returns boolean.52// 2) isa_and_nonnull: The parameter is null or non-null, returns boolean.53bool evalCall(const CallEvent &Call, CheckerContext &C) const;54void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;5556private:57// These are known in the LLVM project. The pairs are in the following form:58// {{match-mode, {namespace, call}, argument-count}, {callback, kind}}59const CallDescriptionMap<std::pair<CastCheck, CallKind>> CDM = {60{{CDM::SimpleFunc, {"llvm", "cast"}, 1},61{&CastValueChecker::evalCast, CallKind::Function}},62{{CDM::SimpleFunc, {"llvm", "dyn_cast"}, 1},63{&CastValueChecker::evalDynCast, CallKind::Function}},64{{CDM::SimpleFunc, {"llvm", "cast_or_null"}, 1},65{&CastValueChecker::evalCastOrNull, CallKind::Function}},66{{CDM::SimpleFunc, {"llvm", "dyn_cast_or_null"}, 1},67{&CastValueChecker::evalDynCastOrNull, CallKind::Function}},68{{CDM::CXXMethod, {"clang", "castAs"}, 0},69{&CastValueChecker::evalCastAs, CallKind::Method}},70{{CDM::CXXMethod, {"clang", "getAs"}, 0},71{&CastValueChecker::evalGetAs, CallKind::Method}},72{{CDM::SimpleFunc, {"llvm", "isa"}, 1},73{&CastValueChecker::evalIsa, CallKind::InstanceOf}},74{{CDM::SimpleFunc, {"llvm", "isa_and_nonnull"}, 1},75{&CastValueChecker::evalIsaAndNonNull, CallKind::InstanceOf}}};7677void evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,78CheckerContext &C) const;79void evalDynCast(const CallEvent &Call, DefinedOrUnknownSVal DV,80CheckerContext &C) const;81void evalCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV,82CheckerContext &C) const;83void evalDynCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV,84CheckerContext &C) const;85void evalCastAs(const CallEvent &Call, DefinedOrUnknownSVal DV,86CheckerContext &C) const;87void evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,88CheckerContext &C) const;89void evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV,90CheckerContext &C) const;91void evalIsaAndNonNull(const CallEvent &Call, DefinedOrUnknownSVal DV,92CheckerContext &C) const;93};94} // namespace9596static bool isInfeasibleCast(const DynamicCastInfo *CastInfo,97bool CastSucceeds) {98if (!CastInfo)99return false;100101return CastSucceeds ? CastInfo->fails() : CastInfo->succeeds();102}103104static const NoteTag *getNoteTag(CheckerContext &C,105const DynamicCastInfo *CastInfo,106QualType CastToTy, const Expr *Object,107bool CastSucceeds, bool IsKnownCast) {108std::string CastToName =109CastInfo ? CastInfo->to()->getAsCXXRecordDecl()->getNameAsString()110: CastToTy.getAsString();111Object = Object->IgnoreParenImpCasts();112113return C.getNoteTag(114[=]() -> std::string {115SmallString<128> Msg;116llvm::raw_svector_ostream Out(Msg);117118if (!IsKnownCast)119Out << "Assuming ";120121if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) {122Out << '\'' << DRE->getDecl()->getDeclName() << '\'';123} else if (const auto *ME = dyn_cast<MemberExpr>(Object)) {124Out << (IsKnownCast ? "Field '" : "field '")125<< ME->getMemberDecl()->getDeclName() << '\'';126} else {127Out << (IsKnownCast ? "The object" : "the object");128}129130Out << ' ' << (CastSucceeds ? "is a" : "is not a") << " '" << CastToName131<< '\'';132133return std::string(Out.str());134},135/*IsPrunable=*/true);136}137138static const NoteTag *getNoteTag(CheckerContext &C,139SmallVector<QualType, 4> CastToTyVec,140const Expr *Object,141bool IsKnownCast) {142Object = Object->IgnoreParenImpCasts();143144return C.getNoteTag(145[=]() -> std::string {146SmallString<128> Msg;147llvm::raw_svector_ostream Out(Msg);148149if (!IsKnownCast)150Out << "Assuming ";151152if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) {153Out << '\'' << DRE->getDecl()->getNameAsString() << '\'';154} else if (const auto *ME = dyn_cast<MemberExpr>(Object)) {155Out << (IsKnownCast ? "Field '" : "field '")156<< ME->getMemberDecl()->getNameAsString() << '\'';157} else {158Out << (IsKnownCast ? "The object" : "the object");159}160Out << " is";161162bool First = true;163for (QualType CastToTy: CastToTyVec) {164std::string CastToName =165CastToTy->getAsCXXRecordDecl()166? CastToTy->getAsCXXRecordDecl()->getNameAsString()167: CastToTy.getAsString();168Out << ' ' << ((CastToTyVec.size() == 1) ? "not" :169(First ? "neither" : "nor")) << " a '" << CastToName170<< '\'';171First = false;172}173174return std::string(Out.str());175},176/*IsPrunable=*/true);177}178179//===----------------------------------------------------------------------===//180// Main logic to evaluate a cast.181//===----------------------------------------------------------------------===//182183static QualType alignReferenceTypes(QualType toAlign, QualType alignTowards,184ASTContext &ACtx) {185if (alignTowards->isLValueReferenceType() &&186alignTowards.isConstQualified()) {187toAlign.addConst();188return ACtx.getLValueReferenceType(toAlign);189} else if (alignTowards->isLValueReferenceType())190return ACtx.getLValueReferenceType(toAlign);191else if (alignTowards->isRValueReferenceType())192return ACtx.getRValueReferenceType(toAlign);193194llvm_unreachable("Must align towards a reference type!");195}196197static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV,198CheckerContext &C, bool IsNonNullParam,199bool IsNonNullReturn,200bool IsCheckedCast = false) {201ProgramStateRef State = C.getState()->assume(DV, IsNonNullParam);202if (!State)203return;204205const Expr *Object;206QualType CastFromTy;207QualType CastToTy = Call.getResultType();208209if (Call.getNumArgs() > 0) {210Object = Call.getArgExpr(0);211CastFromTy = Call.parameters()[0]->getType();212} else {213Object = cast<CXXInstanceCall>(&Call)->getCXXThisExpr();214CastFromTy = Object->getType();215if (CastToTy->isPointerType()) {216if (!CastFromTy->isPointerType())217return;218} else {219if (!CastFromTy->isReferenceType())220return;221222CastFromTy = alignReferenceTypes(CastFromTy, CastToTy, C.getASTContext());223}224}225226const MemRegion *MR = DV.getAsRegion();227const DynamicCastInfo *CastInfo =228getDynamicCastInfo(State, MR, CastFromTy, CastToTy);229230// We assume that every checked cast succeeds.231bool CastSucceeds = IsCheckedCast || CastFromTy == CastToTy;232if (!CastSucceeds) {233if (CastInfo)234CastSucceeds = IsNonNullReturn && CastInfo->succeeds();235else236CastSucceeds = IsNonNullReturn;237}238239// Check for infeasible casts.240if (isInfeasibleCast(CastInfo, CastSucceeds)) {241C.generateSink(State, C.getPredecessor());242return;243}244245// Store the type and the cast information.246bool IsKnownCast = CastInfo || IsCheckedCast || CastFromTy == CastToTy;247if (!IsKnownCast || IsCheckedCast)248State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,249CastSucceeds);250251SVal V = CastSucceeds ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy)252: C.getSValBuilder().makeNullWithType(CastToTy);253C.addTransition(254State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false),255getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast));256}257258static void addInstanceOfTransition(const CallEvent &Call,259DefinedOrUnknownSVal DV,260ProgramStateRef State, CheckerContext &C,261bool IsInstanceOf) {262const FunctionDecl *FD = Call.getDecl()->getAsFunction();263QualType CastFromTy = Call.parameters()[0]->getType();264SmallVector<QualType, 4> CastToTyVec;265for (unsigned idx = 0; idx < FD->getTemplateSpecializationArgs()->size() - 1;266++idx) {267TemplateArgument CastToTempArg =268FD->getTemplateSpecializationArgs()->get(idx);269switch (CastToTempArg.getKind()) {270default:271return;272case TemplateArgument::Type:273CastToTyVec.push_back(CastToTempArg.getAsType());274break;275case TemplateArgument::Pack:276for (TemplateArgument ArgInPack: CastToTempArg.pack_elements())277CastToTyVec.push_back(ArgInPack.getAsType());278break;279}280}281282const MemRegion *MR = DV.getAsRegion();283if (MR && CastFromTy->isReferenceType())284MR = State->getSVal(DV.castAs<Loc>()).getAsRegion();285286bool Success = false;287bool IsAnyKnown = false;288for (QualType CastToTy: CastToTyVec) {289if (CastFromTy->isPointerType())290CastToTy = C.getASTContext().getPointerType(CastToTy);291else if (CastFromTy->isReferenceType())292CastToTy = alignReferenceTypes(CastToTy, CastFromTy, C.getASTContext());293else294return;295296const DynamicCastInfo *CastInfo =297getDynamicCastInfo(State, MR, CastFromTy, CastToTy);298299bool CastSucceeds;300if (CastInfo)301CastSucceeds = IsInstanceOf && CastInfo->succeeds();302else303CastSucceeds = IsInstanceOf || CastFromTy == CastToTy;304305// Store the type and the cast information.306bool IsKnownCast = CastInfo || CastFromTy == CastToTy;307IsAnyKnown = IsAnyKnown || IsKnownCast;308ProgramStateRef NewState = State;309if (!IsKnownCast)310NewState = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,311IsInstanceOf);312313if (CastSucceeds) {314Success = true;315C.addTransition(316NewState->BindExpr(Call.getOriginExpr(), C.getLocationContext(),317C.getSValBuilder().makeTruthVal(true)),318getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), true,319IsKnownCast));320if (IsKnownCast)321return;322} else if (CastInfo && CastInfo->succeeds()) {323C.generateSink(NewState, C.getPredecessor());324return;325}326}327328if (!Success) {329C.addTransition(330State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),331C.getSValBuilder().makeTruthVal(false)),332getNoteTag(C, CastToTyVec, Call.getArgExpr(0), IsAnyKnown));333}334}335336//===----------------------------------------------------------------------===//337// Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null.338//===----------------------------------------------------------------------===//339340static void evalNonNullParamNonNullReturn(const CallEvent &Call,341DefinedOrUnknownSVal DV,342CheckerContext &C,343bool IsCheckedCast = false) {344addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,345/*IsNonNullReturn=*/true, IsCheckedCast);346}347348static void evalNonNullParamNullReturn(const CallEvent &Call,349DefinedOrUnknownSVal DV,350CheckerContext &C) {351addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,352/*IsNonNullReturn=*/false);353}354355static void evalNullParamNullReturn(const CallEvent &Call,356DefinedOrUnknownSVal DV,357CheckerContext &C) {358if (ProgramStateRef State = C.getState()->assume(DV, false))359C.addTransition(State->BindExpr(Call.getOriginExpr(),360C.getLocationContext(),361C.getSValBuilder().makeNullWithType(362Call.getOriginExpr()->getType()),363false),364C.getNoteTag("Assuming null pointer is passed into cast",365/*IsPrunable=*/true));366}367368void CastValueChecker::evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,369CheckerContext &C) const {370evalNonNullParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true);371}372373void CastValueChecker::evalDynCast(const CallEvent &Call,374DefinedOrUnknownSVal DV,375CheckerContext &C) const {376evalNonNullParamNonNullReturn(Call, DV, C);377evalNonNullParamNullReturn(Call, DV, C);378}379380void CastValueChecker::evalCastOrNull(const CallEvent &Call,381DefinedOrUnknownSVal DV,382CheckerContext &C) const {383evalNonNullParamNonNullReturn(Call, DV, C);384evalNullParamNullReturn(Call, DV, C);385}386387void CastValueChecker::evalDynCastOrNull(const CallEvent &Call,388DefinedOrUnknownSVal DV,389CheckerContext &C) const {390evalNonNullParamNonNullReturn(Call, DV, C);391evalNonNullParamNullReturn(Call, DV, C);392evalNullParamNullReturn(Call, DV, C);393}394395//===----------------------------------------------------------------------===//396// Evaluating castAs, getAs.397//===----------------------------------------------------------------------===//398399static void evalZeroParamNonNullReturn(const CallEvent &Call,400DefinedOrUnknownSVal DV,401CheckerContext &C,402bool IsCheckedCast = false) {403addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,404/*IsNonNullReturn=*/true, IsCheckedCast);405}406407static void evalZeroParamNullReturn(const CallEvent &Call,408DefinedOrUnknownSVal DV,409CheckerContext &C) {410addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,411/*IsNonNullReturn=*/false);412}413414void CastValueChecker::evalCastAs(const CallEvent &Call,415DefinedOrUnknownSVal DV,416CheckerContext &C) const {417evalZeroParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true);418}419420void CastValueChecker::evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,421CheckerContext &C) const {422evalZeroParamNonNullReturn(Call, DV, C);423evalZeroParamNullReturn(Call, DV, C);424}425426//===----------------------------------------------------------------------===//427// Evaluating isa, isa_and_nonnull.428//===----------------------------------------------------------------------===//429430void CastValueChecker::evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV,431CheckerContext &C) const {432ProgramStateRef NonNullState, NullState;433std::tie(NonNullState, NullState) = C.getState()->assume(DV);434435if (NonNullState) {436addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true);437addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false);438}439440if (NullState) {441C.generateSink(NullState, C.getPredecessor());442}443}444445void CastValueChecker::evalIsaAndNonNull(const CallEvent &Call,446DefinedOrUnknownSVal DV,447CheckerContext &C) const {448ProgramStateRef NonNullState, NullState;449std::tie(NonNullState, NullState) = C.getState()->assume(DV);450451if (NonNullState) {452addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true);453addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false);454}455456if (NullState) {457addInstanceOfTransition(Call, DV, NullState, C, /*IsInstanceOf=*/false);458}459}460461//===----------------------------------------------------------------------===//462// Main logic to evaluate a call.463//===----------------------------------------------------------------------===//464465bool CastValueChecker::evalCall(const CallEvent &Call,466CheckerContext &C) const {467const auto *Lookup = CDM.lookup(Call);468if (!Lookup)469return false;470471const CastCheck &Check = Lookup->first;472CallKind Kind = Lookup->second;473474std::optional<DefinedOrUnknownSVal> DV;475476switch (Kind) {477case CallKind::Function: {478// We only model casts from pointers to pointers or from references479// to references. Other casts are most likely specialized and we480// cannot model them.481QualType ParamT = Call.parameters()[0]->getType();482QualType ResultT = Call.getResultType();483if (!(ParamT->isPointerType() && ResultT->isPointerType()) &&484!(ParamT->isReferenceType() && ResultT->isReferenceType())) {485return false;486}487488DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();489break;490}491case CallKind::InstanceOf: {492// We need to obtain the only template argument to determinte the type.493const FunctionDecl *FD = Call.getDecl()->getAsFunction();494if (!FD || !FD->getTemplateSpecializationArgs())495return false;496497DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();498break;499}500case CallKind::Method:501const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call);502if (!InstanceCall)503return false;504505DV = InstanceCall->getCXXThisVal().getAs<DefinedOrUnknownSVal>();506break;507}508509if (!DV)510return false;511512Check(this, Call, *DV, C);513return true;514}515516void CastValueChecker::checkDeadSymbols(SymbolReaper &SR,517CheckerContext &C) const {518C.addTransition(removeDeadCasts(C.getState(), SR));519}520521void ento::registerCastValueChecker(CheckerManager &Mgr) {522Mgr.registerChecker<CastValueChecker>();523}524525bool ento::shouldRegisterCastValueChecker(const CheckerManager &mgr) {526return true;527}528529530