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 20 using namespace clang; 21 using namespace ento; 22 23 namespace { 24 class CastValueChecker : public Checker<eval::Call> { 25 using CastCheck = 26 std::function<void(const CastValueChecker *, const CallExpr *, 27 DefinedOrUnknownSVal, CheckerContext &)>; 28 29 public: 30 // We have three cases to evaluate a cast: 31 // 1) The parameter is non-null, the return value is non-null 32 // 2) The parameter is non-null, the return value is null 33 // 3) The parameter is null, the return value is null 34 // 35 // cast: 1; dyn_cast: 1, 2; cast_or_null: 1, 3; dyn_cast_or_null: 1, 2, 3. 36 bool evalCall(const CallEvent &Call, CheckerContext &C) const; 37 38 private: 39 // These are known in the LLVM project. 40 const CallDescriptionMap<CastCheck> CDM = { 41 {{{"llvm", "cast"}, 1}, &CastValueChecker::evalCast}, 42 {{{"llvm", "dyn_cast"}, 1}, &CastValueChecker::evalDynCast}, 43 {{{"llvm", "cast_or_null"}, 1}, &CastValueChecker::evalCastOrNull}, 44 {{{"llvm", "dyn_cast_or_null"}, 1}, 45 &CastValueChecker::evalDynCastOrNull}}; 46 47 void evalCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, 48 CheckerContext &C) const; 49 void evalDynCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, 50 CheckerContext &C) const; 51 void evalCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, 52 CheckerContext &C) const; 53 void evalDynCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, 54 CheckerContext &C) const; 55 }; 56 } // namespace 57 58 static std::string getCastName(const Expr *Cast) { 59 return Cast->getType()->getPointeeCXXRecordDecl()->getNameAsString(); 60 } 61 62 static void evalNonNullParamNonNullReturn(const CallExpr *CE, 63 DefinedOrUnknownSVal ParamDV, 64 CheckerContext &C) { 65 ProgramStateRef State = C.getState()->assume(ParamDV, true); 66 if (!State) 67 return; 68 69 State = State->BindExpr(CE, C.getLocationContext(), ParamDV, false); 70 71 std::string CastFromName = getCastName(CE->getArg(0)); 72 std::string CastToName = getCastName(CE); 73 74 const NoteTag *CastTag = C.getNoteTag( 75 [CastFromName, CastToName](BugReport &) -> std::string { 76 SmallString<128> Msg; 77 llvm::raw_svector_ostream Out(Msg); 78 79 Out << "Assuming dynamic cast from '" << CastFromName << "' to '" 80 << CastToName << "' succeeds"; 81 return Out.str(); 82 }, 83 /*IsPrunable=*/true); 84 85 C.addTransition(State, CastTag); 86 } 87 88 static void evalNonNullParamNullReturn(const CallExpr *CE, 89 DefinedOrUnknownSVal ParamDV, 90 CheckerContext &C) { 91 ProgramStateRef State = C.getState()->assume(ParamDV, true); 92 if (!State) 93 return; 94 95 State = State->BindExpr(CE, C.getLocationContext(), 96 C.getSValBuilder().makeNull(), false); 97 98 std::string CastFromName = getCastName(CE->getArg(0)); 99 std::string CastToName = getCastName(CE); 100 101 const NoteTag *CastTag = C.getNoteTag( 102 [CastFromName, CastToName](BugReport &) -> std::string { 103 SmallString<128> Msg; 104 llvm::raw_svector_ostream Out(Msg); 105 106 Out << "Assuming dynamic cast from '" << CastFromName << "' to '" 107 << CastToName << "' fails"; 108 return Out.str(); 109 }, 110 /*IsPrunable=*/true); 111 112 C.addTransition(State, CastTag); 113 } 114 115 static void evalNullParamNullReturn(const CallExpr *CE, 116 DefinedOrUnknownSVal ParamDV, 117 CheckerContext &C) { 118 ProgramStateRef State = C.getState()->assume(ParamDV, false); 119 if (!State) 120 return; 121 122 State = State->BindExpr(CE, C.getLocationContext(), 123 C.getSValBuilder().makeNull(), false); 124 125 const NoteTag *CastTag = 126 C.getNoteTag("Assuming null pointer is passed into cast", 127 /*IsPrunable=*/true); 128 129 C.addTransition(State, CastTag); 130 } 131 132 void CastValueChecker::evalCast(const CallExpr *CE, 133 DefinedOrUnknownSVal ParamDV, 134 CheckerContext &C) const { 135 evalNonNullParamNonNullReturn(CE, ParamDV, C); 136 } 137 138 void CastValueChecker::evalDynCast(const CallExpr *CE, 139 DefinedOrUnknownSVal ParamDV, 140 CheckerContext &C) const { 141 evalNonNullParamNonNullReturn(CE, ParamDV, C); 142 evalNonNullParamNullReturn(CE, ParamDV, C); 143 } 144 145 void CastValueChecker::evalCastOrNull(const CallExpr *CE, 146 DefinedOrUnknownSVal ParamDV, 147 CheckerContext &C) const { 148 evalNonNullParamNonNullReturn(CE, ParamDV, C); 149 evalNullParamNullReturn(CE, ParamDV, C); 150 } 151 152 void CastValueChecker::evalDynCastOrNull(const CallExpr *CE, 153 DefinedOrUnknownSVal ParamDV, 154 CheckerContext &C) const { 155 evalNonNullParamNonNullReturn(CE, ParamDV, C); 156 evalNonNullParamNullReturn(CE, ParamDV, C); 157 evalNullParamNullReturn(CE, ParamDV, C); 158 } 159 160 bool CastValueChecker::evalCall(const CallEvent &Call, 161 CheckerContext &C) const { 162 const CastCheck *Check = CDM.lookup(Call); 163 if (!Check) 164 return false; 165 166 const auto *CE = cast<CallExpr>(Call.getOriginExpr()); 167 if (!CE) 168 return false; 169 170 // If we cannot obtain both of the classes we cannot be sure how to model it. 171 if (!CE->getType()->getPointeeCXXRecordDecl() || 172 !CE->getArg(0)->getType()->getPointeeCXXRecordDecl()) 173 return false; 174 175 SVal ParamV = Call.getArgSVal(0); 176 auto ParamDV = ParamV.getAs<DefinedOrUnknownSVal>(); 177 if (!ParamDV) 178 return false; 179 180 (*Check)(this, CE, *ParamDV, C); 181 return true; 182 } 183 184 void ento::registerCastValueChecker(CheckerManager &Mgr) { 185 Mgr.registerChecker<CastValueChecker>(); 186 } 187 188 bool ento::shouldRegisterCastValueChecker(const LangOptions &LO) { 189 return true; 190 } 191