CastValueChecker.cpp revision 360660
1276789Sdim//===- CastValueChecker - Model implementation of custom RTTIs --*- C++ -*-===// 2276789Sdim// 3276789Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4276789Sdim// See https://llvm.org/LICENSE.txt for license information. 5276789Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6276789Sdim// 7276789Sdim//===----------------------------------------------------------------------===// 8276789Sdim// 9276789Sdim// This defines CastValueChecker which models casts of custom RTTIs. 10276789Sdim// 11276789Sdim//===----------------------------------------------------------------------===// 12276789Sdim 13276789Sdim#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 14276789Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 15276789Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h" 16276789Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 17276789Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 18276789Sdim#include "llvm/ADT/Optional.h" 19276789Sdim 20276789Sdimusing namespace clang; 21276789Sdimusing namespace ento; 22276789Sdim 23276789Sdimnamespace { 24276789Sdimclass CastValueChecker : public Checker<eval::Call> { 25276789Sdim using CastCheck = 26276789Sdim std::function<void(const CastValueChecker *, const CallExpr *, 27276789Sdim DefinedOrUnknownSVal, CheckerContext &)>; 28276789Sdim 29276789Sdimpublic: 30276789Sdim // We have three cases to evaluate a cast: 31276789Sdim // 1) The parameter is non-null, the return value is non-null 32276789Sdim // 2) The parameter is non-null, the return value is null 33276789Sdim // 3) The parameter is null, the return value is null 34276789Sdim // 35276789Sdim // cast: 1; dyn_cast: 1, 2; cast_or_null: 1, 3; dyn_cast_or_null: 1, 2, 3. 36276789Sdim bool evalCall(const CallEvent &Call, CheckerContext &C) const; 37276789Sdim 38276789Sdimprivate: 39276789Sdim // These are known in the LLVM project. 40276789Sdim const CallDescriptionMap<CastCheck> CDM = { 41276789Sdim {{{"llvm", "cast"}, 1}, &CastValueChecker::evalCast}, 42276789Sdim {{{"llvm", "dyn_cast"}, 1}, &CastValueChecker::evalDynCast}, 43276789Sdim {{{"llvm", "cast_or_null"}, 1}, &CastValueChecker::evalCastOrNull}, 44276789Sdim {{{"llvm", "dyn_cast_or_null"}, 1}, 45276789Sdim &CastValueChecker::evalDynCastOrNull}}; 46276789Sdim 47276789Sdim void evalCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, 48276789Sdim CheckerContext &C) const; 49276789Sdim void evalDynCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, 50276789Sdim CheckerContext &C) const; 51276789Sdim void evalCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, 52276789Sdim CheckerContext &C) const; 53276789Sdim void evalDynCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, 54276789Sdim CheckerContext &C) const; 55276789Sdim}; 56276789Sdim} // namespace 57276789Sdim 58276789Sdimstatic std::string getCastName(const Expr *Cast) { 59276789Sdim return Cast->getType()->getPointeeCXXRecordDecl()->getNameAsString(); 60276789Sdim} 61276789Sdim 62276789Sdimstatic void evalNonNullParamNonNullReturn(const CallExpr *CE, 63276789Sdim DefinedOrUnknownSVal ParamDV, 64276789Sdim CheckerContext &C) { 65276789Sdim ProgramStateRef State = C.getState()->assume(ParamDV, true); 66276789Sdim if (!State) 67276789Sdim return; 68276789Sdim 69276789Sdim State = State->BindExpr(CE, C.getLocationContext(), ParamDV, false); 70276789Sdim 71276789Sdim std::string CastFromName = getCastName(CE->getArg(0)); 72276789Sdim std::string CastToName = getCastName(CE); 73276789Sdim 74276789Sdim const NoteTag *CastTag = C.getNoteTag( 75276789Sdim [CastFromName, CastToName](BugReport &) -> std::string { 76276789Sdim SmallString<128> Msg; 77276789Sdim llvm::raw_svector_ostream Out(Msg); 78276789Sdim 79276789Sdim Out << "Assuming dynamic cast from '" << CastFromName << "' to '" 80276789Sdim << CastToName << "' succeeds"; 81276789Sdim return Out.str(); 82276789Sdim }, 83276789Sdim /*IsPrunable=*/true); 84276789Sdim 85276789Sdim C.addTransition(State, CastTag); 86276789Sdim} 87276789Sdim 88276789Sdimstatic void evalNonNullParamNullReturn(const CallExpr *CE, 89276789Sdim DefinedOrUnknownSVal ParamDV, 90276789Sdim CheckerContext &C) { 91276789Sdim ProgramStateRef State = C.getState()->assume(ParamDV, true); 92276789Sdim if (!State) 93276789Sdim return; 94276789Sdim 95276789Sdim State = State->BindExpr(CE, C.getLocationContext(), 96276789Sdim C.getSValBuilder().makeNull(), false); 97276789Sdim 98276789Sdim std::string CastFromName = getCastName(CE->getArg(0)); 99276789Sdim std::string CastToName = getCastName(CE); 100276789Sdim 101276789Sdim const NoteTag *CastTag = C.getNoteTag( 102276789Sdim [CastFromName, CastToName](BugReport &) -> std::string { 103276789Sdim SmallString<128> Msg; 104276789Sdim llvm::raw_svector_ostream Out(Msg); 105276789Sdim 106276789Sdim Out << "Assuming dynamic cast from '" << CastFromName << "' to '" 107276789Sdim << CastToName << "' fails"; 108276789Sdim return Out.str(); 109276789Sdim }, 110276789Sdim /*IsPrunable=*/true); 111276789Sdim 112276789Sdim C.addTransition(State, CastTag); 113276789Sdim} 114276789Sdim 115276789Sdimstatic void evalNullParamNullReturn(const CallExpr *CE, 116276789Sdim DefinedOrUnknownSVal ParamDV, 117276789Sdim CheckerContext &C) { 118276789Sdim ProgramStateRef State = C.getState()->assume(ParamDV, false); 119276789Sdim if (!State) 120276789Sdim return; 121276789Sdim 122276789Sdim State = State->BindExpr(CE, C.getLocationContext(), 123276789Sdim C.getSValBuilder().makeNull(), false); 124276789Sdim 125276789Sdim const NoteTag *CastTag = 126276789Sdim C.getNoteTag("Assuming null pointer is passed into cast", 127276789Sdim /*IsPrunable=*/true); 128276789Sdim 129276789Sdim C.addTransition(State, CastTag); 130276789Sdim} 131276789Sdim 132276789Sdimvoid CastValueChecker::evalCast(const CallExpr *CE, 133276789Sdim DefinedOrUnknownSVal ParamDV, 134276789Sdim CheckerContext &C) const { 135276789Sdim evalNonNullParamNonNullReturn(CE, ParamDV, C); 136276789Sdim} 137276789Sdim 138276789Sdimvoid CastValueChecker::evalDynCast(const CallExpr *CE, 139276789Sdim DefinedOrUnknownSVal ParamDV, 140276789Sdim CheckerContext &C) const { 141276789Sdim evalNonNullParamNonNullReturn(CE, ParamDV, C); 142276789Sdim evalNonNullParamNullReturn(CE, ParamDV, C); 143276789Sdim} 144276789Sdim 145276789Sdimvoid CastValueChecker::evalCastOrNull(const CallExpr *CE, 146276789Sdim DefinedOrUnknownSVal ParamDV, 147276789Sdim CheckerContext &C) const { 148276789Sdim evalNonNullParamNonNullReturn(CE, ParamDV, C); 149276789Sdim evalNullParamNullReturn(CE, ParamDV, C); 150276789Sdim} 151276789Sdim 152276789Sdimvoid CastValueChecker::evalDynCastOrNull(const CallExpr *CE, 153276789Sdim DefinedOrUnknownSVal ParamDV, 154276789Sdim CheckerContext &C) const { 155276789Sdim evalNonNullParamNonNullReturn(CE, ParamDV, C); 156276789Sdim evalNonNullParamNullReturn(CE, ParamDV, C); 157276789Sdim evalNullParamNullReturn(CE, ParamDV, C); 158276789Sdim} 159276789Sdim 160276789Sdimbool CastValueChecker::evalCall(const CallEvent &Call, 161276789Sdim CheckerContext &C) const { 162309124Sdim const CastCheck *Check = CDM.lookup(Call); 163309124Sdim if (!Check) 164309124Sdim 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