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 QualType getRecordType(QualType Ty) { 96 Ty = Ty.getCanonicalType(); 97 98 if (Ty->isPointerType()) 99 Ty = Ty->getPointeeType(); 100 101 if (Ty->isReferenceType()) 102 Ty = Ty.getNonReferenceType(); 103 104 return Ty.getUnqualifiedType(); 105 } 106 107 static bool isInfeasibleCast(const DynamicCastInfo *CastInfo, 108 bool CastSucceeds) { 109 if (!CastInfo) 110 return false; 111 112 return CastSucceeds ? CastInfo->fails() : CastInfo->succeeds(); 113 } 114 115 static const NoteTag *getNoteTag(CheckerContext &C, 116 const DynamicCastInfo *CastInfo, 117 QualType CastToTy, const Expr *Object, 118 bool CastSucceeds, bool IsKnownCast) { 119 std::string CastToName = 120 CastInfo ? CastInfo->to()->getAsCXXRecordDecl()->getNameAsString() 121 : CastToTy->getAsCXXRecordDecl()->getNameAsString(); 122 Object = Object->IgnoreParenImpCasts(); 123 124 return C.getNoteTag( 125 [=]() -> std::string { 126 SmallString<128> Msg; 127 llvm::raw_svector_ostream Out(Msg); 128 129 if (!IsKnownCast) 130 Out << "Assuming "; 131 132 if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) { 133 Out << '\'' << DRE->getDecl()->getNameAsString() << '\''; 134 } else if (const auto *ME = dyn_cast<MemberExpr>(Object)) { 135 Out << (IsKnownCast ? "Field '" : "field '") 136 << ME->getMemberDecl()->getNameAsString() << '\''; 137 } else { 138 Out << (IsKnownCast ? "The object" : "the object"); 139 } 140 141 Out << ' ' << (CastSucceeds ? "is a" : "is not a") << " '" << CastToName 142 << '\''; 143 144 return Out.str(); 145 }, 146 /*IsPrunable=*/true); 147 } 148 149 //===----------------------------------------------------------------------===// 150 // Main logic to evaluate a cast. 151 //===----------------------------------------------------------------------===// 152 153 static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV, 154 CheckerContext &C, bool IsNonNullParam, 155 bool IsNonNullReturn, 156 bool IsCheckedCast = false) { 157 ProgramStateRef State = C.getState()->assume(DV, IsNonNullParam); 158 if (!State) 159 return; 160 161 const Expr *Object; 162 QualType CastFromTy; 163 QualType CastToTy = getRecordType(Call.getResultType()); 164 165 if (Call.getNumArgs() > 0) { 166 Object = Call.getArgExpr(0); 167 CastFromTy = getRecordType(Call.parameters()[0]->getType()); 168 } else { 169 Object = cast<CXXInstanceCall>(&Call)->getCXXThisExpr(); 170 CastFromTy = getRecordType(Object->getType()); 171 } 172 173 const MemRegion *MR = DV.getAsRegion(); 174 const DynamicCastInfo *CastInfo = 175 getDynamicCastInfo(State, MR, CastFromTy, CastToTy); 176 177 // We assume that every checked cast succeeds. 178 bool CastSucceeds = IsCheckedCast || CastFromTy == CastToTy; 179 if (!CastSucceeds) { 180 if (CastInfo) 181 CastSucceeds = IsNonNullReturn && CastInfo->succeeds(); 182 else 183 CastSucceeds = IsNonNullReturn; 184 } 185 186 // Check for infeasible casts. 187 if (isInfeasibleCast(CastInfo, CastSucceeds)) { 188 C.generateSink(State, C.getPredecessor()); 189 return; 190 } 191 192 // Store the type and the cast information. 193 bool IsKnownCast = CastInfo || IsCheckedCast || CastFromTy == CastToTy; 194 if (!IsKnownCast || IsCheckedCast) 195 State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy, 196 Call.getResultType(), CastSucceeds); 197 198 SVal V = CastSucceeds ? DV : C.getSValBuilder().makeNull(); 199 C.addTransition( 200 State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false), 201 getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast)); 202 } 203 204 static void addInstanceOfTransition(const CallEvent &Call, 205 DefinedOrUnknownSVal DV, 206 ProgramStateRef State, CheckerContext &C, 207 bool IsInstanceOf) { 208 const FunctionDecl *FD = Call.getDecl()->getAsFunction(); 209 QualType CastToTy = FD->getTemplateSpecializationArgs()->get(0).getAsType(); 210 QualType CastFromTy = getRecordType(Call.parameters()[0]->getType()); 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 Call.getResultType(), 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 // We need to obtain the record type of the call's result to model it. 377 if (Kind != CallKind::InstanceOf && 378 !getRecordType(Call.getResultType())->isRecordType()) 379 return false; 380 381 Optional<DefinedOrUnknownSVal> DV; 382 383 switch (Kind) { 384 case CallKind::Function: { 385 // We need to obtain the record type of the call's parameter to model it. 386 if (!getRecordType(Call.parameters()[0]->getType())->isRecordType()) 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