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