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/CallEvent.h" 24 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 25 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" 26 #include "llvm/ADT/Optional.h" 27 #include <utility> 28 29 using namespace clang; 30 using namespace ento; 31 32 namespace { 33 class CastValueChecker : public Checker<check::DeadSymbols, eval::Call> { 34 enum class CallKind { Function, Method, InstanceOf }; 35 36 using CastCheck = 37 std::function<void(const CastValueChecker *, const CallEvent &Call, 38 DefinedOrUnknownSVal, CheckerContext &)>; 39 40 public: 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. 53 bool evalCall(const CallEvent &Call, CheckerContext &C) const; 54 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 55 56 private: 57 // These are known in the LLVM project. The pairs are in the following form: 58 // {{{namespace, call}, argument-count}, {callback, kind}} 59 const CallDescriptionMap<std::pair<CastCheck, CallKind>> CDM = { 60 {{{"llvm", "cast"}, 1}, 61 {&CastValueChecker::evalCast, CallKind::Function}}, 62 {{{"llvm", "dyn_cast"}, 1}, 63 {&CastValueChecker::evalDynCast, CallKind::Function}}, 64 {{{"llvm", "cast_or_null"}, 1}, 65 {&CastValueChecker::evalCastOrNull, CallKind::Function}}, 66 {{{"llvm", "dyn_cast_or_null"}, 1}, 67 {&CastValueChecker::evalDynCastOrNull, CallKind::Function}}, 68 {{{"clang", "castAs"}, 0}, 69 {&CastValueChecker::evalCastAs, CallKind::Method}}, 70 {{{"clang", "getAs"}, 0}, 71 {&CastValueChecker::evalGetAs, CallKind::Method}}, 72 {{{"llvm", "isa"}, 1}, 73 {&CastValueChecker::evalIsa, CallKind::InstanceOf}}, 74 {{{"llvm", "isa_and_nonnull"}, 1}, 75 {&CastValueChecker::evalIsaAndNonNull, CallKind::InstanceOf}}}; 76 77 void evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV, 78 CheckerContext &C) const; 79 void evalDynCast(const CallEvent &Call, DefinedOrUnknownSVal DV, 80 CheckerContext &C) const; 81 void evalCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV, 82 CheckerContext &C) const; 83 void evalDynCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV, 84 CheckerContext &C) const; 85 void evalCastAs(const CallEvent &Call, DefinedOrUnknownSVal DV, 86 CheckerContext &C) const; 87 void evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV, 88 CheckerContext &C) const; 89 void evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV, 90 CheckerContext &C) const; 91 void evalIsaAndNonNull(const CallEvent &Call, DefinedOrUnknownSVal DV, 92 CheckerContext &C) const; 93 }; 94 } // namespace 95 96 static bool isInfeasibleCast(const DynamicCastInfo *CastInfo, 97 bool CastSucceeds) { 98 if (!CastInfo) 99 return false; 100 101 return CastSucceeds ? CastInfo->fails() : CastInfo->succeeds(); 102 } 103 104 static const NoteTag *getNoteTag(CheckerContext &C, 105 const DynamicCastInfo *CastInfo, 106 QualType CastToTy, const Expr *Object, 107 bool CastSucceeds, bool IsKnownCast) { 108 std::string CastToName = 109 CastInfo ? CastInfo->to()->getPointeeCXXRecordDecl()->getNameAsString() 110 : CastToTy->getPointeeCXXRecordDecl()->getNameAsString(); 111 Object = Object->IgnoreParenImpCasts(); 112 113 return C.getNoteTag( 114 [=]() -> std::string { 115 SmallString<128> Msg; 116 llvm::raw_svector_ostream Out(Msg); 117 118 if (!IsKnownCast) 119 Out << "Assuming "; 120 121 if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) { 122 Out << '\'' << DRE->getDecl()->getNameAsString() << '\''; 123 } else if (const auto *ME = dyn_cast<MemberExpr>(Object)) { 124 Out << (IsKnownCast ? "Field '" : "field '") 125 << ME->getMemberDecl()->getNameAsString() << '\''; 126 } else { 127 Out << (IsKnownCast ? "The object" : "the object"); 128 } 129 130 Out << ' ' << (CastSucceeds ? "is a" : "is not a") << " '" << CastToName 131 << '\''; 132 133 return std::string(Out.str()); 134 }, 135 /*IsPrunable=*/true); 136 } 137 138 //===----------------------------------------------------------------------===// 139 // Main logic to evaluate a cast. 140 //===----------------------------------------------------------------------===// 141 142 static QualType alignReferenceTypes(QualType toAlign, QualType alignTowards, 143 ASTContext &ACtx) { 144 if (alignTowards->isLValueReferenceType() && 145 alignTowards.isConstQualified()) { 146 toAlign.addConst(); 147 return ACtx.getLValueReferenceType(toAlign); 148 } else if (alignTowards->isLValueReferenceType()) 149 return ACtx.getLValueReferenceType(toAlign); 150 else if (alignTowards->isRValueReferenceType()) 151 return ACtx.getRValueReferenceType(toAlign); 152 153 llvm_unreachable("Must align towards a reference type!"); 154 } 155 156 static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV, 157 CheckerContext &C, bool IsNonNullParam, 158 bool IsNonNullReturn, 159 bool IsCheckedCast = false) { 160 ProgramStateRef State = C.getState()->assume(DV, IsNonNullParam); 161 if (!State) 162 return; 163 164 const Expr *Object; 165 QualType CastFromTy; 166 QualType CastToTy = Call.getResultType(); 167 168 if (Call.getNumArgs() > 0) { 169 Object = Call.getArgExpr(0); 170 CastFromTy = Call.parameters()[0]->getType(); 171 } else { 172 Object = cast<CXXInstanceCall>(&Call)->getCXXThisExpr(); 173 CastFromTy = Object->getType(); 174 if (CastToTy->isPointerType()) { 175 if (!CastFromTy->isPointerType()) 176 return; 177 } else { 178 if (!CastFromTy->isReferenceType()) 179 return; 180 181 CastFromTy = alignReferenceTypes(CastFromTy, CastToTy, C.getASTContext()); 182 } 183 } 184 185 const MemRegion *MR = DV.getAsRegion(); 186 const DynamicCastInfo *CastInfo = 187 getDynamicCastInfo(State, MR, CastFromTy, CastToTy); 188 189 // We assume that every checked cast succeeds. 190 bool CastSucceeds = IsCheckedCast || CastFromTy == CastToTy; 191 if (!CastSucceeds) { 192 if (CastInfo) 193 CastSucceeds = IsNonNullReturn && CastInfo->succeeds(); 194 else 195 CastSucceeds = IsNonNullReturn; 196 } 197 198 // Check for infeasible casts. 199 if (isInfeasibleCast(CastInfo, CastSucceeds)) { 200 C.generateSink(State, C.getPredecessor()); 201 return; 202 } 203 204 // Store the type and the cast information. 205 bool IsKnownCast = CastInfo || IsCheckedCast || CastFromTy == CastToTy; 206 if (!IsKnownCast || IsCheckedCast) 207 State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy, 208 CastSucceeds); 209 210 SVal V = CastSucceeds ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy) 211 : C.getSValBuilder().makeNull(); 212 C.addTransition( 213 State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false), 214 getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast)); 215 } 216 217 static void addInstanceOfTransition(const CallEvent &Call, 218 DefinedOrUnknownSVal DV, 219 ProgramStateRef State, CheckerContext &C, 220 bool IsInstanceOf) { 221 const FunctionDecl *FD = Call.getDecl()->getAsFunction(); 222 QualType CastFromTy = Call.parameters()[0]->getType(); 223 QualType CastToTy = FD->getTemplateSpecializationArgs()->get(0).getAsType(); 224 if (CastFromTy->isPointerType()) 225 CastToTy = C.getASTContext().getPointerType(CastToTy); 226 else if (CastFromTy->isReferenceType()) 227 CastToTy = alignReferenceTypes(CastToTy, CastFromTy, C.getASTContext()); 228 else 229 return; 230 231 const MemRegion *MR = DV.getAsRegion(); 232 const DynamicCastInfo *CastInfo = 233 getDynamicCastInfo(State, MR, CastFromTy, CastToTy); 234 235 bool CastSucceeds; 236 if (CastInfo) 237 CastSucceeds = IsInstanceOf && CastInfo->succeeds(); 238 else 239 CastSucceeds = IsInstanceOf || CastFromTy == CastToTy; 240 241 if (isInfeasibleCast(CastInfo, CastSucceeds)) { 242 C.generateSink(State, C.getPredecessor()); 243 return; 244 } 245 246 // Store the type and the cast information. 247 bool IsKnownCast = CastInfo || CastFromTy == CastToTy; 248 if (!IsKnownCast) 249 State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy, 250 IsInstanceOf); 251 252 C.addTransition( 253 State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), 254 C.getSValBuilder().makeTruthVal(CastSucceeds)), 255 getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), CastSucceeds, 256 IsKnownCast)); 257 } 258 259 //===----------------------------------------------------------------------===// 260 // Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null. 261 //===----------------------------------------------------------------------===// 262 263 static void evalNonNullParamNonNullReturn(const CallEvent &Call, 264 DefinedOrUnknownSVal DV, 265 CheckerContext &C, 266 bool IsCheckedCast = false) { 267 addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, 268 /*IsNonNullReturn=*/true, IsCheckedCast); 269 } 270 271 static void evalNonNullParamNullReturn(const CallEvent &Call, 272 DefinedOrUnknownSVal DV, 273 CheckerContext &C) { 274 addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, 275 /*IsNonNullReturn=*/false); 276 } 277 278 static void evalNullParamNullReturn(const CallEvent &Call, 279 DefinedOrUnknownSVal DV, 280 CheckerContext &C) { 281 if (ProgramStateRef State = C.getState()->assume(DV, false)) 282 C.addTransition(State->BindExpr(Call.getOriginExpr(), 283 C.getLocationContext(), 284 C.getSValBuilder().makeNull(), false), 285 C.getNoteTag("Assuming null pointer is passed into cast", 286 /*IsPrunable=*/true)); 287 } 288 289 void CastValueChecker::evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV, 290 CheckerContext &C) const { 291 evalNonNullParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true); 292 } 293 294 void CastValueChecker::evalDynCast(const CallEvent &Call, 295 DefinedOrUnknownSVal DV, 296 CheckerContext &C) const { 297 evalNonNullParamNonNullReturn(Call, DV, C); 298 evalNonNullParamNullReturn(Call, DV, C); 299 } 300 301 void CastValueChecker::evalCastOrNull(const CallEvent &Call, 302 DefinedOrUnknownSVal DV, 303 CheckerContext &C) const { 304 evalNonNullParamNonNullReturn(Call, DV, C); 305 evalNullParamNullReturn(Call, DV, C); 306 } 307 308 void CastValueChecker::evalDynCastOrNull(const CallEvent &Call, 309 DefinedOrUnknownSVal DV, 310 CheckerContext &C) const { 311 evalNonNullParamNonNullReturn(Call, DV, C); 312 evalNonNullParamNullReturn(Call, DV, C); 313 evalNullParamNullReturn(Call, DV, C); 314 } 315 316 //===----------------------------------------------------------------------===// 317 // Evaluating castAs, getAs. 318 //===----------------------------------------------------------------------===// 319 320 static void evalZeroParamNonNullReturn(const CallEvent &Call, 321 DefinedOrUnknownSVal DV, 322 CheckerContext &C, 323 bool IsCheckedCast = false) { 324 addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, 325 /*IsNonNullReturn=*/true, IsCheckedCast); 326 } 327 328 static void evalZeroParamNullReturn(const CallEvent &Call, 329 DefinedOrUnknownSVal DV, 330 CheckerContext &C) { 331 addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, 332 /*IsNonNullReturn=*/false); 333 } 334 335 void CastValueChecker::evalCastAs(const CallEvent &Call, 336 DefinedOrUnknownSVal DV, 337 CheckerContext &C) const { 338 evalZeroParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true); 339 } 340 341 void CastValueChecker::evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV, 342 CheckerContext &C) const { 343 evalZeroParamNonNullReturn(Call, DV, C); 344 evalZeroParamNullReturn(Call, DV, C); 345 } 346 347 //===----------------------------------------------------------------------===// 348 // Evaluating isa, isa_and_nonnull. 349 //===----------------------------------------------------------------------===// 350 351 void CastValueChecker::evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV, 352 CheckerContext &C) const { 353 ProgramStateRef NonNullState, NullState; 354 std::tie(NonNullState, NullState) = C.getState()->assume(DV); 355 356 if (NonNullState) { 357 addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true); 358 addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false); 359 } 360 361 if (NullState) { 362 C.generateSink(NullState, C.getPredecessor()); 363 } 364 } 365 366 void CastValueChecker::evalIsaAndNonNull(const CallEvent &Call, 367 DefinedOrUnknownSVal DV, 368 CheckerContext &C) const { 369 ProgramStateRef NonNullState, NullState; 370 std::tie(NonNullState, NullState) = C.getState()->assume(DV); 371 372 if (NonNullState) { 373 addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true); 374 addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false); 375 } 376 377 if (NullState) { 378 addInstanceOfTransition(Call, DV, NullState, C, /*IsInstanceOf=*/false); 379 } 380 } 381 382 //===----------------------------------------------------------------------===// 383 // Main logic to evaluate a call. 384 //===----------------------------------------------------------------------===// 385 386 bool CastValueChecker::evalCall(const CallEvent &Call, 387 CheckerContext &C) const { 388 const auto *Lookup = CDM.lookup(Call); 389 if (!Lookup) 390 return false; 391 392 const CastCheck &Check = Lookup->first; 393 CallKind Kind = Lookup->second; 394 395 Optional<DefinedOrUnknownSVal> DV; 396 397 switch (Kind) { 398 case CallKind::Function: { 399 // We only model casts from pointers to pointers or from references 400 // to references. Other casts are most likely specialized and we 401 // cannot model them. 402 QualType ParamT = Call.parameters()[0]->getType(); 403 QualType ResultT = Call.getResultType(); 404 if (!(ParamT->isPointerType() && ResultT->isPointerType()) && 405 !(ParamT->isReferenceType() && ResultT->isReferenceType())) 406 return false; 407 408 DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>(); 409 break; 410 } 411 case CallKind::InstanceOf: { 412 // We need to obtain the only template argument to determinte the type. 413 const FunctionDecl *FD = Call.getDecl()->getAsFunction(); 414 if (!FD || !FD->getTemplateSpecializationArgs()) 415 return false; 416 417 DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>(); 418 break; 419 } 420 case CallKind::Method: 421 const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call); 422 if (!InstanceCall) 423 return false; 424 425 DV = InstanceCall->getCXXThisVal().getAs<DefinedOrUnknownSVal>(); 426 break; 427 } 428 429 if (!DV) 430 return false; 431 432 Check(this, Call, *DV, C); 433 return true; 434 } 435 436 void CastValueChecker::checkDeadSymbols(SymbolReaper &SR, 437 CheckerContext &C) const { 438 C.addTransition(removeDeadCasts(C.getState(), SR)); 439 } 440 441 void ento::registerCastValueChecker(CheckerManager &Mgr) { 442 Mgr.registerChecker<CastValueChecker>(); 443 } 444 445 bool ento::shouldRegisterCastValueChecker(const CheckerManager &mgr) { 446 return true; 447 } 448