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 //===----------------------------------------------------------------------===// 12 13 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 14 #include "clang/StaticAnalyzer/Core/Checker.h" 15 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 16 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 17 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 18 #include "llvm/ADT/Optional.h" 19 #include <utility> 20 21 using namespace clang; 22 using namespace ento; 23 24 namespace { 25 class CastValueChecker : public Checker<eval::Call> { 26 enum class CastKind { Function, Method }; 27 28 using CastCheck = 29 std::function<void(const CastValueChecker *, const CallExpr *, 30 DefinedOrUnknownSVal, CheckerContext &)>; 31 32 using CheckKindPair = std::pair<CastCheck, CastKind>; 33 34 public: 35 // We have five cases to evaluate a cast: 36 // 1) The parameter is non-null, the return value is non-null 37 // 2) The parameter is non-null, the return value is null 38 // 3) The parameter is null, the return value is null 39 // cast: 1; dyn_cast: 1, 2; cast_or_null: 1, 3; dyn_cast_or_null: 1, 2, 3. 40 // 41 // 4) castAs: has no parameter, the return value is non-null. 42 // 5) getAs: has no parameter, the return value is null or non-null. 43 bool evalCall(const CallEvent &Call, CheckerContext &C) const; 44 45 private: 46 // These are known in the LLVM project. The pairs are in the following form: 47 // {{{namespace, call}, argument-count}, {callback, kind}} 48 const CallDescriptionMap<CheckKindPair> CDM = { 49 {{{"llvm", "cast"}, 1}, 50 {&CastValueChecker::evalCast, CastKind::Function}}, 51 {{{"llvm", "dyn_cast"}, 1}, 52 {&CastValueChecker::evalDynCast, CastKind::Function}}, 53 {{{"llvm", "cast_or_null"}, 1}, 54 {&CastValueChecker::evalCastOrNull, CastKind::Function}}, 55 {{{"llvm", "dyn_cast_or_null"}, 1}, 56 {&CastValueChecker::evalDynCastOrNull, CastKind::Function}}, 57 {{{"clang", "castAs"}, 0}, 58 {&CastValueChecker::evalCastAs, CastKind::Method}}, 59 {{{"clang", "getAs"}, 0}, 60 {&CastValueChecker::evalGetAs, CastKind::Method}}}; 61 62 void evalCast(const CallExpr *CE, DefinedOrUnknownSVal DV, 63 CheckerContext &C) const; 64 void evalDynCast(const CallExpr *CE, DefinedOrUnknownSVal DV, 65 CheckerContext &C) const; 66 void evalCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal DV, 67 CheckerContext &C) const; 68 void evalDynCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal DV, 69 CheckerContext &C) const; 70 void evalCastAs(const CallExpr *CE, DefinedOrUnknownSVal DV, 71 CheckerContext &C) const; 72 void evalGetAs(const CallExpr *CE, DefinedOrUnknownSVal DV, 73 CheckerContext &C) const; 74 }; 75 } // namespace 76 77 static std::string getCastName(const Expr *Cast) { 78 QualType Ty = Cast->getType(); 79 if (const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl()) 80 return RD->getNameAsString(); 81 82 return Ty->getPointeeCXXRecordDecl()->getNameAsString(); 83 } 84 85 static const NoteTag *getCastTag(bool IsNullReturn, const CallExpr *CE, 86 CheckerContext &C, 87 bool IsCheckedCast = false) { 88 Optional<std::string> CastFromName = (CE->getNumArgs() > 0) 89 ? getCastName(CE->getArg(0)) 90 : Optional<std::string>(); 91 std::string CastToName = getCastName(CE); 92 93 return C.getNoteTag( 94 [CastFromName, CastToName, IsNullReturn, 95 IsCheckedCast](BugReport &) -> std::string { 96 SmallString<128> Msg; 97 llvm::raw_svector_ostream Out(Msg); 98 99 Out << (!IsCheckedCast ? "Assuming dynamic cast " : "Checked cast "); 100 if (CastFromName) 101 Out << "from '" << *CastFromName << "' "; 102 103 Out << "to '" << CastToName << "' " 104 << (!IsNullReturn ? "succeeds" : "fails"); 105 106 return Out.str(); 107 }, 108 /*IsPrunable=*/true); 109 } 110 111 static ProgramStateRef getState(bool IsNullReturn, 112 DefinedOrUnknownSVal ReturnDV, 113 const CallExpr *CE, ProgramStateRef State, 114 CheckerContext &C) { 115 return State->BindExpr( 116 CE, C.getLocationContext(), 117 IsNullReturn ? C.getSValBuilder().makeNull() : ReturnDV, false); 118 } 119 120 //===----------------------------------------------------------------------===// 121 // Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null. 122 //===----------------------------------------------------------------------===// 123 124 static void evalNonNullParamNonNullReturn(const CallExpr *CE, 125 DefinedOrUnknownSVal DV, 126 CheckerContext &C, 127 bool IsCheckedCast = false) { 128 bool IsNullReturn = false; 129 if (ProgramStateRef State = C.getState()->assume(DV, true)) 130 C.addTransition(getState(IsNullReturn, DV, CE, State, C), 131 getCastTag(IsNullReturn, CE, C, IsCheckedCast)); 132 } 133 134 static void evalNonNullParamNullReturn(const CallExpr *CE, 135 DefinedOrUnknownSVal DV, 136 CheckerContext &C) { 137 bool IsNullReturn = true; 138 if (ProgramStateRef State = C.getState()->assume(DV, true)) 139 C.addTransition(getState(IsNullReturn, DV, CE, State, C), 140 getCastTag(IsNullReturn, CE, C)); 141 } 142 143 static void evalNullParamNullReturn(const CallExpr *CE, DefinedOrUnknownSVal DV, 144 CheckerContext &C) { 145 if (ProgramStateRef State = C.getState()->assume(DV, false)) 146 C.addTransition(getState(/*IsNullReturn=*/true, DV, CE, State, C), 147 C.getNoteTag("Assuming null pointer is passed into cast", 148 /*IsPrunable=*/true)); 149 } 150 151 void CastValueChecker::evalCast(const CallExpr *CE, DefinedOrUnknownSVal DV, 152 CheckerContext &C) const { 153 evalNonNullParamNonNullReturn(CE, DV, C, /*IsCheckedCast=*/true); 154 } 155 156 void CastValueChecker::evalDynCast(const CallExpr *CE, DefinedOrUnknownSVal DV, 157 CheckerContext &C) const { 158 evalNonNullParamNonNullReturn(CE, DV, C); 159 evalNonNullParamNullReturn(CE, DV, C); 160 } 161 162 void CastValueChecker::evalCastOrNull(const CallExpr *CE, 163 DefinedOrUnknownSVal DV, 164 CheckerContext &C) const { 165 evalNonNullParamNonNullReturn(CE, DV, C); 166 evalNullParamNullReturn(CE, DV, C); 167 } 168 169 void CastValueChecker::evalDynCastOrNull(const CallExpr *CE, 170 DefinedOrUnknownSVal DV, 171 CheckerContext &C) const { 172 evalNonNullParamNonNullReturn(CE, DV, C); 173 evalNonNullParamNullReturn(CE, DV, C); 174 evalNullParamNullReturn(CE, DV, C); 175 } 176 177 //===----------------------------------------------------------------------===// 178 // Evaluating castAs, getAs. 179 //===----------------------------------------------------------------------===// 180 181 static void evalZeroParamNonNullReturn(const CallExpr *CE, 182 DefinedOrUnknownSVal DV, 183 CheckerContext &C, 184 bool IsCheckedCast = false) { 185 bool IsNullReturn = false; 186 if (ProgramStateRef State = C.getState()->assume(DV, true)) 187 C.addTransition(getState(IsNullReturn, DV, CE, C.getState(), C), 188 getCastTag(IsNullReturn, CE, C, IsCheckedCast)); 189 } 190 191 static void evalZeroParamNullReturn(const CallExpr *CE, DefinedOrUnknownSVal DV, 192 CheckerContext &C) { 193 bool IsNullReturn = true; 194 if (ProgramStateRef State = C.getState()->assume(DV, true)) 195 C.addTransition(getState(IsNullReturn, DV, CE, C.getState(), C), 196 getCastTag(IsNullReturn, CE, C)); 197 } 198 199 void CastValueChecker::evalCastAs(const CallExpr *CE, DefinedOrUnknownSVal DV, 200 CheckerContext &C) const { 201 evalZeroParamNonNullReturn(CE, DV, C, /*IsCheckedCast=*/true); 202 } 203 204 void CastValueChecker::evalGetAs(const CallExpr *CE, DefinedOrUnknownSVal DV, 205 CheckerContext &C) const { 206 evalZeroParamNonNullReturn(CE, DV, C); 207 evalZeroParamNullReturn(CE, DV, C); 208 } 209 210 bool CastValueChecker::evalCall(const CallEvent &Call, 211 CheckerContext &C) const { 212 const auto *Lookup = CDM.lookup(Call); 213 if (!Lookup) 214 return false; 215 216 // If we cannot obtain the call's class we cannot be sure how to model it. 217 QualType ResultTy = Call.getResultType(); 218 if (!ResultTy->getPointeeCXXRecordDecl()) 219 return false; 220 221 const CastCheck &Check = Lookup->first; 222 CastKind Kind = Lookup->second; 223 224 const auto *CE = cast<CallExpr>(Call.getOriginExpr()); 225 Optional<DefinedOrUnknownSVal> DV; 226 227 switch (Kind) { 228 case CastKind::Function: { 229 // If we cannot obtain the arg's class we cannot be sure how to model it. 230 QualType ArgTy = Call.parameters()[0]->getType(); 231 if (!ArgTy->getAsCXXRecordDecl() && !ArgTy->getPointeeCXXRecordDecl()) 232 return false; 233 234 DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>(); 235 break; 236 } 237 case CastKind::Method: 238 // If we cannot obtain the 'InstanceCall' we cannot be sure how to model it. 239 const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call); 240 if (!InstanceCall) 241 return false; 242 243 DV = InstanceCall->getCXXThisVal().getAs<DefinedOrUnknownSVal>(); 244 break; 245 } 246 247 if (!DV) 248 return false; 249 250 Check(this, CE, *DV, C); 251 return true; 252 } 253 254 void ento::registerCastValueChecker(CheckerManager &Mgr) { 255 Mgr.registerChecker<CastValueChecker>(); 256 } 257 258 bool ento::shouldRegisterCastValueChecker(const LangOptions &LO) { 259 return true; 260 } 261