diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 6a73754b73a00..9a05eea9de8ac 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -239,6 +239,10 @@ Improvements to Clang's diagnostics specializations in th same way as it already did for other declarators. (#GH147333) +- A new warning ``-Walloc-size`` has been added to detect calls to functions + decorated with the ``alloc_size`` attribute don't allocate enough space for + the target pointer type. + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 77d0912d1f41b..d2ffe9c2e7dd3 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -40,6 +40,7 @@ #include namespace clang { + class AllocSizeAttr; class APValue; class ASTContext; class BlockDecl; @@ -3261,6 +3262,14 @@ class CallExpr : public Expr { setDependence(getDependence() | ExprDependence::TypeValueInstantiation); } + /// Try to get the alloc_size attribute of the callee. May return null. + const AllocSizeAttr *getCalleeAllocSizeAttr() const; + + /// Get the total size in bytes allocated by calling a function decorated with + /// alloc_size. Returns std::nullopt if the the result cannot be evaluated. + std::optional + getBytesReturnedByAllocSizeCall(const ASTContext &Ctx) const; + bool isCallToStdMove() const; static bool classof(const Stmt *T) { diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index b9405c5fc86c1..2504841f6df33 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -973,6 +973,21 @@ An example of how to use ``alloc_size`` assert(__builtin_object_size(a, 0) == 100); } +When ``-Walloc-size`` is enabled, this attribute allows the compiler to +diagnose cases when the allocated memory is insufficient for the size of the +type the returned pointer is cast to. + +.. code-block:: c + + void *my_malloc(int a) __attribute__((alloc_size(1))); + void consumer_func(int *); + + int main() { + int *ptr = my_malloc(sizeof(int)); // no warning + int *w = my_malloc(1); // warning: allocation of insufficient size '1' for type 'int' with size '4' + consumer_func(my_malloc(1)); // warning: allocation of insufficient size '1' for type 'int' with size '4' + } + .. Note:: This attribute works differently in clang than it does in GCC. Specifically, clang will only trace ``const`` pointers (as above); we give up on pointers that are not marked as ``const``. In the vast majority of cases, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4c98e0f8b04ed..4bee813edf645 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3687,6 +3687,10 @@ def warn_alloca_align_alignof : Warning< "second argument to __builtin_alloca_with_align is supposed to be in bits">, InGroup>; +def warn_alloc_size : Warning< + "allocation of insufficient size '%0' for type %1 with size '%2'">, + InGroup>; + def err_alignment_too_small : Error< "requested alignment must be %0 or greater">; def err_alignment_too_big : Error< diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 340de6d4be934..9d1490c2ef834 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3533,6 +3533,56 @@ bool CallExpr::isBuiltinAssumeFalse(const ASTContext &Ctx) const { Arg->EvaluateAsBooleanCondition(ArgVal, Ctx) && !ArgVal; } +const AllocSizeAttr *CallExpr::getCalleeAllocSizeAttr() const { + if (const FunctionDecl *DirectCallee = getDirectCallee()) + return DirectCallee->getAttr(); + if (const Decl *IndirectCallee = getCalleeDecl()) + return IndirectCallee->getAttr(); + return nullptr; +} + +std::optional +CallExpr::getBytesReturnedByAllocSizeCall(const ASTContext &Ctx) const { + const AllocSizeAttr *AllocSize = getCalleeAllocSizeAttr(); + + assert(AllocSize && AllocSize->getElemSizeParam().isValid()); + unsigned SizeArgNo = AllocSize->getElemSizeParam().getASTIndex(); + unsigned BitsInSizeT = Ctx.getTypeSize(Ctx.getSizeType()); + if (getNumArgs() <= SizeArgNo) + return {}; + + auto EvaluateAsSizeT = [&](const Expr *E, llvm::APSInt &Into) { + Expr::EvalResult ExprResult; + if (E->isValueDependent() || + !E->EvaluateAsInt(ExprResult, Ctx, Expr::SE_AllowSideEffects)) + return false; + Into = ExprResult.Val.getInt(); + if (Into.isNegative() || !Into.isIntN(BitsInSizeT)) + return false; + Into = Into.zext(BitsInSizeT); + return true; + }; + + llvm::APSInt SizeOfElem; + if (!EvaluateAsSizeT(getArg(SizeArgNo), SizeOfElem)) + return {}; + + if (!AllocSize->getNumElemsParam().isValid()) + return SizeOfElem; + + llvm::APSInt NumberOfElems; + unsigned NumArgNo = AllocSize->getNumElemsParam().getASTIndex(); + if (!EvaluateAsSizeT(getArg(NumArgNo), NumberOfElems)) + return {}; + + bool Overflow; + llvm::APInt BytesAvailable = SizeOfElem.umul_ov(NumberOfElems, Overflow); + if (Overflow) + return {}; + + return BytesAvailable; +} + bool CallExpr::isCallToStdMove() const { return getBuiltinCallee() == Builtin::BImove; } diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index a71cb8b0143be..2c8e9e59472e1 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -114,15 +114,6 @@ namespace { return Ctx.getLValueReferenceType(E->getType()); } - /// Given a CallExpr, try to get the alloc_size attribute. May return null. - static const AllocSizeAttr *getAllocSizeAttr(const CallExpr *CE) { - if (const FunctionDecl *DirectCallee = CE->getDirectCallee()) - return DirectCallee->getAttr(); - if (const Decl *IndirectCallee = CE->getCalleeDecl()) - return IndirectCallee->getAttr(); - return nullptr; - } - /// Attempts to unwrap a CallExpr (with an alloc_size attribute) from an Expr. /// This will look through a single cast. /// @@ -142,7 +133,7 @@ namespace { E = Cast->getSubExpr()->IgnoreParens(); if (const auto *CE = dyn_cast(E)) - return getAllocSizeAttr(CE) ? CE : nullptr; + return CE->getCalleeAllocSizeAttr() ? CE : nullptr; return nullptr; } @@ -9466,57 +9457,6 @@ bool LValueExprEvaluator::VisitBinAssign(const BinaryOperator *E) { // Pointer Evaluation //===----------------------------------------------------------------------===// -/// Attempts to compute the number of bytes available at the pointer -/// returned by a function with the alloc_size attribute. Returns true if we -/// were successful. Places an unsigned number into `Result`. -/// -/// This expects the given CallExpr to be a call to a function with an -/// alloc_size attribute. -static bool getBytesReturnedByAllocSizeCall(const ASTContext &Ctx, - const CallExpr *Call, - llvm::APInt &Result) { - const AllocSizeAttr *AllocSize = getAllocSizeAttr(Call); - - assert(AllocSize && AllocSize->getElemSizeParam().isValid()); - unsigned SizeArgNo = AllocSize->getElemSizeParam().getASTIndex(); - unsigned BitsInSizeT = Ctx.getTypeSize(Ctx.getSizeType()); - if (Call->getNumArgs() <= SizeArgNo) - return false; - - auto EvaluateAsSizeT = [&](const Expr *E, APSInt &Into) { - Expr::EvalResult ExprResult; - if (!E->EvaluateAsInt(ExprResult, Ctx, Expr::SE_AllowSideEffects)) - return false; - Into = ExprResult.Val.getInt(); - if (Into.isNegative() || !Into.isIntN(BitsInSizeT)) - return false; - Into = Into.zext(BitsInSizeT); - return true; - }; - - APSInt SizeOfElem; - if (!EvaluateAsSizeT(Call->getArg(SizeArgNo), SizeOfElem)) - return false; - - if (!AllocSize->getNumElemsParam().isValid()) { - Result = std::move(SizeOfElem); - return true; - } - - APSInt NumberOfElems; - unsigned NumArgNo = AllocSize->getNumElemsParam().getASTIndex(); - if (!EvaluateAsSizeT(Call->getArg(NumArgNo), NumberOfElems)) - return false; - - bool Overflow; - llvm::APInt BytesAvailable = SizeOfElem.umul_ov(NumberOfElems, Overflow); - if (Overflow) - return false; - - Result = std::move(BytesAvailable); - return true; -} - /// Convenience function. LVal's base must be a call to an alloc_size /// function. static bool getBytesReturnedByAllocSizeCall(const ASTContext &Ctx, @@ -9526,7 +9466,12 @@ static bool getBytesReturnedByAllocSizeCall(const ASTContext &Ctx, "Can't get the size of a non alloc_size function"); const auto *Base = LVal.getLValueBase().get(); const CallExpr *CE = tryUnwrapAllocSizeCall(Base); - return getBytesReturnedByAllocSizeCall(Ctx, CE, Result); + std::optional Size = CE->getBytesReturnedByAllocSizeCall(Ctx); + if (!Size) + return false; + + Result = std::move(*Size); + return true; } /// Attempts to evaluate the given LValueBase as the result of a call to @@ -10017,7 +9962,7 @@ bool PointerExprEvaluator::visitNonBuiltinCallExpr(const CallExpr *E) { if (ExprEvaluatorBaseTy::VisitCallExpr(E)) return true; - if (!(InvalidBaseOK && getAllocSizeAttr(E))) + if (!(InvalidBaseOK && E->getCalleeAllocSizeAttr())) return false; Result.setInvalid(E); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index a73093545aa7f..5a6b6ea9588b7 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -18,6 +18,7 @@ #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/ASTLambda.h" #include "clang/AST/ASTMutationListener.h" +#include "clang/AST/Attrs.inc" #include "clang/AST/CXXInheritance.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" @@ -7818,6 +7819,39 @@ ExprResult Sema::CheckExtVectorCast(SourceRange R, QualType DestTy, return prepareVectorSplat(DestTy, CastExpr); } +/// Check that a call to alloc_size function specifies sufficient space for the +/// destination type. +static void CheckSufficientAllocSize(Sema &S, QualType DestType, + const Expr *E) { + QualType SourceType = E->getType(); + if (!DestType->isPointerType() || !SourceType->isPointerType() || + DestType == SourceType) + return; + + const auto *CE = dyn_cast(E->IgnoreParenCasts()); + if (!CE) + return; + + // Find the total size allocated by the function call. + if (!CE->getCalleeAllocSizeAttr()) + return; + std::optional AllocSize = + CE->getBytesReturnedByAllocSizeCall(S.Context); + if (!AllocSize) + return; + auto Size = CharUnits::fromQuantity(AllocSize->getZExtValue()); + + QualType TargetType = DestType->getPointeeType(); + // Find the destination size. As a special case function types have size of + // one byte to match the sizeof operator behavior. + auto LhsSize = TargetType->isFunctionType() + ? CharUnits::One() + : S.Context.getTypeSizeInCharsIfKnown(TargetType); + if (LhsSize && Size < LhsSize) + S.Diag(E->getExprLoc(), diag::warn_alloc_size) + << Size.getQuantity() << TargetType << LhsSize->getQuantity(); +} + ExprResult Sema::ActOnCastExpr(Scope *S, SourceLocation LParenLoc, Declarator &D, ParsedType &Ty, @@ -7883,6 +7917,8 @@ Sema::ActOnCastExpr(Scope *S, SourceLocation LParenLoc, DiscardMisalignedMemberAddress(castType.getTypePtr(), CastExpr); + CheckSufficientAllocSize(*this, castType, CastExpr); + return BuildCStyleCastExpr(LParenLoc, castTInfo, RParenLoc, CastExpr); } @@ -9893,6 +9929,12 @@ AssignConvertType Sema::CheckSingleAssignmentConstraints(QualType LHSType, AssignConvertType result = CheckAssignmentConstraints(LHSType, RHS, Kind, ConvertRHS); + // If assigning a void * created by an allocation function call to some other + // type, check that the allocated size is sufficient for that type. + if (result != AssignConvertType::Incompatible && + RHS.get()->getType()->isVoidPointerType()) + CheckSufficientAllocSize(*this, LHSType, RHS.get()); + // C99 6.5.16.1p2: The value of the right operand is converted to the // type of the assignment expression. // CheckAssignmentConstraints allows the left-hand side to be a reference, diff --git a/clang/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp b/clang/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp index b9eb85d6b69fa..fc324c311f82d 100644 --- a/clang/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp +++ b/clang/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator,cplusplus.NewDelete -std=c++11 -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator,cplusplus.NewDelete,cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -verify %s +// RUN: %clang_analyze_cc1 -Wno-alloc-size -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator,cplusplus.NewDelete -std=c++11 -verify %s +// RUN: %clang_analyze_cc1 -Wno-alloc-size -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator,cplusplus.NewDelete,cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -verify %s #include "Inputs/system-header-simulator-for-malloc.h" diff --git a/clang/test/Analysis/Malloc+MismatchedDeallocator_intersections.cpp b/clang/test/Analysis/Malloc+MismatchedDeallocator_intersections.cpp index b0cef2591486d..98873409a4fa2 100644 --- a/clang/test/Analysis/Malloc+MismatchedDeallocator_intersections.cpp +++ b/clang/test/Analysis/Malloc+MismatchedDeallocator_intersections.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator -std=c++11 -verify %s +// RUN: %clang_analyze_cc1 -Wno-alloc-size -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator -std=c++11 -verify %s // expected-no-diagnostics typedef __typeof(sizeof(int)) size_t; diff --git a/clang/test/Analysis/MismatchedDeallocator-checker-test.mm b/clang/test/Analysis/MismatchedDeallocator-checker-test.mm index ef8b24ba8de32..21cbe86c9726f 100644 --- a/clang/test/Analysis/MismatchedDeallocator-checker-test.mm +++ b/clang/test/Analysis/MismatchedDeallocator-checker-test.mm @@ -1,5 +1,5 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.MismatchedDeallocator -fblocks -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.MismatchedDeallocator -fblocks -DTEST_INLINABLE_ALLOCATORS -verify %s +// RUN: %clang_analyze_cc1 -Wno-alloc-size -analyzer-checker=core,unix.MismatchedDeallocator -fblocks -verify %s +// RUN: %clang_analyze_cc1 -Wno-alloc-size -analyzer-checker=core,unix.MismatchedDeallocator -fblocks -DTEST_INLINABLE_ALLOCATORS -verify %s #include "Inputs/system-header-simulator-objc.h" #include "Inputs/system-header-simulator-cxx.h" diff --git a/clang/test/Analysis/NewDelete-checker-test.cpp b/clang/test/Analysis/NewDelete-checker-test.cpp index c417b9c2ac97e..5ab6e16edf0df 100644 --- a/clang/test/Analysis/NewDelete-checker-test.cpp +++ b/clang/test/Analysis/NewDelete-checker-test.cpp @@ -1,31 +1,37 @@ // RUN: %clang_analyze_cc1 -std=c++11 -fblocks %s \ +// RUN: -Wno-alloc-size \ // RUN: -verify=expected,newdelete \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=cplusplus.NewDelete // // RUN: %clang_analyze_cc1 -DLEAKS -std=c++11 -fblocks %s \ +// RUN: -Wno-alloc-size \ // RUN: -verify=expected,newdelete,leak \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=cplusplus.NewDelete \ // RUN: -analyzer-checker=cplusplus.NewDeleteLeaks // // RUN: %clang_analyze_cc1 -std=c++11 -fblocks -verify %s \ +// RUN: -Wno-alloc-size \ // RUN: -verify=expected,leak \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=cplusplus.NewDeleteLeaks // // RUN: %clang_analyze_cc1 -std=c++17 -fblocks %s \ +// RUN: -Wno-alloc-size \ // RUN: -verify=expected,newdelete \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=cplusplus.NewDelete // // RUN: %clang_analyze_cc1 -DLEAKS -std=c++17 -fblocks %s \ +// RUN: -Wno-alloc-size \ // RUN: -verify=expected,newdelete,leak \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=cplusplus.NewDelete \ // RUN: -analyzer-checker=cplusplus.NewDeleteLeaks // // RUN: %clang_analyze_cc1 -std=c++17 -fblocks -verify %s \ +// RUN: -Wno-alloc-size \ // RUN: -verify=expected,leak,inspection \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=cplusplus.NewDeleteLeaks \ diff --git a/clang/test/Analysis/NewDelete-intersections.mm b/clang/test/Analysis/NewDelete-intersections.mm index eddfb3294073e..dec9c2922cfea 100644 --- a/clang/test/Analysis/NewDelete-intersections.mm +++ b/clang/test/Analysis/NewDelete-intersections.mm @@ -1,4 +1,5 @@ // RUN: %clang_analyze_cc1 -std=c++11 -fblocks %s \ +// RUN: -Wno-alloc-size \ // RUN: -verify=newdelete \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=cplusplus.NewDelete @@ -6,11 +7,13 @@ // leak-no-diagnostics // RUN: %clang_analyze_cc1 -std=c++11 -DLEAKS -fblocks %s \ +// RUN: -Wno-alloc-size \ // RUN: -verify=leak \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=cplusplus.NewDeleteLeaks // RUN: %clang_analyze_cc1 -std=c++11 -DLEAKS -fblocks %s \ +// RUN: -Wno-alloc-size \ // RUN: -verify=mismatch \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=unix.MismatchedDeallocator diff --git a/clang/test/Analysis/castsize.c b/clang/test/Analysis/castsize.c index 81aa60c0414cd..b08203bdc2254 100644 --- a/clang/test/Analysis/castsize.c +++ b/clang/test/Analysis/castsize.c @@ -1,5 +1,5 @@ // RUN: %clang_analyze_cc1 -verify %s \ -// RUN: -analyzer-checker=core,unix.Malloc,alpha.core.CastSize +// RUN: -Wno-alloc-size -analyzer-checker=core,unix.Malloc,alpha.core.CastSize typedef typeof(sizeof(int)) size_t; void *malloc(size_t); diff --git a/clang/test/Analysis/malloc-annotations.c b/clang/test/Analysis/malloc-annotations.c index 68ac71d51e45c..969c84d249b5d 100644 --- a/clang/test/Analysis/malloc-annotations.c +++ b/clang/test/Analysis/malloc-annotations.c @@ -1,4 +1,5 @@ // RUN: %clang_analyze_cc1 -verify \ +// RUN: -Wno-alloc-size \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=alpha.deadcode.UnreachableCode \ // RUN: -analyzer-checker=alpha.core.CastSize \ diff --git a/clang/test/Analysis/malloc-sizeof.c b/clang/test/Analysis/malloc-sizeof.c index 4573c193881e3..6202795bc1005 100644 --- a/clang/test/Analysis/malloc-sizeof.c +++ b/clang/test/Analysis/malloc-sizeof.c @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=unix.MallocSizeof -verify %s +// RUN: %clang_analyze_cc1 -Wno-alloc-size -analyzer-checker=unix.MallocSizeof -verify %s #include diff --git a/clang/test/Analysis/malloc.c b/clang/test/Analysis/malloc.c index 82eb364a78f96..84593ad6837da 100644 --- a/clang/test/Analysis/malloc.c +++ b/clang/test/Analysis/malloc.c @@ -1,4 +1,5 @@ // RUN: %clang_analyze_cc1 -Wno-strict-prototypes -Wno-error=implicit-int -verify %s \ +// RUN: -Wno-alloc-size \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=alpha.deadcode.UnreachableCode \ // RUN: -analyzer-checker=alpha.core.CastSize \ diff --git a/clang/test/Analysis/unix-fns.c b/clang/test/Analysis/unix-fns.c index 77894285bcb69..2a971be2e6023 100644 --- a/clang/test/Analysis/unix-fns.c +++ b/clang/test/Analysis/unix-fns.c @@ -1,6 +1,6 @@ -// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core,unix.API,osx.API,optin.portability %s -analyzer-output=plist -analyzer-config faux-bodies=true -fblocks -verify -o %t.plist +// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -Wno-alloc-size -analyzer-checker=core,unix.API,osx.API,optin.portability %s -analyzer-output=plist -analyzer-config faux-bodies=true -fblocks -verify -o %t.plist // RUN: %normalize_plist <%t.plist | diff -ub %S/Inputs/expected-plists/unix-fns.c.plist - -// RUN: %clang_analyze_cc1 -triple x86_64-unknown-linux -analyzer-checker=core,unix.API,osx.API,optin.portability %s -analyzer-output=plist -analyzer-config faux-bodies=true -fblocks -verify -o %t.plist +// RUN: %clang_analyze_cc1 -triple x86_64-unknown-linux -Wno-alloc-size -analyzer-checker=core,unix.API,osx.API,optin.portability %s -analyzer-output=plist -analyzer-config faux-bodies=true -fblocks -verify -o %t.plist // RUN: %normalize_plist <%t.plist | diff -ub %S/Inputs/expected-plists/unix-fns.c.plist - // RUN: mkdir -p %t.dir // RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.API,osx.API,optin.portability -analyzer-output=html -analyzer-config faux-bodies=true -fblocks -o %t.dir %s diff --git a/clang/test/Sema/implicit-void-ptr-cast.c b/clang/test/Sema/implicit-void-ptr-cast.c index f083cfb77329f..037feaa137289 100644 --- a/clang/test/Sema/implicit-void-ptr-cast.c +++ b/clang/test/Sema/implicit-void-ptr-cast.c @@ -1,8 +1,8 @@ -// RUN: %clang_cc1 -fsyntax-only -std=c23 -verify=c -Wimplicit-void-ptr-cast %s -// RUN: %clang_cc1 -fsyntax-only -std=c23 -verify=c -Wc++-compat %s -// RUN: %clang_cc1 -fsyntax-only -verify=cxx -x c++ %s -// RUN: %clang_cc1 -fsyntax-only -std=c23 -verify=good %s -// RUN: %clang_cc1 -fsyntax-only -std=c23 -verify=good -Wc++-compat -Wno-implicit-void-ptr-cast %s +// RUN: %clang_cc1 -fsyntax-only -Wno-alloc-size -std=c23 -verify=c -Wimplicit-void-ptr-cast %s +// RUN: %clang_cc1 -fsyntax-only -Wno-alloc-size -std=c23 -verify=c -Wc++-compat %s +// RUN: %clang_cc1 -fsyntax-only -Wno-alloc-size -verify=cxx -x c++ %s +// RUN: %clang_cc1 -fsyntax-only -Wno-alloc-size -std=c23 -verify=good %s +// RUN: %clang_cc1 -fsyntax-only -Wno-alloc-size -std=c23 -verify=good -Wc++-compat -Wno-implicit-void-ptr-cast %s // good-no-diagnostics typedef __typeof__(sizeof(int)) size_t; diff --git a/clang/test/Sema/warn-alloc-size.c b/clang/test/Sema/warn-alloc-size.c new file mode 100644 index 0000000000000..445b0ba677337 --- /dev/null +++ b/clang/test/Sema/warn-alloc-size.c @@ -0,0 +1,49 @@ +// RUN: %clang_cc1 -triple x86_64-linux -fsyntax-only -verify -Walloc-size %s +struct Foo { int x[10]; }; + +struct ZeroSize { + int flexible_array[]; +}; + +typedef __typeof__(sizeof(int)) size_t; +void *my_malloc(size_t) __attribute__((alloc_size(1))); +void *my_calloc(size_t, size_t) __attribute__((alloc_size(2, 1))); + +void foo_consumer(struct Foo* p); + +void alloc_foo(void) { + struct Foo *ptr1 = my_malloc(sizeof(struct Foo)); + struct Foo *ptr2 = my_malloc(sizeof(*ptr2)); + struct Foo *ptr3 = my_calloc(1, sizeof(*ptr3)); + struct Foo *ptr4 = my_calloc(sizeof(*ptr4), 1); + struct Foo (*ptr5)[5] = my_malloc(sizeof(*ptr5)); + void *ptr6 = my_malloc(4); + + // Test insufficient size with different allocation functions. + struct Foo *ptr7 = my_malloc(sizeof(ptr7)); // expected-warning {{allocation of insufficient size '8' for type 'struct Foo' with size '40'}} + struct Foo *ptr8 = my_calloc(1, sizeof(ptr8)); // expected-warning {{allocation of insufficient size '8' for type 'struct Foo' with size '40'}} + struct Foo *ptr9 = my_calloc(sizeof(ptr9), 1); // expected-warning {{allocation of insufficient size '8' for type 'struct Foo' with size '40'}} + + // Test function arguments. + foo_consumer(my_malloc(4)); // expected-warning {{allocation of insufficient size '4' for type 'struct Foo' with size '40'}} + + // Test explicit cast. + struct Foo *ptr10 = (struct Foo *)my_malloc(sizeof(*ptr10)); + struct Foo *ptr11 = (struct Foo *)my_malloc(sizeof(ptr11)); // expected-warning {{allocation of insufficient size '8' for type 'struct Foo' with size '40'}} + struct Foo *ptr12 = (struct Foo *)my_calloc(1, sizeof(ptr12)); // expected-warning {{allocation of insufficient size '8' for type 'struct Foo' with size '40'}} + struct Foo *ptr13 = (struct Foo *)my_malloc(4); // expected-warning {{allocation of insufficient size '4' for type 'struct Foo' with size '40'}} + void *ptr14 = (struct Foo *)my_malloc(4); // expected-warning {{allocation of insufficient size '4' for type 'struct Foo' with size '40'}} + + struct Foo *ptr15 = (void *)(struct Foo *)my_malloc(4); // expected-warning 2 {{allocation of insufficient size '4' for type 'struct Foo' with size '40'}} + int *ptr16 = (unsigned *)(void *)(int *)my_malloc(1); // expected-warning {{initializing 'int *' with an expression of type 'unsigned int *' converts between pointers to integer types with different sign}} + // expected-warning@-1 {{allocation of insufficient size '1' for type 'int' with size '4'}} + // expected-warning@-2 {{allocation of insufficient size '1' for type 'unsigned int' with size '4'}} + int *ptr17 = (void *)(int *)my_malloc(1); // expected-warning {{allocation of insufficient size '1' for type 'int' with size '4'}} + // expected-warning@-1 {{allocation of insufficient size '1' for type 'int' with size '4'}} + (void)(int *)my_malloc(1); // expected-warning {{allocation of insufficient size '1' for type 'int' with size '4'}} + + struct ZeroSize *ptr18 = my_malloc(0); // okay because sizeof(struct ZeroSize) = 0 + + void *funcptr_1 = (void (*)(int))my_malloc(0); // expected-warning {{allocation of insufficient size '0' for type 'void (int)' with size '1'}} + void *funcptr_2 = (void (*)(int))my_malloc(1); +}