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