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