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