1238730Sdelphij//=== CastToStructChecker.cpp ----------------------------------*- C++ -*--===// 2293190Sdelphij// 3238730Sdelphij// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4238730Sdelphij// See https://llvm.org/LICENSE.txt for license information. 5238730Sdelphij// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6238730Sdelphij// 7238730Sdelphij//===----------------------------------------------------------------------===// 8238730Sdelphij// 960786Sps// This files defines CastToStructChecker, a builtin checker that checks for 1060786Sps// cast from non-struct pointer to struct pointer and widening struct data cast. 1160786Sps// This check corresponds to CWE-588. 1260786Sps// 1360786Sps//===----------------------------------------------------------------------===// 1460786Sps 1560786Sps#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 1660786Sps#include "clang/AST/RecursiveASTVisitor.h" 1760786Sps#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 1860786Sps#include "clang/StaticAnalyzer/Core/Checker.h" 1960786Sps#include "clang/StaticAnalyzer/Core/CheckerManager.h" 2060786Sps#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 2160786Sps 2260786Spsusing namespace clang; 2360786Spsusing namespace ento; 2460786Sps 2560786Spsnamespace { 2660786Spsclass CastToStructVisitor : public RecursiveASTVisitor<CastToStructVisitor> { 2760786Sps BugReporter &BR; 2860786Sps const CheckerBase *Checker; 2960786Sps AnalysisDeclContext *AC; 3060786Sps 31170259Sdelphijpublic: 3260786Sps explicit CastToStructVisitor(BugReporter &B, const CheckerBase *Checker, 3360786Sps AnalysisDeclContext *A) 3460786Sps : BR(B), Checker(Checker), AC(A) {} 3560786Sps bool VisitCastExpr(const CastExpr *CE); 3660786Sps}; 3760786Sps} 3860786Sps 3960786Spsbool CastToStructVisitor::VisitCastExpr(const CastExpr *CE) { 4060786Sps const Expr *E = CE->getSubExpr(); 4160786Sps ASTContext &Ctx = AC->getASTContext(); 4260786Sps QualType OrigTy = Ctx.getCanonicalType(E->getType()); 4389022Sps QualType ToTy = Ctx.getCanonicalType(CE->getType()); 4460786Sps 4589022Sps const PointerType *OrigPTy = dyn_cast<PointerType>(OrigTy.getTypePtr()); 4660786Sps const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr()); 4789022Sps 4860786Sps if (!ToPTy || !OrigPTy) 4989022Sps return true; 5060786Sps 5160786Sps QualType OrigPointeeTy = OrigPTy->getPointeeType(); 5289022Sps QualType ToPointeeTy = ToPTy->getPointeeType(); 5389022Sps 54170259Sdelphij if (!ToPointeeTy->isStructureOrClassType()) 55170259Sdelphij return true; 5660786Sps 5760786Sps // We allow cast from void*. 5860786Sps if (OrigPointeeTy->isVoidType()) 5960786Sps return true; 6089022Sps 6160786Sps // Now the cast-to-type is struct pointer, the original type is not void*. 6260786Sps if (!OrigPointeeTy->isRecordType()) { 6360786Sps SourceRange Sr[1] = {CE->getSourceRange()}; 6460786Sps PathDiagnosticLocation Loc(CE, BR.getSourceManager(), AC); 6560786Sps BR.EmitBasicReport( 6660786Sps AC->getDecl(), Checker, "Cast from non-struct type to struct type", 6760786Sps categories::LogicError, "Casting a non-structure type to a structure " 6860786Sps "type and accessing a field can lead to memory " 6960786Sps "access errors or data corruption.", 7060786Sps Loc, Sr); 7160786Sps } else { 72170259Sdelphij // Don't warn when size of data is unknown. 7360786Sps const auto *U = dyn_cast<UnaryOperator>(E); 7460786Sps if (!U || U->getOpcode() != UO_AddrOf) 7560786Sps return true; 7689022Sps 7760786Sps // Don't warn for references 7860786Sps const ValueDecl *VD = nullptr; 7960786Sps if (const auto *SE = dyn_cast<DeclRefExpr>(U->getSubExpr())) 8060786Sps VD = SE->getDecl(); 8160786Sps else if (const auto *SE = dyn_cast<MemberExpr>(U->getSubExpr())) 8260786Sps VD = SE->getMemberDecl(); 8360786Sps if (!VD || VD->getType()->isReferenceType()) 8460786Sps return true; 8560786Sps 8660786Sps if (ToPointeeTy->isIncompleteType() || 8760786Sps OrigPointeeTy->isIncompleteType()) 88293190Sdelphij return true; 8960786Sps 90293190Sdelphij // Warn when there is widening cast. 9160786Sps unsigned ToWidth = Ctx.getTypeInfo(ToPointeeTy).Width; 9260786Sps unsigned OrigWidth = Ctx.getTypeInfo(OrigPointeeTy).Width; 9360786Sps if (ToWidth <= OrigWidth) 9460786Sps return true; 9560786Sps 9660786Sps PathDiagnosticLocation Loc(CE, BR.getSourceManager(), AC); 9760786Sps BR.EmitBasicReport(AC->getDecl(), Checker, "Widening cast to struct type", 9860786Sps categories::LogicError, 9960786Sps "Casting data to a larger structure type and accessing " 10060786Sps "a field can lead to memory access errors or data " 10160786Sps "corruption.", 10260786Sps Loc, CE->getSourceRange()); 10360786Sps } 10460786Sps 10560786Sps return true; 10660786Sps} 10760786Sps 10860786Spsnamespace { 10960786Spsclass CastToStructChecker : public Checker<check::ASTCodeBody> { 11060786Spspublic: 11160786Sps void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, 11260786Sps BugReporter &BR) const { 11360786Sps CastToStructVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D)); 11460786Sps Visitor.TraverseDecl(const_cast<Decl *>(D)); 11560786Sps } 11660786Sps}; 117128348Stjr} // end anonymous namespace 118128348Stjr 119128348Stjrvoid ento::registerCastToStructChecker(CheckerManager &mgr) { 120128348Stjr mgr.registerChecker<CastToStructChecker>(); 12160786Sps} 12260786Sps 12360786Spsbool ento::shouldRegisterCastToStructChecker(const CheckerManager &mgr) { 124128348Stjr return true; 125128348Stjr} 126128348Stjr