1//== DynamicTypeChecker.cpp ------------------------------------ -*- C++ -*--=// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This checker looks for cases where the dynamic type of an object is unrelated 11// to its static type. The type information utilized by this check is collected 12// by the DynamicTypePropagation checker. This check does not report any type 13// error for ObjC Generic types, in order to avoid duplicate erros from the 14// ObjC Generics checker. This checker is not supposed to modify the program 15// state, it is just the observer of the type information provided by other 16// checkers. 17// 18//===----------------------------------------------------------------------===// 19 20#include "ClangSACheckers.h" 21#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 22#include "clang/StaticAnalyzer/Core/Checker.h" 23#include "clang/StaticAnalyzer/Core/CheckerManager.h" 24#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 25#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" 26#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 27#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 28 29using namespace clang; 30using namespace ento; 31 32namespace { 33class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> { 34 mutable std::unique_ptr<BugType> BT; 35 void initBugType() const { 36 if (!BT) 37 BT.reset( 38 new BugType(this, "Dynamic and static type mismatch", "Type Error")); 39 } 40 41 class DynamicTypeBugVisitor 42 : public BugReporterVisitorImpl<DynamicTypeBugVisitor> { 43 public: 44 DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {} 45 46 void Profile(llvm::FoldingSetNodeID &ID) const override { 47 static int X = 0; 48 ID.AddPointer(&X); 49 ID.AddPointer(Reg); 50 } 51 52 PathDiagnosticPiece *VisitNode(const ExplodedNode *N, 53 const ExplodedNode *PrevN, 54 BugReporterContext &BRC, 55 BugReport &BR) override; 56 57 private: 58 // The tracked region. 59 const MemRegion *Reg; 60 }; 61 62 void reportTypeError(QualType DynamicType, QualType StaticType, 63 const MemRegion *Reg, const Stmt *ReportedNode, 64 CheckerContext &C) const; 65 66public: 67 void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const; 68}; 69} 70 71void DynamicTypeChecker::reportTypeError(QualType DynamicType, 72 QualType StaticType, 73 const MemRegion *Reg, 74 const Stmt *ReportedNode, 75 CheckerContext &C) const { 76 initBugType(); 77 SmallString<192> Buf; 78 llvm::raw_svector_ostream OS(Buf); 79 OS << "Object has a dynamic type '"; 80 QualType::print(DynamicType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(), 81 llvm::Twine()); 82 OS << "' which is incompatible with static type '"; 83 QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(), 84 llvm::Twine()); 85 OS << "'"; 86 std::unique_ptr<BugReport> R( 87 new BugReport(*BT, OS.str(), C.generateNonFatalErrorNode())); 88 R->markInteresting(Reg); 89 R->addVisitor(llvm::make_unique<DynamicTypeBugVisitor>(Reg)); 90 R->addRange(ReportedNode->getSourceRange()); 91 C.emitReport(std::move(R)); 92} 93 94PathDiagnosticPiece *DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode( 95 const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, 96 BugReport &BR) { 97 ProgramStateRef State = N->getState(); 98 ProgramStateRef StatePrev = PrevN->getState(); 99 100 DynamicTypeInfo TrackedType = getDynamicTypeInfo(State, Reg); 101 DynamicTypeInfo TrackedTypePrev = getDynamicTypeInfo(StatePrev, Reg); 102 if (!TrackedType.isValid()) 103 return nullptr; 104 105 if (TrackedTypePrev.isValid() && 106 TrackedTypePrev.getType() == TrackedType.getType()) 107 return nullptr; 108 109 // Retrieve the associated statement. 110 const Stmt *S = nullptr; 111 ProgramPoint ProgLoc = N->getLocation(); 112 if (Optional<StmtPoint> SP = ProgLoc.getAs<StmtPoint>()) { 113 S = SP->getStmt(); 114 } 115 116 if (!S) 117 return nullptr; 118 119 const LangOptions &LangOpts = BRC.getASTContext().getLangOpts(); 120 121 SmallString<256> Buf; 122 llvm::raw_svector_ostream OS(Buf); 123 OS << "Type '"; 124 QualType::print(TrackedType.getType().getTypePtr(), Qualifiers(), OS, 125 LangOpts, llvm::Twine()); 126 OS << "' is inferred from "; 127 128 if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) { 129 OS << "explicit cast (from '"; 130 QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(), 131 Qualifiers(), OS, LangOpts, llvm::Twine()); 132 OS << "' to '"; 133 QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS, 134 LangOpts, llvm::Twine()); 135 OS << "')"; 136 } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) { 137 OS << "implicit cast (from '"; 138 QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(), 139 Qualifiers(), OS, LangOpts, llvm::Twine()); 140 OS << "' to '"; 141 QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS, 142 LangOpts, llvm::Twine()); 143 OS << "')"; 144 } else { 145 OS << "this context"; 146 } 147 148 // Generate the extra diagnostic. 149 PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 150 N->getLocationContext()); 151 return new PathDiagnosticEventPiece(Pos, OS.str(), true, nullptr); 152} 153 154static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) { 155 const ObjCInterfaceDecl *Decl = ObjPtr->getInterfaceDecl(); 156 if (!Decl) 157 return false; 158 159 return Decl->getDefinition(); 160} 161 162// TODO: consider checking explicit casts? 163void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE, 164 CheckerContext &C) const { 165 // TODO: C++ support. 166 if (CE->getCastKind() != CK_BitCast) 167 return; 168 169 const MemRegion *Region = C.getSVal(CE).getAsRegion(); 170 if (!Region) 171 return; 172 173 ProgramStateRef State = C.getState(); 174 DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region); 175 176 if (!DynTypeInfo.isValid()) 177 return; 178 179 QualType DynType = DynTypeInfo.getType(); 180 QualType StaticType = CE->getType(); 181 182 const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>(); 183 const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>(); 184 185 if (!DynObjCType || !StaticObjCType) 186 return; 187 188 if (!hasDefinition(DynObjCType) || !hasDefinition(StaticObjCType)) 189 return; 190 191 ASTContext &ASTCtxt = C.getASTContext(); 192 193 // Strip kindeofness to correctly detect subtyping relationships. 194 DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt); 195 StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt); 196 197 // Specialized objects are handled by the generics checker. 198 if (StaticObjCType->isSpecialized()) 199 return; 200 201 if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType)) 202 return; 203 204 if (DynTypeInfo.canBeASubClass() && 205 ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType)) 206 return; 207 208 reportTypeError(DynType, StaticType, Region, CE, C); 209} 210 211void ento::registerDynamicTypeChecker(CheckerManager &mgr) { 212 mgr.registerChecker<DynamicTypeChecker>(); 213} 214