//===- DynamicType.cpp - Dynamic type related APIs --------------*- 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 file defines APIs that track and query dynamic type information. This // information can be used to devirtualize calls during the symbolic execution // or do type checking. // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" #include "clang/Basic/JsonSupport.h" #include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "llvm/Support/Casting.h" #include "llvm/Support/raw_ostream.h" #include /// The GDM component containing the dynamic type info. This is a map from a /// symbol to its most likely type. REGISTER_MAP_WITH_PROGRAMSTATE(DynamicTypeMap, const clang::ento::MemRegion *, clang::ento::DynamicTypeInfo) /// A set factory of dynamic cast informations. REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(CastSet, clang::ento::DynamicCastInfo) /// A map from symbols to cast informations. REGISTER_MAP_WITH_PROGRAMSTATE(DynamicCastMap, const clang::ento::MemRegion *, CastSet) // A map from Class object symbols to the most likely pointed-to type. REGISTER_MAP_WITH_PROGRAMSTATE(DynamicClassObjectMap, clang::ento::SymbolRef, clang::ento::DynamicTypeInfo) namespace clang { namespace ento { DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR) { MR = MR->StripCasts(); // Look up the dynamic type in the GDM. if (const DynamicTypeInfo *DTI = State->get(MR)) return *DTI; // Otherwise, fall back to what we know about the region. if (const auto *TR = dyn_cast(MR)) return DynamicTypeInfo(TR->getLocationType(), /*CanBeSub=*/false); if (const auto *SR = dyn_cast(MR)) { SymbolRef Sym = SR->getSymbol(); return DynamicTypeInfo(Sym->getType()); } return {}; } const DynamicTypeInfo *getRawDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR) { return State->get(MR); } static void unbox(QualType &Ty) { // FIXME: Why are we being fed references to pointers in the first place? while (Ty->isReferenceType() || Ty->isPointerType()) Ty = Ty->getPointeeType(); Ty = Ty.getCanonicalType().getUnqualifiedType(); } const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State, const MemRegion *MR, QualType CastFromTy, QualType CastToTy) { const auto *Lookup = State->get().lookup(MR); if (!Lookup) return nullptr; unbox(CastFromTy); unbox(CastToTy); for (const DynamicCastInfo &Cast : *Lookup) if (Cast.equals(CastFromTy, CastToTy)) return &Cast; return nullptr; } DynamicTypeInfo getClassObjectDynamicTypeInfo(ProgramStateRef State, SymbolRef Sym) { const DynamicTypeInfo *DTI = State->get(Sym); return DTI ? *DTI : DynamicTypeInfo{}; } ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR, DynamicTypeInfo NewTy) { State = State->set(MR->StripCasts(), NewTy); assert(State); return State; } ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR, QualType NewTy, bool CanBeSubClassed) { return setDynamicTypeInfo(State, MR, DynamicTypeInfo(NewTy, CanBeSubClassed)); } ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State, const MemRegion *MR, QualType CastFromTy, QualType CastToTy, bool CastSucceeds) { if (!MR) return State; if (CastSucceeds) { assert((CastToTy->isAnyPointerType() || CastToTy->isReferenceType()) && "DynamicTypeInfo should always be a pointer."); State = State->set(MR, CastToTy); } unbox(CastFromTy); unbox(CastToTy); DynamicCastInfo::CastResult ResultKind = CastSucceeds ? DynamicCastInfo::CastResult::Success : DynamicCastInfo::CastResult::Failure; CastSet::Factory &F = State->get_context(); const CastSet *TempSet = State->get(MR); CastSet Set = TempSet ? *TempSet : F.getEmptySet(); Set = F.add(Set, {CastFromTy, CastToTy, ResultKind}); State = State->set(MR, Set); assert(State); return State; } ProgramStateRef setClassObjectDynamicTypeInfo(ProgramStateRef State, SymbolRef Sym, DynamicTypeInfo NewTy) { State = State->set(Sym, NewTy); return State; } ProgramStateRef setClassObjectDynamicTypeInfo(ProgramStateRef State, SymbolRef Sym, QualType NewTy, bool CanBeSubClassed) { return setClassObjectDynamicTypeInfo(State, Sym, DynamicTypeInfo(NewTy, CanBeSubClassed)); } static bool isLive(SymbolReaper &SR, const MemRegion *MR) { return SR.isLiveRegion(MR); } static bool isLive(SymbolReaper &SR, SymbolRef Sym) { return SR.isLive(Sym); } template static ProgramStateRef removeDeadImpl(ProgramStateRef State, SymbolReaper &SR) { const auto &Map = State->get(); for (const auto &Elem : Map) if (!isLive(SR, Elem.first)) State = State->remove(Elem.first); return State; } ProgramStateRef removeDeadTypes(ProgramStateRef State, SymbolReaper &SR) { return removeDeadImpl(State, SR); } ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR) { return removeDeadImpl(State, SR); } ProgramStateRef removeDeadClassObjectTypes(ProgramStateRef State, SymbolReaper &SR) { return removeDeadImpl(State, SR); } //===----------------------------------------------------------------------===// // Implementation of the 'printer-to-JSON' function //===----------------------------------------------------------------------===// static raw_ostream &printJson(const MemRegion *Region, raw_ostream &Out, const char *NL, unsigned int Space, bool IsDot) { return Out << "\"region\": \"" << Region << "\""; } static raw_ostream &printJson(const SymExpr *Symbol, raw_ostream &Out, const char *NL, unsigned int Space, bool IsDot) { return Out << "\"symbol\": \"" << Symbol << "\""; } static raw_ostream &printJson(const DynamicTypeInfo &DTI, raw_ostream &Out, const char *NL, unsigned int Space, bool IsDot) { Out << "\"dyn_type\": "; if (!DTI.isValid()) { Out << "null"; } else { QualType ToPrint = DTI.getType(); if (ToPrint->isAnyPointerType()) ToPrint = ToPrint->getPointeeType(); Out << '\"' << ToPrint.getAsString() << "\", \"sub_classable\": " << (DTI.canBeASubClass() ? "true" : "false"); } return Out; } static raw_ostream &printJson(const DynamicCastInfo &DCI, raw_ostream &Out, const char *NL, unsigned int Space, bool IsDot) { return Out << "\"from\": \"" << DCI.from().getAsString() << "\", \"to\": \"" << DCI.to().getAsString() << "\", \"kind\": \"" << (DCI.succeeds() ? "success" : "fail") << "\""; } template static raw_ostream &printJson(const std::pair &Pair, raw_ostream &Out, const char *NL, unsigned int Space, bool IsDot) { printJson(Pair.first, Out, NL, Space, IsDot) << ", "; return printJson(Pair.second, Out, NL, Space, IsDot); } template static raw_ostream &printJsonContainer(const ContainerTy &Container, raw_ostream &Out, const char *NL, unsigned int Space, bool IsDot) { if (Container.isEmpty()) { return Out << "null"; } ++Space; Out << '[' << NL; for (auto I = Container.begin(); I != Container.end(); ++I) { const auto &Element = *I; Indent(Out, Space, IsDot) << "{ "; printJson(Element, Out, NL, Space, IsDot) << " }"; if (std::next(I) != Container.end()) Out << ','; Out << NL; } --Space; return Indent(Out, Space, IsDot) << "]"; } static raw_ostream &printJson(const CastSet &Set, raw_ostream &Out, const char *NL, unsigned int Space, bool IsDot) { Out << "\"casts\": "; return printJsonContainer(Set, Out, NL, Space, IsDot); } template static void printJsonImpl(raw_ostream &Out, ProgramStateRef State, const char *Name, const char *NL, unsigned int Space, bool IsDot, bool PrintEvenIfEmpty = true) { const auto &Map = State->get(); if (Map.isEmpty() && !PrintEvenIfEmpty) return; Indent(Out, Space, IsDot) << "\"" << Name << "\": "; printJsonContainer(Map, Out, NL, Space, IsDot) << "," << NL; } static void printDynamicTypesJson(raw_ostream &Out, ProgramStateRef State, const char *NL, unsigned int Space, bool IsDot) { printJsonImpl(Out, State, "dynamic_types", NL, Space, IsDot); } static void printDynamicCastsJson(raw_ostream &Out, ProgramStateRef State, const char *NL, unsigned int Space, bool IsDot) { printJsonImpl(Out, State, "dynamic_casts", NL, Space, IsDot); } static void printClassObjectDynamicTypesJson(raw_ostream &Out, ProgramStateRef State, const char *NL, unsigned int Space, bool IsDot) { // Let's print Class object type information only if we have something // meaningful to print. printJsonImpl(Out, State, "class_object_types", NL, Space, IsDot, /*PrintEvenIfEmpty=*/false); } void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State, const char *NL, unsigned int Space, bool IsDot) { printDynamicTypesJson(Out, State, NL, Space, IsDot); printDynamicCastsJson(Out, State, NL, Space, IsDot); printClassObjectDynamicTypesJson(Out, State, NL, Space, IsDot); } } // namespace ento } // namespace clang