CastValueChecker.cpp revision 351350
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 20using namespace clang; 21using namespace ento; 22 23namespace { 24class CastValueChecker : public Checker<eval::Call> { 25 using CastCheck = 26 std::function<void(const CastValueChecker *, const CallExpr *, 27 DefinedOrUnknownSVal, CheckerContext &)>; 28 29public: 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 38private: 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 58static std::string getCastName(const Expr *Cast) { 59 return Cast->getType()->getPointeeCXXRecordDecl()->getNameAsString(); 60} 61 62static 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 88static 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 115static 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 132void CastValueChecker::evalCast(const CallExpr *CE, 133 DefinedOrUnknownSVal ParamDV, 134 CheckerContext &C) const { 135 evalNonNullParamNonNullReturn(CE, ParamDV, C); 136} 137 138void CastValueChecker::evalDynCast(const CallExpr *CE, 139 DefinedOrUnknownSVal ParamDV, 140 CheckerContext &C) const { 141 evalNonNullParamNonNullReturn(CE, ParamDV, C); 142 evalNonNullParamNullReturn(CE, ParamDV, C); 143} 144 145void CastValueChecker::evalCastOrNull(const CallExpr *CE, 146 DefinedOrUnknownSVal ParamDV, 147 CheckerContext &C) const { 148 evalNonNullParamNonNullReturn(CE, ParamDV, C); 149 evalNullParamNullReturn(CE, ParamDV, C); 150} 151 152void 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 160bool 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 184void ento::registerCastValueChecker(CheckerManager &Mgr) { 185 Mgr.registerChecker<CastValueChecker>(); 186} 187 188bool ento::shouldRegisterCastValueChecker(const LangOptions &LO) { 189 return true; 190} 191