1 //=== CastToStructChecker.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 files defines CastToStructChecker, a builtin checker that checks for 11 // cast from non-struct pointer to struct pointer and widening struct data cast. 12 // This check corresponds to CWE-588. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #include "ClangSACheckers.h" 17 #include "clang/AST/RecursiveASTVisitor.h" 18 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 19 #include "clang/StaticAnalyzer/Core/Checker.h" 20 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 21 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 22 23 using namespace clang; 24 using namespace ento; 25 26 namespace { 27 class CastToStructVisitor : public RecursiveASTVisitor<CastToStructVisitor> { 28 BugReporter &BR; 29 const CheckerBase *Checker; 30 AnalysisDeclContext *AC; 31 32 public: 33 explicit CastToStructVisitor(BugReporter &B, const CheckerBase *Checker, 34 AnalysisDeclContext *A) 35 : BR(B), Checker(Checker), AC(A) {} 36 bool VisitCastExpr(const CastExpr *CE); 37 }; 38 } 39 40 bool CastToStructVisitor::VisitCastExpr(const CastExpr *CE) { 41 const Expr *E = CE->getSubExpr(); 42 ASTContext &Ctx = AC->getASTContext(); 43 QualType OrigTy = Ctx.getCanonicalType(E->getType()); 44 QualType ToTy = Ctx.getCanonicalType(CE->getType()); 45 46 const PointerType *OrigPTy = dyn_cast<PointerType>(OrigTy.getTypePtr()); 47 const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr()); 48 49 if (!ToPTy || !OrigPTy) 50 return true; 51 52 QualType OrigPointeeTy = OrigPTy->getPointeeType(); 53 QualType ToPointeeTy = ToPTy->getPointeeType(); 54 55 if (!ToPointeeTy->isStructureOrClassType()) 56 return true; 57 58 // We allow cast from void*. 59 if (OrigPointeeTy->isVoidType()) 60 return true; 61 62 // Now the cast-to-type is struct pointer, the original type is not void*. 63 if (!OrigPointeeTy->isRecordType()) { 64 SourceRange Sr[1] = {CE->getSourceRange()}; 65 PathDiagnosticLocation Loc(CE, BR.getSourceManager(), AC); 66 BR.EmitBasicReport( 67 AC->getDecl(), Checker, "Cast from non-struct type to struct type", 68 categories::LogicError, "Casting a non-structure type to a structure " 69 "type and accessing a field can lead to memory " 70 "access errors or data corruption.", 71 Loc, Sr); 72 } else { 73 // Don't warn when size of data is unknown. 74 const auto *U = dyn_cast<UnaryOperator>(E); 75 if (!U || U->getOpcode() != UO_AddrOf) 76 return true; 77 78 // Don't warn for references 79 const ValueDecl *VD = nullptr; 80 if (const auto *SE = dyn_cast<DeclRefExpr>(U->getSubExpr())) 81 VD = dyn_cast<ValueDecl>(SE->getDecl()); 82 else if (const auto *SE = dyn_cast<MemberExpr>(U->getSubExpr())) 83 VD = SE->getMemberDecl(); 84 if (!VD || VD->getType()->isReferenceType()) 85 return true; 86 87 // Warn when there is widening cast. 88 unsigned ToWidth = Ctx.getTypeInfo(ToPointeeTy).Width; 89 unsigned OrigWidth = Ctx.getTypeInfo(OrigPointeeTy).Width; 90 if (ToWidth <= OrigWidth) 91 return true; 92 93 PathDiagnosticLocation Loc(CE, BR.getSourceManager(), AC); 94 BR.EmitBasicReport(AC->getDecl(), Checker, "Widening cast to struct type", 95 categories::LogicError, 96 "Casting data to a larger structure type and accessing " 97 "a field can lead to memory access errors or data " 98 "corruption.", 99 Loc, CE->getSourceRange()); 100 } 101 102 return true; 103 } 104 105 namespace { 106 class CastToStructChecker : public Checker<check::ASTCodeBody> { 107 public: 108 void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, 109 BugReporter &BR) const { 110 CastToStructVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D)); 111 Visitor.TraverseDecl(const_cast<Decl *>(D)); 112 } 113 }; 114 } // end anonymous namespace 115 116 void ento::registerCastToStructChecker(CheckerManager &mgr) { 117 mgr.registerChecker<CastToStructChecker>(); 118 } 119