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