1e5dd7070Spatrick //===- CastValueChecker - Model implementation of custom RTTIs --*- C++ -*-===// 2e5dd7070Spatrick // 3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information. 5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e5dd7070Spatrick // 7e5dd7070Spatrick //===----------------------------------------------------------------------===// 8e5dd7070Spatrick // 9e5dd7070Spatrick // This defines CastValueChecker which models casts of custom RTTIs. 10e5dd7070Spatrick // 11e5dd7070Spatrick // TODO list: 12e5dd7070Spatrick // - It only allows one succesful cast between two types however in the wild 13e5dd7070Spatrick // the object could be casted to multiple types. 14e5dd7070Spatrick // - It needs to check the most likely type information from the dynamic type 15e5dd7070Spatrick // map to increase precision of dynamic casting. 16e5dd7070Spatrick // 17e5dd7070Spatrick //===----------------------------------------------------------------------===// 18e5dd7070Spatrick 19e5dd7070Spatrick #include "clang/AST/DeclTemplate.h" 20e5dd7070Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 21e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h" 22e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/CheckerManager.h" 23e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 24e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 25e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" 26e5dd7070Spatrick #include "llvm/ADT/Optional.h" 27e5dd7070Spatrick #include <utility> 28e5dd7070Spatrick 29e5dd7070Spatrick using namespace clang; 30e5dd7070Spatrick using namespace ento; 31e5dd7070Spatrick 32e5dd7070Spatrick namespace { 33*ec727ea7Spatrick class CastValueChecker : public Checker<check::DeadSymbols, eval::Call> { 34e5dd7070Spatrick enum class CallKind { Function, Method, InstanceOf }; 35e5dd7070Spatrick 36e5dd7070Spatrick using CastCheck = 37e5dd7070Spatrick std::function<void(const CastValueChecker *, const CallEvent &Call, 38e5dd7070Spatrick DefinedOrUnknownSVal, CheckerContext &)>; 39e5dd7070Spatrick 40e5dd7070Spatrick public: 41e5dd7070Spatrick // We have five cases to evaluate a cast: 42e5dd7070Spatrick // 1) The parameter is non-null, the return value is non-null. 43e5dd7070Spatrick // 2) The parameter is non-null, the return value is null. 44e5dd7070Spatrick // 3) The parameter is null, the return value is null. 45e5dd7070Spatrick // cast: 1; dyn_cast: 1, 2; cast_or_null: 1, 3; dyn_cast_or_null: 1, 2, 3. 46e5dd7070Spatrick // 47e5dd7070Spatrick // 4) castAs: Has no parameter, the return value is non-null. 48e5dd7070Spatrick // 5) getAs: Has no parameter, the return value is null or non-null. 49e5dd7070Spatrick // 50e5dd7070Spatrick // We have two cases to check the parameter is an instance of the given type. 51e5dd7070Spatrick // 1) isa: The parameter is non-null, returns boolean. 52e5dd7070Spatrick // 2) isa_and_nonnull: The parameter is null or non-null, returns boolean. 53e5dd7070Spatrick bool evalCall(const CallEvent &Call, CheckerContext &C) const; 54*ec727ea7Spatrick void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 55e5dd7070Spatrick 56e5dd7070Spatrick private: 57e5dd7070Spatrick // These are known in the LLVM project. The pairs are in the following form: 58e5dd7070Spatrick // {{{namespace, call}, argument-count}, {callback, kind}} 59e5dd7070Spatrick const CallDescriptionMap<std::pair<CastCheck, CallKind>> CDM = { 60e5dd7070Spatrick {{{"llvm", "cast"}, 1}, 61e5dd7070Spatrick {&CastValueChecker::evalCast, CallKind::Function}}, 62e5dd7070Spatrick {{{"llvm", "dyn_cast"}, 1}, 63e5dd7070Spatrick {&CastValueChecker::evalDynCast, CallKind::Function}}, 64e5dd7070Spatrick {{{"llvm", "cast_or_null"}, 1}, 65e5dd7070Spatrick {&CastValueChecker::evalCastOrNull, CallKind::Function}}, 66e5dd7070Spatrick {{{"llvm", "dyn_cast_or_null"}, 1}, 67e5dd7070Spatrick {&CastValueChecker::evalDynCastOrNull, CallKind::Function}}, 68e5dd7070Spatrick {{{"clang", "castAs"}, 0}, 69e5dd7070Spatrick {&CastValueChecker::evalCastAs, CallKind::Method}}, 70e5dd7070Spatrick {{{"clang", "getAs"}, 0}, 71e5dd7070Spatrick {&CastValueChecker::evalGetAs, CallKind::Method}}, 72e5dd7070Spatrick {{{"llvm", "isa"}, 1}, 73e5dd7070Spatrick {&CastValueChecker::evalIsa, CallKind::InstanceOf}}, 74e5dd7070Spatrick {{{"llvm", "isa_and_nonnull"}, 1}, 75e5dd7070Spatrick {&CastValueChecker::evalIsaAndNonNull, CallKind::InstanceOf}}}; 76e5dd7070Spatrick 77e5dd7070Spatrick void evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV, 78e5dd7070Spatrick CheckerContext &C) const; 79e5dd7070Spatrick void evalDynCast(const CallEvent &Call, DefinedOrUnknownSVal DV, 80e5dd7070Spatrick CheckerContext &C) const; 81e5dd7070Spatrick void evalCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV, 82e5dd7070Spatrick CheckerContext &C) const; 83e5dd7070Spatrick void evalDynCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV, 84e5dd7070Spatrick CheckerContext &C) const; 85e5dd7070Spatrick void evalCastAs(const CallEvent &Call, DefinedOrUnknownSVal DV, 86e5dd7070Spatrick CheckerContext &C) const; 87e5dd7070Spatrick void evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV, 88e5dd7070Spatrick CheckerContext &C) const; 89e5dd7070Spatrick void evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV, 90e5dd7070Spatrick CheckerContext &C) const; 91e5dd7070Spatrick void evalIsaAndNonNull(const CallEvent &Call, DefinedOrUnknownSVal DV, 92e5dd7070Spatrick CheckerContext &C) const; 93e5dd7070Spatrick }; 94e5dd7070Spatrick } // namespace 95e5dd7070Spatrick 96e5dd7070Spatrick static bool isInfeasibleCast(const DynamicCastInfo *CastInfo, 97e5dd7070Spatrick bool CastSucceeds) { 98e5dd7070Spatrick if (!CastInfo) 99e5dd7070Spatrick return false; 100e5dd7070Spatrick 101e5dd7070Spatrick return CastSucceeds ? CastInfo->fails() : CastInfo->succeeds(); 102e5dd7070Spatrick } 103e5dd7070Spatrick 104e5dd7070Spatrick static const NoteTag *getNoteTag(CheckerContext &C, 105e5dd7070Spatrick const DynamicCastInfo *CastInfo, 106e5dd7070Spatrick QualType CastToTy, const Expr *Object, 107e5dd7070Spatrick bool CastSucceeds, bool IsKnownCast) { 108e5dd7070Spatrick std::string CastToName = 109*ec727ea7Spatrick CastInfo ? CastInfo->to()->getAsCXXRecordDecl()->getNameAsString() 110e5dd7070Spatrick : CastToTy->getPointeeCXXRecordDecl()->getNameAsString(); 111e5dd7070Spatrick Object = Object->IgnoreParenImpCasts(); 112e5dd7070Spatrick 113e5dd7070Spatrick return C.getNoteTag( 114e5dd7070Spatrick [=]() -> std::string { 115e5dd7070Spatrick SmallString<128> Msg; 116e5dd7070Spatrick llvm::raw_svector_ostream Out(Msg); 117e5dd7070Spatrick 118e5dd7070Spatrick if (!IsKnownCast) 119e5dd7070Spatrick Out << "Assuming "; 120e5dd7070Spatrick 121e5dd7070Spatrick if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) { 122e5dd7070Spatrick Out << '\'' << DRE->getDecl()->getNameAsString() << '\''; 123e5dd7070Spatrick } else if (const auto *ME = dyn_cast<MemberExpr>(Object)) { 124e5dd7070Spatrick Out << (IsKnownCast ? "Field '" : "field '") 125e5dd7070Spatrick << ME->getMemberDecl()->getNameAsString() << '\''; 126e5dd7070Spatrick } else { 127e5dd7070Spatrick Out << (IsKnownCast ? "The object" : "the object"); 128e5dd7070Spatrick } 129e5dd7070Spatrick 130e5dd7070Spatrick Out << ' ' << (CastSucceeds ? "is a" : "is not a") << " '" << CastToName 131e5dd7070Spatrick << '\''; 132e5dd7070Spatrick 133*ec727ea7Spatrick return std::string(Out.str()); 134*ec727ea7Spatrick }, 135*ec727ea7Spatrick /*IsPrunable=*/true); 136*ec727ea7Spatrick } 137*ec727ea7Spatrick 138*ec727ea7Spatrick static const NoteTag *getNoteTag(CheckerContext &C, 139*ec727ea7Spatrick SmallVector<QualType, 4> CastToTyVec, 140*ec727ea7Spatrick const Expr *Object, 141*ec727ea7Spatrick bool IsKnownCast) { 142*ec727ea7Spatrick Object = Object->IgnoreParenImpCasts(); 143*ec727ea7Spatrick 144*ec727ea7Spatrick return C.getNoteTag( 145*ec727ea7Spatrick [=]() -> std::string { 146*ec727ea7Spatrick SmallString<128> Msg; 147*ec727ea7Spatrick llvm::raw_svector_ostream Out(Msg); 148*ec727ea7Spatrick 149*ec727ea7Spatrick if (!IsKnownCast) 150*ec727ea7Spatrick Out << "Assuming "; 151*ec727ea7Spatrick 152*ec727ea7Spatrick if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) { 153*ec727ea7Spatrick Out << '\'' << DRE->getDecl()->getNameAsString() << '\''; 154*ec727ea7Spatrick } else if (const auto *ME = dyn_cast<MemberExpr>(Object)) { 155*ec727ea7Spatrick Out << (IsKnownCast ? "Field '" : "field '") 156*ec727ea7Spatrick << ME->getMemberDecl()->getNameAsString() << '\''; 157*ec727ea7Spatrick } else { 158*ec727ea7Spatrick Out << (IsKnownCast ? "The object" : "the object"); 159*ec727ea7Spatrick } 160*ec727ea7Spatrick Out << " is"; 161*ec727ea7Spatrick 162*ec727ea7Spatrick bool First = true; 163*ec727ea7Spatrick for (QualType CastToTy: CastToTyVec) { 164*ec727ea7Spatrick std::string CastToName = 165*ec727ea7Spatrick CastToTy->getAsCXXRecordDecl() ? 166*ec727ea7Spatrick CastToTy->getAsCXXRecordDecl()->getNameAsString() : 167*ec727ea7Spatrick CastToTy->getPointeeCXXRecordDecl()->getNameAsString(); 168*ec727ea7Spatrick Out << ' ' << ((CastToTyVec.size() == 1) ? "not" : 169*ec727ea7Spatrick (First ? "neither" : "nor")) << " a '" << CastToName 170*ec727ea7Spatrick << '\''; 171*ec727ea7Spatrick First = false; 172*ec727ea7Spatrick } 173*ec727ea7Spatrick 174*ec727ea7Spatrick return std::string(Out.str()); 175e5dd7070Spatrick }, 176e5dd7070Spatrick /*IsPrunable=*/true); 177e5dd7070Spatrick } 178e5dd7070Spatrick 179e5dd7070Spatrick //===----------------------------------------------------------------------===// 180e5dd7070Spatrick // Main logic to evaluate a cast. 181e5dd7070Spatrick //===----------------------------------------------------------------------===// 182e5dd7070Spatrick 183e5dd7070Spatrick static QualType alignReferenceTypes(QualType toAlign, QualType alignTowards, 184e5dd7070Spatrick ASTContext &ACtx) { 185e5dd7070Spatrick if (alignTowards->isLValueReferenceType() && 186e5dd7070Spatrick alignTowards.isConstQualified()) { 187e5dd7070Spatrick toAlign.addConst(); 188e5dd7070Spatrick return ACtx.getLValueReferenceType(toAlign); 189e5dd7070Spatrick } else if (alignTowards->isLValueReferenceType()) 190e5dd7070Spatrick return ACtx.getLValueReferenceType(toAlign); 191e5dd7070Spatrick else if (alignTowards->isRValueReferenceType()) 192e5dd7070Spatrick return ACtx.getRValueReferenceType(toAlign); 193e5dd7070Spatrick 194e5dd7070Spatrick llvm_unreachable("Must align towards a reference type!"); 195e5dd7070Spatrick } 196e5dd7070Spatrick 197e5dd7070Spatrick static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV, 198e5dd7070Spatrick CheckerContext &C, bool IsNonNullParam, 199e5dd7070Spatrick bool IsNonNullReturn, 200e5dd7070Spatrick bool IsCheckedCast = false) { 201e5dd7070Spatrick ProgramStateRef State = C.getState()->assume(DV, IsNonNullParam); 202e5dd7070Spatrick if (!State) 203e5dd7070Spatrick return; 204e5dd7070Spatrick 205e5dd7070Spatrick const Expr *Object; 206e5dd7070Spatrick QualType CastFromTy; 207e5dd7070Spatrick QualType CastToTy = Call.getResultType(); 208e5dd7070Spatrick 209e5dd7070Spatrick if (Call.getNumArgs() > 0) { 210e5dd7070Spatrick Object = Call.getArgExpr(0); 211e5dd7070Spatrick CastFromTy = Call.parameters()[0]->getType(); 212e5dd7070Spatrick } else { 213e5dd7070Spatrick Object = cast<CXXInstanceCall>(&Call)->getCXXThisExpr(); 214e5dd7070Spatrick CastFromTy = Object->getType(); 215e5dd7070Spatrick if (CastToTy->isPointerType()) { 216e5dd7070Spatrick if (!CastFromTy->isPointerType()) 217e5dd7070Spatrick return; 218e5dd7070Spatrick } else { 219e5dd7070Spatrick if (!CastFromTy->isReferenceType()) 220e5dd7070Spatrick return; 221e5dd7070Spatrick 222e5dd7070Spatrick CastFromTy = alignReferenceTypes(CastFromTy, CastToTy, C.getASTContext()); 223e5dd7070Spatrick } 224e5dd7070Spatrick } 225e5dd7070Spatrick 226e5dd7070Spatrick const MemRegion *MR = DV.getAsRegion(); 227e5dd7070Spatrick const DynamicCastInfo *CastInfo = 228e5dd7070Spatrick getDynamicCastInfo(State, MR, CastFromTy, CastToTy); 229e5dd7070Spatrick 230e5dd7070Spatrick // We assume that every checked cast succeeds. 231e5dd7070Spatrick bool CastSucceeds = IsCheckedCast || CastFromTy == CastToTy; 232e5dd7070Spatrick if (!CastSucceeds) { 233e5dd7070Spatrick if (CastInfo) 234e5dd7070Spatrick CastSucceeds = IsNonNullReturn && CastInfo->succeeds(); 235e5dd7070Spatrick else 236e5dd7070Spatrick CastSucceeds = IsNonNullReturn; 237e5dd7070Spatrick } 238e5dd7070Spatrick 239e5dd7070Spatrick // Check for infeasible casts. 240e5dd7070Spatrick if (isInfeasibleCast(CastInfo, CastSucceeds)) { 241e5dd7070Spatrick C.generateSink(State, C.getPredecessor()); 242e5dd7070Spatrick return; 243e5dd7070Spatrick } 244e5dd7070Spatrick 245e5dd7070Spatrick // Store the type and the cast information. 246e5dd7070Spatrick bool IsKnownCast = CastInfo || IsCheckedCast || CastFromTy == CastToTy; 247e5dd7070Spatrick if (!IsKnownCast || IsCheckedCast) 248e5dd7070Spatrick State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy, 249e5dd7070Spatrick CastSucceeds); 250e5dd7070Spatrick 251e5dd7070Spatrick SVal V = CastSucceeds ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy) 252e5dd7070Spatrick : C.getSValBuilder().makeNull(); 253e5dd7070Spatrick C.addTransition( 254e5dd7070Spatrick State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false), 255e5dd7070Spatrick getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast)); 256e5dd7070Spatrick } 257e5dd7070Spatrick 258e5dd7070Spatrick static void addInstanceOfTransition(const CallEvent &Call, 259e5dd7070Spatrick DefinedOrUnknownSVal DV, 260e5dd7070Spatrick ProgramStateRef State, CheckerContext &C, 261e5dd7070Spatrick bool IsInstanceOf) { 262e5dd7070Spatrick const FunctionDecl *FD = Call.getDecl()->getAsFunction(); 263e5dd7070Spatrick QualType CastFromTy = Call.parameters()[0]->getType(); 264*ec727ea7Spatrick SmallVector<QualType, 4> CastToTyVec; 265*ec727ea7Spatrick for (unsigned idx = 0; idx < FD->getTemplateSpecializationArgs()->size() - 1; 266*ec727ea7Spatrick ++idx) { 267*ec727ea7Spatrick TemplateArgument CastToTempArg = 268*ec727ea7Spatrick FD->getTemplateSpecializationArgs()->get(idx); 269*ec727ea7Spatrick switch (CastToTempArg.getKind()) { 270*ec727ea7Spatrick default: 271*ec727ea7Spatrick return; 272*ec727ea7Spatrick case TemplateArgument::Type: 273*ec727ea7Spatrick CastToTyVec.push_back(CastToTempArg.getAsType()); 274*ec727ea7Spatrick break; 275*ec727ea7Spatrick case TemplateArgument::Pack: 276*ec727ea7Spatrick for (TemplateArgument ArgInPack: CastToTempArg.pack_elements()) 277*ec727ea7Spatrick CastToTyVec.push_back(ArgInPack.getAsType()); 278*ec727ea7Spatrick break; 279*ec727ea7Spatrick } 280*ec727ea7Spatrick } 281*ec727ea7Spatrick 282*ec727ea7Spatrick const MemRegion *MR = DV.getAsRegion(); 283*ec727ea7Spatrick if (MR && CastFromTy->isReferenceType()) 284*ec727ea7Spatrick MR = State->getSVal(DV.castAs<Loc>()).getAsRegion(); 285*ec727ea7Spatrick 286*ec727ea7Spatrick bool Success = false; 287*ec727ea7Spatrick bool IsAnyKnown = false; 288*ec727ea7Spatrick for (QualType CastToTy: CastToTyVec) { 289e5dd7070Spatrick if (CastFromTy->isPointerType()) 290e5dd7070Spatrick CastToTy = C.getASTContext().getPointerType(CastToTy); 291e5dd7070Spatrick else if (CastFromTy->isReferenceType()) 292e5dd7070Spatrick CastToTy = alignReferenceTypes(CastToTy, CastFromTy, C.getASTContext()); 293e5dd7070Spatrick else 294e5dd7070Spatrick return; 295e5dd7070Spatrick 296e5dd7070Spatrick const DynamicCastInfo *CastInfo = 297e5dd7070Spatrick getDynamicCastInfo(State, MR, CastFromTy, CastToTy); 298e5dd7070Spatrick 299e5dd7070Spatrick bool CastSucceeds; 300e5dd7070Spatrick if (CastInfo) 301e5dd7070Spatrick CastSucceeds = IsInstanceOf && CastInfo->succeeds(); 302e5dd7070Spatrick else 303e5dd7070Spatrick CastSucceeds = IsInstanceOf || CastFromTy == CastToTy; 304e5dd7070Spatrick 305e5dd7070Spatrick // Store the type and the cast information. 306e5dd7070Spatrick bool IsKnownCast = CastInfo || CastFromTy == CastToTy; 307*ec727ea7Spatrick IsAnyKnown = IsAnyKnown || IsKnownCast; 308*ec727ea7Spatrick ProgramStateRef NewState = State; 309e5dd7070Spatrick if (!IsKnownCast) 310*ec727ea7Spatrick NewState = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy, 311e5dd7070Spatrick IsInstanceOf); 312e5dd7070Spatrick 313*ec727ea7Spatrick if (CastSucceeds) { 314*ec727ea7Spatrick Success = true; 315*ec727ea7Spatrick C.addTransition( 316*ec727ea7Spatrick NewState->BindExpr(Call.getOriginExpr(), C.getLocationContext(), 317*ec727ea7Spatrick C.getSValBuilder().makeTruthVal(true)), 318*ec727ea7Spatrick getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), true, 319*ec727ea7Spatrick IsKnownCast)); 320*ec727ea7Spatrick if (IsKnownCast) 321*ec727ea7Spatrick return; 322*ec727ea7Spatrick } else if (CastInfo && CastInfo->succeeds()) { 323*ec727ea7Spatrick C.generateSink(NewState, C.getPredecessor()); 324*ec727ea7Spatrick return; 325*ec727ea7Spatrick } 326*ec727ea7Spatrick } 327*ec727ea7Spatrick 328*ec727ea7Spatrick if (!Success) { 329e5dd7070Spatrick C.addTransition( 330e5dd7070Spatrick State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), 331*ec727ea7Spatrick C.getSValBuilder().makeTruthVal(false)), 332*ec727ea7Spatrick getNoteTag(C, CastToTyVec, Call.getArgExpr(0), IsAnyKnown)); 333*ec727ea7Spatrick } 334e5dd7070Spatrick } 335e5dd7070Spatrick 336e5dd7070Spatrick //===----------------------------------------------------------------------===// 337e5dd7070Spatrick // Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null. 338e5dd7070Spatrick //===----------------------------------------------------------------------===// 339e5dd7070Spatrick 340e5dd7070Spatrick static void evalNonNullParamNonNullReturn(const CallEvent &Call, 341e5dd7070Spatrick DefinedOrUnknownSVal DV, 342e5dd7070Spatrick CheckerContext &C, 343e5dd7070Spatrick bool IsCheckedCast = false) { 344e5dd7070Spatrick addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, 345e5dd7070Spatrick /*IsNonNullReturn=*/true, IsCheckedCast); 346e5dd7070Spatrick } 347e5dd7070Spatrick 348e5dd7070Spatrick static void evalNonNullParamNullReturn(const CallEvent &Call, 349e5dd7070Spatrick DefinedOrUnknownSVal DV, 350e5dd7070Spatrick CheckerContext &C) { 351e5dd7070Spatrick addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, 352e5dd7070Spatrick /*IsNonNullReturn=*/false); 353e5dd7070Spatrick } 354e5dd7070Spatrick 355e5dd7070Spatrick static void evalNullParamNullReturn(const CallEvent &Call, 356e5dd7070Spatrick DefinedOrUnknownSVal DV, 357e5dd7070Spatrick CheckerContext &C) { 358e5dd7070Spatrick if (ProgramStateRef State = C.getState()->assume(DV, false)) 359e5dd7070Spatrick C.addTransition(State->BindExpr(Call.getOriginExpr(), 360e5dd7070Spatrick C.getLocationContext(), 361e5dd7070Spatrick C.getSValBuilder().makeNull(), false), 362e5dd7070Spatrick C.getNoteTag("Assuming null pointer is passed into cast", 363e5dd7070Spatrick /*IsPrunable=*/true)); 364e5dd7070Spatrick } 365e5dd7070Spatrick 366e5dd7070Spatrick void CastValueChecker::evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV, 367e5dd7070Spatrick CheckerContext &C) const { 368e5dd7070Spatrick evalNonNullParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true); 369e5dd7070Spatrick } 370e5dd7070Spatrick 371e5dd7070Spatrick void CastValueChecker::evalDynCast(const CallEvent &Call, 372e5dd7070Spatrick DefinedOrUnknownSVal DV, 373e5dd7070Spatrick CheckerContext &C) const { 374e5dd7070Spatrick evalNonNullParamNonNullReturn(Call, DV, C); 375e5dd7070Spatrick evalNonNullParamNullReturn(Call, DV, C); 376e5dd7070Spatrick } 377e5dd7070Spatrick 378e5dd7070Spatrick void CastValueChecker::evalCastOrNull(const CallEvent &Call, 379e5dd7070Spatrick DefinedOrUnknownSVal DV, 380e5dd7070Spatrick CheckerContext &C) const { 381e5dd7070Spatrick evalNonNullParamNonNullReturn(Call, DV, C); 382e5dd7070Spatrick evalNullParamNullReturn(Call, DV, C); 383e5dd7070Spatrick } 384e5dd7070Spatrick 385e5dd7070Spatrick void CastValueChecker::evalDynCastOrNull(const CallEvent &Call, 386e5dd7070Spatrick DefinedOrUnknownSVal DV, 387e5dd7070Spatrick CheckerContext &C) const { 388e5dd7070Spatrick evalNonNullParamNonNullReturn(Call, DV, C); 389e5dd7070Spatrick evalNonNullParamNullReturn(Call, DV, C); 390e5dd7070Spatrick evalNullParamNullReturn(Call, DV, C); 391e5dd7070Spatrick } 392e5dd7070Spatrick 393e5dd7070Spatrick //===----------------------------------------------------------------------===// 394e5dd7070Spatrick // Evaluating castAs, getAs. 395e5dd7070Spatrick //===----------------------------------------------------------------------===// 396e5dd7070Spatrick 397e5dd7070Spatrick static void evalZeroParamNonNullReturn(const CallEvent &Call, 398e5dd7070Spatrick DefinedOrUnknownSVal DV, 399e5dd7070Spatrick CheckerContext &C, 400e5dd7070Spatrick bool IsCheckedCast = false) { 401e5dd7070Spatrick addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, 402e5dd7070Spatrick /*IsNonNullReturn=*/true, IsCheckedCast); 403e5dd7070Spatrick } 404e5dd7070Spatrick 405e5dd7070Spatrick static void evalZeroParamNullReturn(const CallEvent &Call, 406e5dd7070Spatrick DefinedOrUnknownSVal DV, 407e5dd7070Spatrick CheckerContext &C) { 408e5dd7070Spatrick addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, 409e5dd7070Spatrick /*IsNonNullReturn=*/false); 410e5dd7070Spatrick } 411e5dd7070Spatrick 412e5dd7070Spatrick void CastValueChecker::evalCastAs(const CallEvent &Call, 413e5dd7070Spatrick DefinedOrUnknownSVal DV, 414e5dd7070Spatrick CheckerContext &C) const { 415e5dd7070Spatrick evalZeroParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true); 416e5dd7070Spatrick } 417e5dd7070Spatrick 418e5dd7070Spatrick void CastValueChecker::evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV, 419e5dd7070Spatrick CheckerContext &C) const { 420e5dd7070Spatrick evalZeroParamNonNullReturn(Call, DV, C); 421e5dd7070Spatrick evalZeroParamNullReturn(Call, DV, C); 422e5dd7070Spatrick } 423e5dd7070Spatrick 424e5dd7070Spatrick //===----------------------------------------------------------------------===// 425e5dd7070Spatrick // Evaluating isa, isa_and_nonnull. 426e5dd7070Spatrick //===----------------------------------------------------------------------===// 427e5dd7070Spatrick 428e5dd7070Spatrick void CastValueChecker::evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV, 429e5dd7070Spatrick CheckerContext &C) const { 430e5dd7070Spatrick ProgramStateRef NonNullState, NullState; 431e5dd7070Spatrick std::tie(NonNullState, NullState) = C.getState()->assume(DV); 432e5dd7070Spatrick 433e5dd7070Spatrick if (NonNullState) { 434e5dd7070Spatrick addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true); 435e5dd7070Spatrick addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false); 436e5dd7070Spatrick } 437e5dd7070Spatrick 438e5dd7070Spatrick if (NullState) { 439e5dd7070Spatrick C.generateSink(NullState, C.getPredecessor()); 440e5dd7070Spatrick } 441e5dd7070Spatrick } 442e5dd7070Spatrick 443e5dd7070Spatrick void CastValueChecker::evalIsaAndNonNull(const CallEvent &Call, 444e5dd7070Spatrick DefinedOrUnknownSVal DV, 445e5dd7070Spatrick CheckerContext &C) const { 446e5dd7070Spatrick ProgramStateRef NonNullState, NullState; 447e5dd7070Spatrick std::tie(NonNullState, NullState) = C.getState()->assume(DV); 448e5dd7070Spatrick 449e5dd7070Spatrick if (NonNullState) { 450e5dd7070Spatrick addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true); 451e5dd7070Spatrick addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false); 452e5dd7070Spatrick } 453e5dd7070Spatrick 454e5dd7070Spatrick if (NullState) { 455e5dd7070Spatrick addInstanceOfTransition(Call, DV, NullState, C, /*IsInstanceOf=*/false); 456e5dd7070Spatrick } 457e5dd7070Spatrick } 458e5dd7070Spatrick 459e5dd7070Spatrick //===----------------------------------------------------------------------===// 460e5dd7070Spatrick // Main logic to evaluate a call. 461e5dd7070Spatrick //===----------------------------------------------------------------------===// 462e5dd7070Spatrick 463e5dd7070Spatrick bool CastValueChecker::evalCall(const CallEvent &Call, 464e5dd7070Spatrick CheckerContext &C) const { 465e5dd7070Spatrick const auto *Lookup = CDM.lookup(Call); 466e5dd7070Spatrick if (!Lookup) 467e5dd7070Spatrick return false; 468e5dd7070Spatrick 469e5dd7070Spatrick const CastCheck &Check = Lookup->first; 470e5dd7070Spatrick CallKind Kind = Lookup->second; 471e5dd7070Spatrick 472e5dd7070Spatrick Optional<DefinedOrUnknownSVal> DV; 473e5dd7070Spatrick 474e5dd7070Spatrick switch (Kind) { 475e5dd7070Spatrick case CallKind::Function: { 476e5dd7070Spatrick // We only model casts from pointers to pointers or from references 477e5dd7070Spatrick // to references. Other casts are most likely specialized and we 478e5dd7070Spatrick // cannot model them. 479e5dd7070Spatrick QualType ParamT = Call.parameters()[0]->getType(); 480e5dd7070Spatrick QualType ResultT = Call.getResultType(); 481e5dd7070Spatrick if (!(ParamT->isPointerType() && ResultT->isPointerType()) && 482*ec727ea7Spatrick !(ParamT->isReferenceType() && ResultT->isReferenceType())) { 483e5dd7070Spatrick return false; 484*ec727ea7Spatrick } 485e5dd7070Spatrick 486e5dd7070Spatrick DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>(); 487e5dd7070Spatrick break; 488e5dd7070Spatrick } 489e5dd7070Spatrick case CallKind::InstanceOf: { 490e5dd7070Spatrick // We need to obtain the only template argument to determinte the type. 491e5dd7070Spatrick const FunctionDecl *FD = Call.getDecl()->getAsFunction(); 492e5dd7070Spatrick if (!FD || !FD->getTemplateSpecializationArgs()) 493e5dd7070Spatrick return false; 494e5dd7070Spatrick 495e5dd7070Spatrick DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>(); 496e5dd7070Spatrick break; 497e5dd7070Spatrick } 498e5dd7070Spatrick case CallKind::Method: 499e5dd7070Spatrick const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call); 500e5dd7070Spatrick if (!InstanceCall) 501e5dd7070Spatrick return false; 502e5dd7070Spatrick 503e5dd7070Spatrick DV = InstanceCall->getCXXThisVal().getAs<DefinedOrUnknownSVal>(); 504e5dd7070Spatrick break; 505e5dd7070Spatrick } 506e5dd7070Spatrick 507e5dd7070Spatrick if (!DV) 508e5dd7070Spatrick return false; 509e5dd7070Spatrick 510e5dd7070Spatrick Check(this, Call, *DV, C); 511e5dd7070Spatrick return true; 512e5dd7070Spatrick } 513e5dd7070Spatrick 514*ec727ea7Spatrick void CastValueChecker::checkDeadSymbols(SymbolReaper &SR, 515*ec727ea7Spatrick CheckerContext &C) const { 516*ec727ea7Spatrick C.addTransition(removeDeadCasts(C.getState(), SR)); 517*ec727ea7Spatrick } 518*ec727ea7Spatrick 519e5dd7070Spatrick void ento::registerCastValueChecker(CheckerManager &Mgr) { 520e5dd7070Spatrick Mgr.registerChecker<CastValueChecker>(); 521e5dd7070Spatrick } 522e5dd7070Spatrick 523*ec727ea7Spatrick bool ento::shouldRegisterCastValueChecker(const CheckerManager &mgr) { 524e5dd7070Spatrick return true; 525e5dd7070Spatrick } 526