123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 |
- //===- CastValueChecker - Model implementation of custom RTTIs --*- 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 defines CastValueChecker which models casts of custom RTTIs.
- //
- // TODO list:
- // - It only allows one succesful cast between two types however in the wild
- // the object could be casted to multiple types.
- // - It needs to check the most likely type information from the dynamic type
- // map to increase precision of dynamic casting.
- //
- //===----------------------------------------------------------------------===//
- #include "clang/AST/DeclTemplate.h"
- #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
- #include "clang/StaticAnalyzer/Core/Checker.h"
- #include "clang/StaticAnalyzer/Core/CheckerManager.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
- #include "llvm/ADT/Optional.h"
- #include <utility>
- using namespace clang;
- using namespace ento;
- namespace {
- class CastValueChecker : public Checker<check::DeadSymbols, eval::Call> {
- enum class CallKind { Function, Method, InstanceOf };
- using CastCheck =
- std::function<void(const CastValueChecker *, const CallEvent &Call,
- DefinedOrUnknownSVal, CheckerContext &)>;
- public:
- // We have five cases to evaluate a cast:
- // 1) The parameter is non-null, the return value is non-null.
- // 2) The parameter is non-null, the return value is null.
- // 3) The parameter is null, the return value is null.
- // cast: 1; dyn_cast: 1, 2; cast_or_null: 1, 3; dyn_cast_or_null: 1, 2, 3.
- //
- // 4) castAs: Has no parameter, the return value is non-null.
- // 5) getAs: Has no parameter, the return value is null or non-null.
- //
- // We have two cases to check the parameter is an instance of the given type.
- // 1) isa: The parameter is non-null, returns boolean.
- // 2) isa_and_nonnull: The parameter is null or non-null, returns boolean.
- bool evalCall(const CallEvent &Call, CheckerContext &C) const;
- void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
- private:
- // These are known in the LLVM project. The pairs are in the following form:
- // {{{namespace, call}, argument-count}, {callback, kind}}
- const CallDescriptionMap<std::pair<CastCheck, CallKind>> CDM = {
- {{{"llvm", "cast"}, 1},
- {&CastValueChecker::evalCast, CallKind::Function}},
- {{{"llvm", "dyn_cast"}, 1},
- {&CastValueChecker::evalDynCast, CallKind::Function}},
- {{{"llvm", "cast_or_null"}, 1},
- {&CastValueChecker::evalCastOrNull, CallKind::Function}},
- {{{"llvm", "dyn_cast_or_null"}, 1},
- {&CastValueChecker::evalDynCastOrNull, CallKind::Function}},
- {{{"clang", "castAs"}, 0},
- {&CastValueChecker::evalCastAs, CallKind::Method}},
- {{{"clang", "getAs"}, 0},
- {&CastValueChecker::evalGetAs, CallKind::Method}},
- {{{"llvm", "isa"}, 1},
- {&CastValueChecker::evalIsa, CallKind::InstanceOf}},
- {{{"llvm", "isa_and_nonnull"}, 1},
- {&CastValueChecker::evalIsaAndNonNull, CallKind::InstanceOf}}};
- void evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
- CheckerContext &C) const;
- void evalDynCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
- CheckerContext &C) const;
- void evalCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
- CheckerContext &C) const;
- void evalDynCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
- CheckerContext &C) const;
- void evalCastAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
- CheckerContext &C) const;
- void evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
- CheckerContext &C) const;
- void evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV,
- CheckerContext &C) const;
- void evalIsaAndNonNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
- CheckerContext &C) const;
- };
- } // namespace
- static bool isInfeasibleCast(const DynamicCastInfo *CastInfo,
- bool CastSucceeds) {
- if (!CastInfo)
- return false;
- return CastSucceeds ? CastInfo->fails() : CastInfo->succeeds();
- }
- static const NoteTag *getNoteTag(CheckerContext &C,
- const DynamicCastInfo *CastInfo,
- QualType CastToTy, const Expr *Object,
- bool CastSucceeds, bool IsKnownCast) {
- std::string CastToName =
- CastInfo ? CastInfo->to()->getAsCXXRecordDecl()->getNameAsString()
- : CastToTy.getAsString();
- Object = Object->IgnoreParenImpCasts();
- return C.getNoteTag(
- [=]() -> std::string {
- SmallString<128> Msg;
- llvm::raw_svector_ostream Out(Msg);
- if (!IsKnownCast)
- Out << "Assuming ";
- if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) {
- Out << '\'' << DRE->getDecl()->getDeclName() << '\'';
- } else if (const auto *ME = dyn_cast<MemberExpr>(Object)) {
- Out << (IsKnownCast ? "Field '" : "field '")
- << ME->getMemberDecl()->getDeclName() << '\'';
- } else {
- Out << (IsKnownCast ? "The object" : "the object");
- }
- Out << ' ' << (CastSucceeds ? "is a" : "is not a") << " '" << CastToName
- << '\'';
- return std::string(Out.str());
- },
- /*IsPrunable=*/true);
- }
- static const NoteTag *getNoteTag(CheckerContext &C,
- SmallVector<QualType, 4> CastToTyVec,
- const Expr *Object,
- bool IsKnownCast) {
- Object = Object->IgnoreParenImpCasts();
- return C.getNoteTag(
- [=]() -> std::string {
- SmallString<128> Msg;
- llvm::raw_svector_ostream Out(Msg);
- if (!IsKnownCast)
- Out << "Assuming ";
- if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) {
- Out << '\'' << DRE->getDecl()->getNameAsString() << '\'';
- } else if (const auto *ME = dyn_cast<MemberExpr>(Object)) {
- Out << (IsKnownCast ? "Field '" : "field '")
- << ME->getMemberDecl()->getNameAsString() << '\'';
- } else {
- Out << (IsKnownCast ? "The object" : "the object");
- }
- Out << " is";
- bool First = true;
- for (QualType CastToTy: CastToTyVec) {
- std::string CastToName =
- CastToTy->getAsCXXRecordDecl()
- ? CastToTy->getAsCXXRecordDecl()->getNameAsString()
- : CastToTy.getAsString();
- Out << ' ' << ((CastToTyVec.size() == 1) ? "not" :
- (First ? "neither" : "nor")) << " a '" << CastToName
- << '\'';
- First = false;
- }
- return std::string(Out.str());
- },
- /*IsPrunable=*/true);
- }
- //===----------------------------------------------------------------------===//
- // Main logic to evaluate a cast.
- //===----------------------------------------------------------------------===//
- static QualType alignReferenceTypes(QualType toAlign, QualType alignTowards,
- ASTContext &ACtx) {
- if (alignTowards->isLValueReferenceType() &&
- alignTowards.isConstQualified()) {
- toAlign.addConst();
- return ACtx.getLValueReferenceType(toAlign);
- } else if (alignTowards->isLValueReferenceType())
- return ACtx.getLValueReferenceType(toAlign);
- else if (alignTowards->isRValueReferenceType())
- return ACtx.getRValueReferenceType(toAlign);
- llvm_unreachable("Must align towards a reference type!");
- }
- static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV,
- CheckerContext &C, bool IsNonNullParam,
- bool IsNonNullReturn,
- bool IsCheckedCast = false) {
- ProgramStateRef State = C.getState()->assume(DV, IsNonNullParam);
- if (!State)
- return;
- const Expr *Object;
- QualType CastFromTy;
- QualType CastToTy = Call.getResultType();
- if (Call.getNumArgs() > 0) {
- Object = Call.getArgExpr(0);
- CastFromTy = Call.parameters()[0]->getType();
- } else {
- Object = cast<CXXInstanceCall>(&Call)->getCXXThisExpr();
- CastFromTy = Object->getType();
- if (CastToTy->isPointerType()) {
- if (!CastFromTy->isPointerType())
- return;
- } else {
- if (!CastFromTy->isReferenceType())
- return;
- CastFromTy = alignReferenceTypes(CastFromTy, CastToTy, C.getASTContext());
- }
- }
- const MemRegion *MR = DV.getAsRegion();
- const DynamicCastInfo *CastInfo =
- getDynamicCastInfo(State, MR, CastFromTy, CastToTy);
- // We assume that every checked cast succeeds.
- bool CastSucceeds = IsCheckedCast || CastFromTy == CastToTy;
- if (!CastSucceeds) {
- if (CastInfo)
- CastSucceeds = IsNonNullReturn && CastInfo->succeeds();
- else
- CastSucceeds = IsNonNullReturn;
- }
- // Check for infeasible casts.
- if (isInfeasibleCast(CastInfo, CastSucceeds)) {
- C.generateSink(State, C.getPredecessor());
- return;
- }
- // Store the type and the cast information.
- bool IsKnownCast = CastInfo || IsCheckedCast || CastFromTy == CastToTy;
- if (!IsKnownCast || IsCheckedCast)
- State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
- CastSucceeds);
- SVal V = CastSucceeds ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy)
- : C.getSValBuilder().makeNull();
- C.addTransition(
- State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false),
- getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast));
- }
- static void addInstanceOfTransition(const CallEvent &Call,
- DefinedOrUnknownSVal DV,
- ProgramStateRef State, CheckerContext &C,
- bool IsInstanceOf) {
- const FunctionDecl *FD = Call.getDecl()->getAsFunction();
- QualType CastFromTy = Call.parameters()[0]->getType();
- SmallVector<QualType, 4> CastToTyVec;
- for (unsigned idx = 0; idx < FD->getTemplateSpecializationArgs()->size() - 1;
- ++idx) {
- TemplateArgument CastToTempArg =
- FD->getTemplateSpecializationArgs()->get(idx);
- switch (CastToTempArg.getKind()) {
- default:
- return;
- case TemplateArgument::Type:
- CastToTyVec.push_back(CastToTempArg.getAsType());
- break;
- case TemplateArgument::Pack:
- for (TemplateArgument ArgInPack: CastToTempArg.pack_elements())
- CastToTyVec.push_back(ArgInPack.getAsType());
- break;
- }
- }
- const MemRegion *MR = DV.getAsRegion();
- if (MR && CastFromTy->isReferenceType())
- MR = State->getSVal(DV.castAs<Loc>()).getAsRegion();
- bool Success = false;
- bool IsAnyKnown = false;
- for (QualType CastToTy: CastToTyVec) {
- if (CastFromTy->isPointerType())
- CastToTy = C.getASTContext().getPointerType(CastToTy);
- else if (CastFromTy->isReferenceType())
- CastToTy = alignReferenceTypes(CastToTy, CastFromTy, C.getASTContext());
- else
- return;
- const DynamicCastInfo *CastInfo =
- getDynamicCastInfo(State, MR, CastFromTy, CastToTy);
- bool CastSucceeds;
- if (CastInfo)
- CastSucceeds = IsInstanceOf && CastInfo->succeeds();
- else
- CastSucceeds = IsInstanceOf || CastFromTy == CastToTy;
- // Store the type and the cast information.
- bool IsKnownCast = CastInfo || CastFromTy == CastToTy;
- IsAnyKnown = IsAnyKnown || IsKnownCast;
- ProgramStateRef NewState = State;
- if (!IsKnownCast)
- NewState = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
- IsInstanceOf);
- if (CastSucceeds) {
- Success = true;
- C.addTransition(
- NewState->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
- C.getSValBuilder().makeTruthVal(true)),
- getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), true,
- IsKnownCast));
- if (IsKnownCast)
- return;
- } else if (CastInfo && CastInfo->succeeds()) {
- C.generateSink(NewState, C.getPredecessor());
- return;
- }
- }
- if (!Success) {
- C.addTransition(
- State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
- C.getSValBuilder().makeTruthVal(false)),
- getNoteTag(C, CastToTyVec, Call.getArgExpr(0), IsAnyKnown));
- }
- }
- //===----------------------------------------------------------------------===//
- // Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null.
- //===----------------------------------------------------------------------===//
- static void evalNonNullParamNonNullReturn(const CallEvent &Call,
- DefinedOrUnknownSVal DV,
- CheckerContext &C,
- bool IsCheckedCast = false) {
- addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
- /*IsNonNullReturn=*/true, IsCheckedCast);
- }
- static void evalNonNullParamNullReturn(const CallEvent &Call,
- DefinedOrUnknownSVal DV,
- CheckerContext &C) {
- addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
- /*IsNonNullReturn=*/false);
- }
- static void evalNullParamNullReturn(const CallEvent &Call,
- DefinedOrUnknownSVal DV,
- CheckerContext &C) {
- if (ProgramStateRef State = C.getState()->assume(DV, false))
- C.addTransition(State->BindExpr(Call.getOriginExpr(),
- C.getLocationContext(),
- C.getSValBuilder().makeNull(), false),
- C.getNoteTag("Assuming null pointer is passed into cast",
- /*IsPrunable=*/true));
- }
- void CastValueChecker::evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
- CheckerContext &C) const {
- evalNonNullParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true);
- }
- void CastValueChecker::evalDynCast(const CallEvent &Call,
- DefinedOrUnknownSVal DV,
- CheckerContext &C) const {
- evalNonNullParamNonNullReturn(Call, DV, C);
- evalNonNullParamNullReturn(Call, DV, C);
- }
- void CastValueChecker::evalCastOrNull(const CallEvent &Call,
- DefinedOrUnknownSVal DV,
- CheckerContext &C) const {
- evalNonNullParamNonNullReturn(Call, DV, C);
- evalNullParamNullReturn(Call, DV, C);
- }
- void CastValueChecker::evalDynCastOrNull(const CallEvent &Call,
- DefinedOrUnknownSVal DV,
- CheckerContext &C) const {
- evalNonNullParamNonNullReturn(Call, DV, C);
- evalNonNullParamNullReturn(Call, DV, C);
- evalNullParamNullReturn(Call, DV, C);
- }
- //===----------------------------------------------------------------------===//
- // Evaluating castAs, getAs.
- //===----------------------------------------------------------------------===//
- static void evalZeroParamNonNullReturn(const CallEvent &Call,
- DefinedOrUnknownSVal DV,
- CheckerContext &C,
- bool IsCheckedCast = false) {
- addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
- /*IsNonNullReturn=*/true, IsCheckedCast);
- }
- static void evalZeroParamNullReturn(const CallEvent &Call,
- DefinedOrUnknownSVal DV,
- CheckerContext &C) {
- addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
- /*IsNonNullReturn=*/false);
- }
- void CastValueChecker::evalCastAs(const CallEvent &Call,
- DefinedOrUnknownSVal DV,
- CheckerContext &C) const {
- evalZeroParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true);
- }
- void CastValueChecker::evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
- CheckerContext &C) const {
- evalZeroParamNonNullReturn(Call, DV, C);
- evalZeroParamNullReturn(Call, DV, C);
- }
- //===----------------------------------------------------------------------===//
- // Evaluating isa, isa_and_nonnull.
- //===----------------------------------------------------------------------===//
- void CastValueChecker::evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV,
- CheckerContext &C) const {
- ProgramStateRef NonNullState, NullState;
- std::tie(NonNullState, NullState) = C.getState()->assume(DV);
- if (NonNullState) {
- addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true);
- addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false);
- }
- if (NullState) {
- C.generateSink(NullState, C.getPredecessor());
- }
- }
- void CastValueChecker::evalIsaAndNonNull(const CallEvent &Call,
- DefinedOrUnknownSVal DV,
- CheckerContext &C) const {
- ProgramStateRef NonNullState, NullState;
- std::tie(NonNullState, NullState) = C.getState()->assume(DV);
- if (NonNullState) {
- addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true);
- addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false);
- }
- if (NullState) {
- addInstanceOfTransition(Call, DV, NullState, C, /*IsInstanceOf=*/false);
- }
- }
- //===----------------------------------------------------------------------===//
- // Main logic to evaluate a call.
- //===----------------------------------------------------------------------===//
- bool CastValueChecker::evalCall(const CallEvent &Call,
- CheckerContext &C) const {
- const auto *Lookup = CDM.lookup(Call);
- if (!Lookup)
- return false;
- const CastCheck &Check = Lookup->first;
- CallKind Kind = Lookup->second;
- Optional<DefinedOrUnknownSVal> DV;
- switch (Kind) {
- case CallKind::Function: {
- // We only model casts from pointers to pointers or from references
- // to references. Other casts are most likely specialized and we
- // cannot model them.
- QualType ParamT = Call.parameters()[0]->getType();
- QualType ResultT = Call.getResultType();
- if (!(ParamT->isPointerType() && ResultT->isPointerType()) &&
- !(ParamT->isReferenceType() && ResultT->isReferenceType())) {
- return false;
- }
- DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
- break;
- }
- case CallKind::InstanceOf: {
- // We need to obtain the only template argument to determinte the type.
- const FunctionDecl *FD = Call.getDecl()->getAsFunction();
- if (!FD || !FD->getTemplateSpecializationArgs())
- return false;
- DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
- break;
- }
- case CallKind::Method:
- const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call);
- if (!InstanceCall)
- return false;
- DV = InstanceCall->getCXXThisVal().getAs<DefinedOrUnknownSVal>();
- break;
- }
- if (!DV)
- return false;
- Check(this, Call, *DV, C);
- return true;
- }
- void CastValueChecker::checkDeadSymbols(SymbolReaper &SR,
- CheckerContext &C) const {
- C.addTransition(removeDeadCasts(C.getState(), SR));
- }
- void ento::registerCastValueChecker(CheckerManager &Mgr) {
- Mgr.registerChecker<CastValueChecker>();
- }
- bool ento::shouldRegisterCastValueChecker(const CheckerManager &mgr) {
- return true;
- }
|