clang API Documentation

CastSizeChecker.cpp
Go to the documentation of this file.
00001 //=== CastSizeChecker.cpp ---------------------------------------*- C++ -*-===//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 // CastSizeChecker checks when casting a malloc'ed symbolic region to type T,
00011 // whether the size of the symbolic region is a multiple of the size of T.
00012 //
00013 //===----------------------------------------------------------------------===//
00014 #include "ClangSACheckers.h"
00015 #include "clang/AST/CharUnits.h"
00016 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
00017 #include "clang/StaticAnalyzer/Core/Checker.h"
00018 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
00019 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
00020 
00021 using namespace clang;
00022 using namespace ento;
00023 
00024 namespace {
00025 class CastSizeChecker : public Checker< check::PreStmt<CastExpr> > {
00026   mutable std::unique_ptr<BuiltinBug> BT;
00027 
00028 public:
00029   void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
00030 };
00031 }
00032 
00033 /// Check if we are casting to a struct with a flexible array at the end.
00034 /// \code
00035 /// struct foo {
00036 ///   size_t len;
00037 ///   struct bar data[];
00038 /// };
00039 /// \endcode
00040 /// or
00041 /// \code
00042 /// struct foo {
00043 ///   size_t len;
00044 ///   struct bar data[0];
00045 /// }
00046 /// \endcode
00047 /// In these cases it is also valid to allocate size of struct foo + a multiple
00048 /// of struct bar.
00049 static bool evenFlexibleArraySize(ASTContext &Ctx, CharUnits RegionSize,
00050                                   CharUnits TypeSize, QualType ToPointeeTy) {
00051   const RecordType *RT = ToPointeeTy->getAs<RecordType>();
00052   if (!RT)
00053     return false;
00054 
00055   const RecordDecl *RD = RT->getDecl();
00056   RecordDecl::field_iterator Iter(RD->field_begin());
00057   RecordDecl::field_iterator End(RD->field_end());
00058   const FieldDecl *Last = nullptr;
00059   for (; Iter != End; ++Iter)
00060     Last = *Iter;
00061   assert(Last && "empty structs should already be handled");
00062 
00063   const Type *ElemType = Last->getType()->getArrayElementTypeNoTypeQual();
00064   CharUnits FlexSize;
00065   if (const ConstantArrayType *ArrayTy =
00066         Ctx.getAsConstantArrayType(Last->getType())) {
00067     FlexSize = Ctx.getTypeSizeInChars(ElemType);
00068     if (ArrayTy->getSize() == 1 && TypeSize > FlexSize)
00069       TypeSize -= FlexSize;
00070     else if (ArrayTy->getSize() != 0)
00071       return false;
00072   } else if (RD->hasFlexibleArrayMember()) {
00073     FlexSize = Ctx.getTypeSizeInChars(ElemType);
00074   } else {
00075     return false;
00076   }
00077 
00078   if (FlexSize.isZero())
00079     return false;
00080 
00081   CharUnits Left = RegionSize - TypeSize;
00082   if (Left.isNegative())
00083     return false;
00084 
00085   if (Left % FlexSize == 0)
00086     return true;
00087 
00088   return false;
00089 }
00090 
00091 void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
00092   const Expr *E = CE->getSubExpr();
00093   ASTContext &Ctx = C.getASTContext();
00094   QualType ToTy = Ctx.getCanonicalType(CE->getType());
00095   const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr());
00096 
00097   if (!ToPTy)
00098     return;
00099 
00100   QualType ToPointeeTy = ToPTy->getPointeeType();
00101 
00102   // Only perform the check if 'ToPointeeTy' is a complete type.
00103   if (ToPointeeTy->isIncompleteType())
00104     return;
00105 
00106   ProgramStateRef state = C.getState();
00107   const MemRegion *R = state->getSVal(E, C.getLocationContext()).getAsRegion();
00108   if (!R)
00109     return;
00110 
00111   const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R);
00112   if (!SR)
00113     return;
00114 
00115   SValBuilder &svalBuilder = C.getSValBuilder();
00116   SVal extent = SR->getExtent(svalBuilder);
00117   const llvm::APSInt *extentInt = svalBuilder.getKnownValue(state, extent);
00118   if (!extentInt)
00119     return;
00120 
00121   CharUnits regionSize = CharUnits::fromQuantity(extentInt->getSExtValue());
00122   CharUnits typeSize = C.getASTContext().getTypeSizeInChars(ToPointeeTy);
00123 
00124   // Ignore void, and a few other un-sizeable types.
00125   if (typeSize.isZero())
00126     return;
00127 
00128   if (regionSize % typeSize == 0)
00129     return;
00130 
00131   if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy))
00132     return;
00133 
00134   if (ExplodedNode *errorNode = C.generateSink()) {
00135     if (!BT)
00136       BT.reset(new BuiltinBug(this, "Cast region with wrong size.",
00137                                     "Cast a region whose size is not a multiple"
00138                                     " of the destination type size."));
00139     BugReport *R = new BugReport(*BT, BT->getDescription(), errorNode);
00140     R->addRange(CE->getSourceRange());
00141     C.emitReport(R);
00142   }
00143 }
00144 
00145 void ento::registerCastSizeChecker(CheckerManager &mgr) {
00146   mgr.registerChecker<CastSizeChecker>();
00147 }