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/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 20 #include "clang/StaticAnalyzer/Core/Checker.h" 21 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 22 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 23 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 24 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" 25 #include "llvm/ADT/Optional.h" 26 #include <utility> 27 28 using namespace clang; 29 using namespace ento; 30 31 namespace { 32 class CastValueChecker : public Checker<eval::Call> { 33 enum class CallKind { Function, Method }; 34 35 using CastCheck = 36 std::function<void(const CastValueChecker *, const CallEvent &Call, 37 DefinedOrUnknownSVal, CheckerContext &)>; 38 39 public: 40 // We have five cases to evaluate a cast: 41 // 1) The parameter is non-null, the return value is non-null. 42 // 2) The parameter is non-null, the return value is null. 43 // 3) The parameter is null, the return value is null. 44 // cast: 1; dyn_cast: 1, 2; cast_or_null: 1, 3; dyn_cast_or_null: 1, 2, 3. 45 // 46 // 4) castAs: Has no parameter, the return value is non-null. 47 // 5) getAs: Has no parameter, the return value is null or non-null. 48 bool evalCall(const CallEvent &Call, CheckerContext &C) const; 49 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 50 51 private: 52 // These are known in the LLVM project. The pairs are in the following form: 53 // {{{namespace, call}, argument-count}, {callback, kind}} 54 const CallDescriptionMap<std::pair<CastCheck, CallKind>> CDM = { 55 {{{"llvm", "cast"}, 1}, 56 {&CastValueChecker::evalCast, CallKind::Function}}, 57 {{{"llvm", "dyn_cast"}, 1}, 58 {&CastValueChecker::evalDynCast, CallKind::Function}}, 59 {{{"llvm", "cast_or_null"}, 1}, 60 {&CastValueChecker::evalCastOrNull, CallKind::Function}}, 61 {{{"llvm", "dyn_cast_or_null"}, 1}, 62 {&CastValueChecker::evalDynCastOrNull, CallKind::Function}}, 63 {{{"clang", "castAs"}, 0}, 64 {&CastValueChecker::evalCastAs, CallKind::Method}}, 65 {{{"clang", "getAs"}, 0}, 66 {&CastValueChecker::evalGetAs, CallKind::Method}}}; 67 68 void evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV, 69 CheckerContext &C) const; 70 void evalDynCast(const CallEvent &Call, DefinedOrUnknownSVal DV, 71 CheckerContext &C) const; 72 void evalCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV, 73 CheckerContext &C) const; 74 void evalDynCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV, 75 CheckerContext &C) const; 76 void evalCastAs(const CallEvent &Call, DefinedOrUnknownSVal DV, 77 CheckerContext &C) const; 78 void evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV, 79 CheckerContext &C) const; 80 }; 81 } // namespace 82 83 static QualType getRecordType(QualType Ty) { 84 Ty = Ty.getCanonicalType(); 85 86 if (Ty->isPointerType()) 87 Ty = Ty->getPointeeType(); 88 89 if (Ty->isReferenceType()) 90 Ty = Ty.getNonReferenceType(); 91 92 return Ty.getUnqualifiedType(); 93 } 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()->getAsCXXRecordDecl()->getNameAsString() 109 : CastToTy->getAsCXXRecordDecl()->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 = getRecordType(Call.getResultType()); 152 153 if (Call.getNumArgs() > 0) { 154 Object = Call.getArgExpr(0); 155 CastFromTy = getRecordType(Call.parameters()[0]->getType()); 156 } else { 157 Object = cast<CXXInstanceCall>(&Call)->getCXXThisExpr(); 158 CastFromTy = getRecordType(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 Call.getResultType(), 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 //===----------------------------------------------------------------------===// 193 // Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null. 194 //===----------------------------------------------------------------------===// 195 196 static void evalNonNullParamNonNullReturn(const CallEvent &Call, 197 DefinedOrUnknownSVal DV, 198 CheckerContext &C, 199 bool IsCheckedCast = false) { 200 addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, 201 /*IsNonNullReturn=*/true, IsCheckedCast); 202 } 203 204 static void evalNonNullParamNullReturn(const CallEvent &Call, 205 DefinedOrUnknownSVal DV, 206 CheckerContext &C) { 207 addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, 208 /*IsNonNullReturn=*/false); 209 } 210 211 static void evalNullParamNullReturn(const CallEvent &Call, 212 DefinedOrUnknownSVal DV, 213 CheckerContext &C) { 214 if (ProgramStateRef State = C.getState()->assume(DV, false)) 215 C.addTransition(State->BindExpr(Call.getOriginExpr(), 216 C.getLocationContext(), 217 C.getSValBuilder().makeNull(), false), 218 C.getNoteTag("Assuming null pointer is passed into cast", 219 /*IsPrunable=*/true)); 220 } 221 222 void CastValueChecker::evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV, 223 CheckerContext &C) const { 224 evalNonNullParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true); 225 } 226 227 void CastValueChecker::evalDynCast(const CallEvent &Call, 228 DefinedOrUnknownSVal DV, 229 CheckerContext &C) const { 230 evalNonNullParamNonNullReturn(Call, DV, C); 231 evalNonNullParamNullReturn(Call, DV, C); 232 } 233 234 void CastValueChecker::evalCastOrNull(const CallEvent &Call, 235 DefinedOrUnknownSVal DV, 236 CheckerContext &C) const { 237 evalNonNullParamNonNullReturn(Call, DV, C); 238 evalNullParamNullReturn(Call, DV, C); 239 } 240 241 void CastValueChecker::evalDynCastOrNull(const CallEvent &Call, 242 DefinedOrUnknownSVal DV, 243 CheckerContext &C) const { 244 evalNonNullParamNonNullReturn(Call, DV, C); 245 evalNonNullParamNullReturn(Call, DV, C); 246 evalNullParamNullReturn(Call, DV, C); 247 } 248 249 //===----------------------------------------------------------------------===// 250 // Evaluating castAs, getAs. 251 //===----------------------------------------------------------------------===// 252 253 static void evalZeroParamNonNullReturn(const CallEvent &Call, 254 DefinedOrUnknownSVal DV, 255 CheckerContext &C, 256 bool IsCheckedCast = false) { 257 addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, 258 /*IsNonNullReturn=*/true, IsCheckedCast); 259 } 260 261 static void evalZeroParamNullReturn(const CallEvent &Call, 262 DefinedOrUnknownSVal DV, 263 CheckerContext &C) { 264 addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, 265 /*IsNonNullReturn=*/false); 266 } 267 268 void CastValueChecker::evalCastAs(const CallEvent &Call, 269 DefinedOrUnknownSVal DV, 270 CheckerContext &C) const { 271 evalZeroParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true); 272 } 273 274 void CastValueChecker::evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV, 275 CheckerContext &C) const { 276 evalZeroParamNonNullReturn(Call, DV, C); 277 evalZeroParamNullReturn(Call, DV, C); 278 } 279 280 //===----------------------------------------------------------------------===// 281 // Main logic to evaluate a call. 282 //===----------------------------------------------------------------------===// 283 284 bool CastValueChecker::evalCall(const CallEvent &Call, 285 CheckerContext &C) const { 286 const auto *Lookup = CDM.lookup(Call); 287 if (!Lookup) 288 return false; 289 290 // We need to obtain the record type of the call's result to model it. 291 if (!getRecordType(Call.getResultType())->isRecordType()) 292 return false; 293 294 const CastCheck &Check = Lookup->first; 295 CallKind Kind = Lookup->second; 296 Optional<DefinedOrUnknownSVal> DV; 297 298 switch (Kind) { 299 case CallKind::Function: { 300 // We need to obtain the record type of the call's parameter to model it. 301 if (!getRecordType(Call.parameters()[0]->getType())->isRecordType()) 302 return false; 303 304 DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>(); 305 break; 306 } 307 case CallKind::Method: 308 const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call); 309 if (!InstanceCall) 310 return false; 311 312 DV = InstanceCall->getCXXThisVal().getAs<DefinedOrUnknownSVal>(); 313 break; 314 } 315 316 if (!DV) 317 return false; 318 319 Check(this, Call, *DV, C); 320 return true; 321 } 322 323 void CastValueChecker::checkDeadSymbols(SymbolReaper &SR, 324 CheckerContext &C) const { 325 C.addTransition(removeDeadCasts(C.getState(), SR)); 326 } 327 328 void ento::registerCastValueChecker(CheckerManager &Mgr) { 329 Mgr.registerChecker<CastValueChecker>(); 330 } 331 332 bool ento::shouldRegisterCastValueChecker(const LangOptions &LO) { 333 return true; 334 } 335