Skip to content

Commit c7f786a

Browse files
author
git apple-llvm automerger
committed
Merge commit '6dc188d4eb15' from llvm.org/main into next
2 parents 5b99301 + 6dc188d commit c7f786a

19 files changed

+206
-77
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,10 @@ Improvements to Clang's diagnostics
239239
specializations in th same way as it already did for other declarators.
240240
(#GH147333)
241241

242+
- A new warning ``-Walloc-size`` has been added to detect calls to functions
243+
decorated with the ``alloc_size`` attribute don't allocate enough space for
244+
the target pointer type.
245+
242246
Improvements to Clang's time-trace
243247
----------------------------------
244248

clang/include/clang/AST/Expr.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include <optional>
4242

4343
namespace clang {
44+
class AllocSizeAttr;
4445
class APValue;
4546
class ASTContext;
4647
class BlockDecl;
@@ -3294,6 +3295,14 @@ class CallExpr : public Expr {
32943295
setDependence(getDependence() | ExprDependence::TypeValueInstantiation);
32953296
}
32963297

3298+
/// Try to get the alloc_size attribute of the callee. May return null.
3299+
const AllocSizeAttr *getCalleeAllocSizeAttr() const;
3300+
3301+
/// Get the total size in bytes allocated by calling a function decorated with
3302+
/// alloc_size. Returns std::nullopt if the the result cannot be evaluated.
3303+
std::optional<llvm::APInt>
3304+
getBytesReturnedByAllocSizeCall(const ASTContext &Ctx) const;
3305+
32973306
bool isCallToStdMove() const;
32983307

32993308
static bool classof(const Stmt *T) {

clang/include/clang/Basic/AttrDocs.td

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,21 @@ An example of how to use ``alloc_size``
973973
assert(__builtin_object_size(a, 0) == 100);
974974
}
975975

976+
When ``-Walloc-size`` is enabled, this attribute allows the compiler to
977+
diagnose cases when the allocated memory is insufficient for the size of the
978+
type the returned pointer is cast to.
979+
980+
.. code-block:: c
981+
982+
void *my_malloc(int a) __attribute__((alloc_size(1)));
983+
void consumer_func(int *);
984+
985+
int main() {
986+
int *ptr = my_malloc(sizeof(int)); // no warning
987+
int *w = my_malloc(1); // warning: allocation of insufficient size '1' for type 'int' with size '4'
988+
consumer_func(my_malloc(1)); // warning: allocation of insufficient size '1' for type 'int' with size '4'
989+
}
990+
976991
.. Note:: This attribute works differently in clang than it does in GCC.
977992
Specifically, clang will only trace ``const`` pointers (as above); we give up
978993
on pointers that are not marked as ``const``. In the vast majority of cases,

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3715,6 +3715,10 @@ def warn_alloca_align_alignof : Warning<
37153715
"second argument to __builtin_alloca_with_align is supposed to be in bits">,
37163716
InGroup<DiagGroup<"alloca-with-align-alignof">>;
37173717

3718+
def warn_alloc_size : Warning<
3719+
"allocation of insufficient size '%0' for type %1 with size '%2'">,
3720+
InGroup<DiagGroup<"alloc-size">>;
3721+
37183722
def err_alignment_too_small : Error<
37193723
"requested alignment must be %0 or greater">;
37203724
def err_alignment_too_big : Error<

clang/lib/AST/Expr.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3887,6 +3887,56 @@ bool CallExpr::isBuiltinAssumeFalse(const ASTContext &Ctx) const {
38873887
Arg->EvaluateAsBooleanCondition(ArgVal, Ctx) && !ArgVal;
38883888
}
38893889

3890+
const AllocSizeAttr *CallExpr::getCalleeAllocSizeAttr() const {
3891+
if (const FunctionDecl *DirectCallee = getDirectCallee())
3892+
return DirectCallee->getAttr<AllocSizeAttr>();
3893+
if (const Decl *IndirectCallee = getCalleeDecl())
3894+
return IndirectCallee->getAttr<AllocSizeAttr>();
3895+
return nullptr;
3896+
}
3897+
3898+
std::optional<llvm::APInt>
3899+
CallExpr::getBytesReturnedByAllocSizeCall(const ASTContext &Ctx) const {
3900+
const AllocSizeAttr *AllocSize = getCalleeAllocSizeAttr();
3901+
3902+
assert(AllocSize && AllocSize->getElemSizeParam().isValid());
3903+
unsigned SizeArgNo = AllocSize->getElemSizeParam().getASTIndex();
3904+
unsigned BitsInSizeT = Ctx.getTypeSize(Ctx.getSizeType());
3905+
if (getNumArgs() <= SizeArgNo)
3906+
return {};
3907+
3908+
auto EvaluateAsSizeT = [&](const Expr *E, llvm::APSInt &Into) {
3909+
Expr::EvalResult ExprResult;
3910+
if (E->isValueDependent() ||
3911+
!E->EvaluateAsInt(ExprResult, Ctx, Expr::SE_AllowSideEffects))
3912+
return false;
3913+
Into = ExprResult.Val.getInt();
3914+
if (Into.isNegative() || !Into.isIntN(BitsInSizeT))
3915+
return false;
3916+
Into = Into.zext(BitsInSizeT);
3917+
return true;
3918+
};
3919+
3920+
llvm::APSInt SizeOfElem;
3921+
if (!EvaluateAsSizeT(getArg(SizeArgNo), SizeOfElem))
3922+
return {};
3923+
3924+
if (!AllocSize->getNumElemsParam().isValid())
3925+
return SizeOfElem;
3926+
3927+
llvm::APSInt NumberOfElems;
3928+
unsigned NumArgNo = AllocSize->getNumElemsParam().getASTIndex();
3929+
if (!EvaluateAsSizeT(getArg(NumArgNo), NumberOfElems))
3930+
return {};
3931+
3932+
bool Overflow;
3933+
llvm::APInt BytesAvailable = SizeOfElem.umul_ov(NumberOfElems, Overflow);
3934+
if (Overflow)
3935+
return {};
3936+
3937+
return BytesAvailable;
3938+
}
3939+
38903940
bool CallExpr::isCallToStdMove() const {
38913941
return getBuiltinCallee() == Builtin::BImove;
38923942
}

clang/lib/AST/ExprConstant.cpp

Lines changed: 8 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,6 @@ namespace {
119119
return Ctx.getLValueReferenceType(E->getType());
120120
}
121121

122-
/// Given a CallExpr, try to get the alloc_size attribute. May return null.
123-
static const AllocSizeAttr *getAllocSizeAttr(const CallExpr *CE) {
124-
if (const FunctionDecl *DirectCallee = CE->getDirectCallee())
125-
return DirectCallee->getAttr<AllocSizeAttr>();
126-
if (const Decl *IndirectCallee = CE->getCalleeDecl())
127-
return IndirectCallee->getAttr<AllocSizeAttr>();
128-
return nullptr;
129-
}
130-
131122
/// Attempts to unwrap a CallExpr (with an alloc_size attribute) from an Expr.
132123
/// This will look through a single cast.
133124
///
@@ -147,7 +138,7 @@ namespace {
147138
E = Cast->getSubExpr()->IgnoreParens();
148139

149140
if (const auto *CE = dyn_cast<CallExpr>(E))
150-
return getAllocSizeAttr(CE) ? CE : nullptr;
141+
return CE->getCalleeAllocSizeAttr() ? CE : nullptr;
151142
return nullptr;
152143
}
153144

@@ -9674,57 +9665,6 @@ bool LValueExprEvaluator::VisitBinAssign(const BinaryOperator *E) {
96749665
// Pointer Evaluation
96759666
//===----------------------------------------------------------------------===//
96769667

9677-
/// Attempts to compute the number of bytes available at the pointer
9678-
/// returned by a function with the alloc_size attribute. Returns true if we
9679-
/// were successful. Places an unsigned number into `Result`.
9680-
///
9681-
/// This expects the given CallExpr to be a call to a function with an
9682-
/// alloc_size attribute.
9683-
static bool getBytesReturnedByAllocSizeCall(const ASTContext &Ctx,
9684-
const CallExpr *Call,
9685-
llvm::APInt &Result) {
9686-
const AllocSizeAttr *AllocSize = getAllocSizeAttr(Call);
9687-
9688-
assert(AllocSize && AllocSize->getElemSizeParam().isValid());
9689-
unsigned SizeArgNo = AllocSize->getElemSizeParam().getASTIndex();
9690-
unsigned BitsInSizeT = Ctx.getTypeSize(Ctx.getSizeType());
9691-
if (Call->getNumArgs() <= SizeArgNo)
9692-
return false;
9693-
9694-
auto EvaluateAsSizeT = [&](const Expr *E, APSInt &Into) {
9695-
Expr::EvalResult ExprResult;
9696-
if (!E->EvaluateAsInt(ExprResult, Ctx, Expr::SE_AllowSideEffects))
9697-
return false;
9698-
Into = ExprResult.Val.getInt();
9699-
if (Into.isNegative() || !Into.isIntN(BitsInSizeT))
9700-
return false;
9701-
Into = Into.zext(BitsInSizeT);
9702-
return true;
9703-
};
9704-
9705-
APSInt SizeOfElem;
9706-
if (!EvaluateAsSizeT(Call->getArg(SizeArgNo), SizeOfElem))
9707-
return false;
9708-
9709-
if (!AllocSize->getNumElemsParam().isValid()) {
9710-
Result = std::move(SizeOfElem);
9711-
return true;
9712-
}
9713-
9714-
APSInt NumberOfElems;
9715-
unsigned NumArgNo = AllocSize->getNumElemsParam().getASTIndex();
9716-
if (!EvaluateAsSizeT(Call->getArg(NumArgNo), NumberOfElems))
9717-
return false;
9718-
9719-
bool Overflow;
9720-
llvm::APInt BytesAvailable = SizeOfElem.umul_ov(NumberOfElems, Overflow);
9721-
if (Overflow)
9722-
return false;
9723-
9724-
Result = std::move(BytesAvailable);
9725-
return true;
9726-
}
9727-
97289668
/// Convenience function. LVal's base must be a call to an alloc_size
97299669
/// function.
97309670
static bool getBytesReturnedByAllocSizeCall(const ASTContext &Ctx,
@@ -9734,7 +9674,12 @@ static bool getBytesReturnedByAllocSizeCall(const ASTContext &Ctx,
97349674
"Can't get the size of a non alloc_size function");
97359675
const auto *Base = LVal.getLValueBase().get<const Expr *>();
97369676
const CallExpr *CE = tryUnwrapAllocSizeCall(Base);
9737-
return getBytesReturnedByAllocSizeCall(Ctx, CE, Result);
9677+
std::optional<llvm::APInt> Size = CE->getBytesReturnedByAllocSizeCall(Ctx);
9678+
if (!Size)
9679+
return false;
9680+
9681+
Result = std::move(*Size);
9682+
return true;
97389683
}
97399684

97409685
/// Attempts to evaluate the given LValueBase as the result of a call to
@@ -10431,7 +10376,7 @@ bool PointerExprEvaluator::visitNonBuiltinCallExpr(const CallExpr *E) {
1043110376
if (ExprEvaluatorBaseTy::VisitCallExpr(E))
1043210377
return true;
1043310378

10434-
if (!(InvalidBaseOK && getAllocSizeAttr(E)))
10379+
if (!(InvalidBaseOK && E->getCalleeAllocSizeAttr()))
1043510380
return false;
1043610381

1043710382
Result.setInvalid(E);

clang/lib/Sema/SemaExpr.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "clang/AST/ASTDiagnostic.h"
2020
#include "clang/AST/ASTLambda.h"
2121
#include "clang/AST/ASTMutationListener.h"
22+
#include "clang/AST/Attrs.inc"
2223
#include "clang/AST/CXXInheritance.h"
2324
#include "clang/AST/Decl.h"
2425
#include "clang/AST/DeclObjC.h"
@@ -9769,6 +9770,39 @@ ExprResult Sema::CheckExtVectorCast(SourceRange R, QualType DestTy,
97699770
return prepareVectorSplat(DestTy, CastExpr);
97709771
}
97719772

9773+
/// Check that a call to alloc_size function specifies sufficient space for the
9774+
/// destination type.
9775+
static void CheckSufficientAllocSize(Sema &S, QualType DestType,
9776+
const Expr *E) {
9777+
QualType SourceType = E->getType();
9778+
if (!DestType->isPointerType() || !SourceType->isPointerType() ||
9779+
DestType == SourceType)
9780+
return;
9781+
9782+
const auto *CE = dyn_cast<CallExpr>(E->IgnoreParenCasts());
9783+
if (!CE)
9784+
return;
9785+
9786+
// Find the total size allocated by the function call.
9787+
if (!CE->getCalleeAllocSizeAttr())
9788+
return;
9789+
std::optional<llvm::APInt> AllocSize =
9790+
CE->getBytesReturnedByAllocSizeCall(S.Context);
9791+
if (!AllocSize)
9792+
return;
9793+
auto Size = CharUnits::fromQuantity(AllocSize->getZExtValue());
9794+
9795+
QualType TargetType = DestType->getPointeeType();
9796+
// Find the destination size. As a special case function types have size of
9797+
// one byte to match the sizeof operator behavior.
9798+
auto LhsSize = TargetType->isFunctionType()
9799+
? CharUnits::One()
9800+
: S.Context.getTypeSizeInCharsIfKnown(TargetType);
9801+
if (LhsSize && Size < LhsSize)
9802+
S.Diag(E->getExprLoc(), diag::warn_alloc_size)
9803+
<< Size.getQuantity() << TargetType << LhsSize->getQuantity();
9804+
}
9805+
97729806
ExprResult
97739807
Sema::ActOnCastExpr(Scope *S, SourceLocation LParenLoc,
97749808
Declarator &D, ParsedType &Ty,
@@ -9834,6 +9868,8 @@ Sema::ActOnCastExpr(Scope *S, SourceLocation LParenLoc,
98349868

98359869
DiscardMisalignedMemberAddress(castType.getTypePtr(), CastExpr);
98369870

9871+
CheckSufficientAllocSize(*this, castType, CastExpr);
9872+
98379873
return BuildCStyleCastExpr(LParenLoc, castTInfo, RParenLoc, CastExpr);
98389874
}
98399875

@@ -12615,6 +12651,12 @@ AssignConvertType Sema::CheckSingleAssignmentConstraints(QualType LHSType,
1261512651
/*TO_UPSTREAM(BoundsSafety) OFF*/
1261612652
result = CheckAssignmentConstraints(LHSType, RHS, Kind, ConvertRHS);
1261712653

12654+
// If assigning a void * created by an allocation function call to some other
12655+
// type, check that the allocated size is sufficient for that type.
12656+
if (result != AssignConvertType::Incompatible &&
12657+
RHS.get()->getType()->isVoidPointerType())
12658+
CheckSufficientAllocSize(*this, LHSType, RHS.get());
12659+
1261812660
// C99 6.5.16.1p2: The value of the right operand is converted to the
1261912661
// type of the assignment expression.
1262012662
// CheckAssignmentConstraints allows the left-hand side to be a reference,

clang/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator,cplusplus.NewDelete -std=c++11 -verify %s
2-
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator,cplusplus.NewDelete,cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -verify %s
1+
// RUN: %clang_analyze_cc1 -Wno-alloc-size -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator,cplusplus.NewDelete -std=c++11 -verify %s
2+
// RUN: %clang_analyze_cc1 -Wno-alloc-size -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator,cplusplus.NewDelete,cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -verify %s
33

44
#include "Inputs/system-header-simulator-for-malloc.h"
55

clang/test/Analysis/Malloc+MismatchedDeallocator_intersections.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator -std=c++11 -verify %s
1+
// RUN: %clang_analyze_cc1 -Wno-alloc-size -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator -std=c++11 -verify %s
22
// expected-no-diagnostics
33

44
typedef __typeof(sizeof(int)) size_t;

clang/test/Analysis/MismatchedDeallocator-checker-test.mm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.MismatchedDeallocator -fblocks -verify %s
2-
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.MismatchedDeallocator -fblocks -DTEST_INLINABLE_ALLOCATORS -verify %s
1+
// RUN: %clang_analyze_cc1 -Wno-alloc-size -analyzer-checker=core,unix.MismatchedDeallocator -fblocks -verify %s
2+
// RUN: %clang_analyze_cc1 -Wno-alloc-size -analyzer-checker=core,unix.MismatchedDeallocator -fblocks -DTEST_INLINABLE_ALLOCATORS -verify %s
33

44
#include "Inputs/system-header-simulator-objc.h"
55
#include "Inputs/system-header-simulator-cxx.h"

0 commit comments

Comments
 (0)