xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //=== CastSizeChecker.cpp ---------------------------------------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // CastSizeChecker checks when casting a malloc'ed symbolic region to type T,
100b57cec5SDimitry Andric // whether the size of the symbolic region is a multiple of the size of T.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
135ffd83dbSDimitry Andric 
140b57cec5SDimitry Andric #include "clang/AST/CharUnits.h"
155ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
160b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20fe6060f1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
210b57cec5SDimitry Andric 
220b57cec5SDimitry Andric using namespace clang;
230b57cec5SDimitry Andric using namespace ento;
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric namespace {
260b57cec5SDimitry Andric class CastSizeChecker : public Checker< check::PreStmt<CastExpr> > {
27647cbc5dSDimitry Andric   const BugType BT{this, "Cast region with wrong size."};
280b57cec5SDimitry Andric 
290b57cec5SDimitry Andric public:
300b57cec5SDimitry Andric   void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
310b57cec5SDimitry Andric };
320b57cec5SDimitry Andric }
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric /// Check if we are casting to a struct with a flexible array at the end.
350b57cec5SDimitry Andric /// \code
360b57cec5SDimitry Andric /// struct foo {
370b57cec5SDimitry Andric ///   size_t len;
380b57cec5SDimitry Andric ///   struct bar data[];
390b57cec5SDimitry Andric /// };
400b57cec5SDimitry Andric /// \endcode
410b57cec5SDimitry Andric /// or
420b57cec5SDimitry Andric /// \code
430b57cec5SDimitry Andric /// struct foo {
440b57cec5SDimitry Andric ///   size_t len;
450b57cec5SDimitry Andric ///   struct bar data[0];
460b57cec5SDimitry Andric /// }
470b57cec5SDimitry Andric /// \endcode
480b57cec5SDimitry Andric /// In these cases it is also valid to allocate size of struct foo + a multiple
490b57cec5SDimitry Andric /// of struct bar.
500b57cec5SDimitry Andric static bool evenFlexibleArraySize(ASTContext &Ctx, CharUnits RegionSize,
510b57cec5SDimitry Andric                                   CharUnits TypeSize, QualType ToPointeeTy) {
520b57cec5SDimitry Andric   const RecordType *RT = ToPointeeTy->getAs<RecordType>();
530b57cec5SDimitry Andric   if (!RT)
540b57cec5SDimitry Andric     return false;
550b57cec5SDimitry Andric 
560b57cec5SDimitry Andric   const RecordDecl *RD = RT->getDecl();
570b57cec5SDimitry Andric   RecordDecl::field_iterator Iter(RD->field_begin());
580b57cec5SDimitry Andric   RecordDecl::field_iterator End(RD->field_end());
590b57cec5SDimitry Andric   const FieldDecl *Last = nullptr;
600b57cec5SDimitry Andric   for (; Iter != End; ++Iter)
610b57cec5SDimitry Andric     Last = *Iter;
620b57cec5SDimitry Andric   assert(Last && "empty structs should already be handled");
630b57cec5SDimitry Andric 
640b57cec5SDimitry Andric   const Type *ElemType = Last->getType()->getArrayElementTypeNoTypeQual();
650b57cec5SDimitry Andric   CharUnits FlexSize;
660b57cec5SDimitry Andric   if (const ConstantArrayType *ArrayTy =
670b57cec5SDimitry Andric         Ctx.getAsConstantArrayType(Last->getType())) {
680b57cec5SDimitry Andric     FlexSize = Ctx.getTypeSizeInChars(ElemType);
690b57cec5SDimitry Andric     if (ArrayTy->getSize() == 1 && TypeSize > FlexSize)
700b57cec5SDimitry Andric       TypeSize -= FlexSize;
71*0fca6ea1SDimitry Andric     else if (!ArrayTy->isZeroSize())
720b57cec5SDimitry Andric       return false;
730b57cec5SDimitry Andric   } else if (RD->hasFlexibleArrayMember()) {
740b57cec5SDimitry Andric     FlexSize = Ctx.getTypeSizeInChars(ElemType);
750b57cec5SDimitry Andric   } else {
760b57cec5SDimitry Andric     return false;
770b57cec5SDimitry Andric   }
780b57cec5SDimitry Andric 
790b57cec5SDimitry Andric   if (FlexSize.isZero())
800b57cec5SDimitry Andric     return false;
810b57cec5SDimitry Andric 
820b57cec5SDimitry Andric   CharUnits Left = RegionSize - TypeSize;
830b57cec5SDimitry Andric   if (Left.isNegative())
840b57cec5SDimitry Andric     return false;
850b57cec5SDimitry Andric 
860b57cec5SDimitry Andric   return Left % FlexSize == 0;
870b57cec5SDimitry Andric }
880b57cec5SDimitry Andric 
890b57cec5SDimitry Andric void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
900b57cec5SDimitry Andric   const Expr *E = CE->getSubExpr();
910b57cec5SDimitry Andric   ASTContext &Ctx = C.getASTContext();
920b57cec5SDimitry Andric   QualType ToTy = Ctx.getCanonicalType(CE->getType());
930b57cec5SDimitry Andric   const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr());
940b57cec5SDimitry Andric 
950b57cec5SDimitry Andric   if (!ToPTy)
960b57cec5SDimitry Andric     return;
970b57cec5SDimitry Andric 
980b57cec5SDimitry Andric   QualType ToPointeeTy = ToPTy->getPointeeType();
990b57cec5SDimitry Andric 
1000b57cec5SDimitry Andric   // Only perform the check if 'ToPointeeTy' is a complete type.
1010b57cec5SDimitry Andric   if (ToPointeeTy->isIncompleteType())
1020b57cec5SDimitry Andric     return;
1030b57cec5SDimitry Andric 
1040b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
1050b57cec5SDimitry Andric   const MemRegion *R = C.getSVal(E).getAsRegion();
1060b57cec5SDimitry Andric   if (!R)
1070b57cec5SDimitry Andric     return;
1080b57cec5SDimitry Andric 
1090b57cec5SDimitry Andric   const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R);
1100b57cec5SDimitry Andric   if (!SR)
1110b57cec5SDimitry Andric     return;
1120b57cec5SDimitry Andric 
1130b57cec5SDimitry Andric   SValBuilder &svalBuilder = C.getSValBuilder();
1145ffd83dbSDimitry Andric 
115fe6060f1SDimitry Andric   DefinedOrUnknownSVal Size = getDynamicExtent(state, SR, svalBuilder);
1165ffd83dbSDimitry Andric   const llvm::APSInt *SizeInt = svalBuilder.getKnownValue(state, Size);
1175ffd83dbSDimitry Andric   if (!SizeInt)
1180b57cec5SDimitry Andric     return;
1190b57cec5SDimitry Andric 
1205ffd83dbSDimitry Andric   CharUnits regionSize = CharUnits::fromQuantity(SizeInt->getZExtValue());
1210b57cec5SDimitry Andric   CharUnits typeSize = C.getASTContext().getTypeSizeInChars(ToPointeeTy);
1220b57cec5SDimitry Andric 
1230b57cec5SDimitry Andric   // Ignore void, and a few other un-sizeable types.
1240b57cec5SDimitry Andric   if (typeSize.isZero())
1250b57cec5SDimitry Andric     return;
1260b57cec5SDimitry Andric 
1270b57cec5SDimitry Andric   if (regionSize % typeSize == 0)
1280b57cec5SDimitry Andric     return;
1290b57cec5SDimitry Andric 
1300b57cec5SDimitry Andric   if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy))
1310b57cec5SDimitry Andric     return;
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric   if (ExplodedNode *errorNode = C.generateErrorNode()) {
1345f757f3fSDimitry Andric     constexpr llvm::StringLiteral Msg =
1355f757f3fSDimitry Andric         "Cast a region whose size is not a multiple of the destination type "
1365f757f3fSDimitry Andric         "size.";
137647cbc5dSDimitry Andric     auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, errorNode);
1380b57cec5SDimitry Andric     R->addRange(CE->getSourceRange());
1390b57cec5SDimitry Andric     C.emitReport(std::move(R));
1400b57cec5SDimitry Andric   }
1410b57cec5SDimitry Andric }
1420b57cec5SDimitry Andric 
1430b57cec5SDimitry Andric void ento::registerCastSizeChecker(CheckerManager &mgr) {
1440b57cec5SDimitry Andric   mgr.registerChecker<CastSizeChecker>();
1450b57cec5SDimitry Andric }
1460b57cec5SDimitry Andric 
1475ffd83dbSDimitry Andric bool ento::shouldRegisterCastSizeChecker(const CheckerManager &mgr) {
1480b57cec5SDimitry Andric   // PR31226: C++ is more complicated than what this checker currently supports.
1490b57cec5SDimitry Andric   // There are derived-to-base casts, there are different rules for 0-size
1500b57cec5SDimitry Andric   // structures, no flexible arrays, etc.
1510b57cec5SDimitry Andric   // FIXME: Disabled on C++ for now.
1525ffd83dbSDimitry Andric   const LangOptions &LO = mgr.getLangOpts();
1530b57cec5SDimitry Andric   return !LO.CPlusPlus;
1540b57cec5SDimitry Andric }
155