[clang] [Clang][counted_by] Add support for 'counted_by' on struct pointers (PR #127116)
Bill Wendling via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 25 05:11:45 PST 2025
https://github.com/bwendling updated https://github.com/llvm/llvm-project/pull/127116
>From b5b9b158e7e379f2d430584e3ccf519677bf27a3 Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Fri, 10 Jan 2025 17:13:30 -0800
Subject: [PATCH 1/3] [Clang][counted_by] Add support for 'counted_by' on
struct pointers
The 'counted_by' attribute is now available for structs. It generates
code for sanity checks as well as __builtin_dynamic_object_size()
calculations. For example:
struct annotated_ptr {
int count;
char *buf __counted_by(count);
};
'counted_by' cannot be applied to a pointer to an incomplete type. So
this is invalid:
struct foo;
struct annotated_ptr {
int count;
struct foo *buf __counted_by(count); /* invalid */
};
The 'count' field member may occur after the pointer. If it does, use
the '-fexperimental-late-parse-attributes' flag (which will hopefully be
made the default in future Clang versions).
Signed-off-by: Bill Wendling <morbo at google.com>
---
clang/lib/CodeGen/CGBuiltin.cpp | 259 ++++++++++++++----
clang/lib/CodeGen/CGExpr.cpp | 130 ++++++---
clang/lib/CodeGen/CodeGenFunction.h | 19 +-
.../CodeGen/attr-counted-by-for-pointers.c | 216 +++++++++++++++
4 files changed, 519 insertions(+), 105 deletions(-)
create mode 100644 clang/test/CodeGen/attr-counted-by-for-pointers.c
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 361e4c4bf2e2e..f7ff244a519d0 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -1061,7 +1061,7 @@ class StructFieldAccess
bool AddrOfSeen = false;
public:
- const ArraySubscriptExpr *ASE = nullptr;
+ const Expr *ArrayIndex = nullptr;
const Expr *VisitMemberExpr(const MemberExpr *E) {
if (AddrOfSeen && E->getType()->isArrayType())
@@ -1071,12 +1071,12 @@ class StructFieldAccess
}
const Expr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
- if (ASE)
+ if (ArrayIndex)
// We don't support multiple subscripts.
return nullptr;
AddrOfSeen = false; // '&ptr->array[idx]' is okay.
- ASE = E;
+ ArrayIndex = E->getIdx();
return Visit(E->getBase());
}
const Expr *VisitCastExpr(const CastExpr *E) {
@@ -1168,12 +1168,10 @@ GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
return std::nullopt;
}
-llvm::Value *
-CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
- unsigned Type,
- llvm::IntegerType *ResType) {
- ASTContext &Ctx = getContext();
-
+llvm::Value *CodeGenFunction::emitCountedBySize(const Expr *E,
+ llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
// Note: If the whole struct is specificed in the __bdos (i.e. Visitor
// returns a DeclRefExpr). The calculation of the whole size of the structure
// with a flexible array member can be done in two ways:
@@ -1192,14 +1190,161 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
// GCC does for consistency's sake.
StructFieldAccess Visitor;
- const MemberExpr *ME = dyn_cast_if_present<MemberExpr>(Visitor.Visit(E));
+ E = Visitor.Visit(E);
+ if (!E)
+ return nullptr;
+
+ const Expr *Idx = Visitor.ArrayIndex;
+ if (Idx) {
+ if (Idx->HasSideEffects(getContext()))
+ // We can't have side-effects.
+ return getDefaultBuiltinObjectSizeResult(Type, ResType);
+
+ if (const auto *IL = dyn_cast<IntegerLiteral>(Idx)) {
+ int64_t Val = IL->getValue().getSExtValue();
+ if (Val < 0)
+ return getDefaultBuiltinObjectSizeResult(Type, ResType);
+
+ // The index is 0, so we don't need to take it into account.
+ if (Val == 0)
+ Idx = nullptr;
+ }
+ }
+
+ if (const auto *ME = dyn_cast<MemberExpr>(E))
+ // This is either a flexible array member or a pointer into a struct with a
+ // flexible array member.
+ return emitCountedByMemberSize(ME, Idx, EmittedE, Type, ResType);
+
+ if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E);
+ ICE && ICE->getCastKind() == CK_LValueToRValue)
+ // This may be a pointer.
+ return emitCountedByPointerSize(ICE, Idx, EmittedE, Type, ResType);
+
+ return nullptr;
+}
+
+llvm::Value *CodeGenFunction::emitCountedByPointerSize(
+ const ImplicitCastExpr *E, const Expr *Idx, llvm::Value *EmittedE,
+ unsigned Type, llvm::IntegerType *ResType) {
+ assert(E->getCastKind() == CK_LValueToRValue &&
+ "must be an LValue to RValue cast");
+
+ const MemberExpr *ME = dyn_cast<MemberExpr>(E->getSubExpr());
if (!ME)
return nullptr;
+ const auto *ArrayBaseFD = dyn_cast<FieldDecl>(ME->getMemberDecl());
+ if (!ArrayBaseFD || !ArrayBaseFD->getType()->isPointerType() ||
+ !ArrayBaseFD->getType()->isCountAttributedType())
+ return nullptr;
+
+ // Get the 'count' FieldDecl.
+ const FieldDecl *CountFD = ArrayBaseFD->findCountedByField();
+ if (!CountFD)
+ // Can't find the field referenced by the "counted_by" attribute.
+ return nullptr;
+
+ // Calculate the array's object size using these formulae. (Note: if the
+ // calculation is negative, we return 0.):
+ //
+ // struct p;
+ // struct s {
+ // /* ... */
+ // struct p **array __attribute__((counted_by(count)));
+ // int count;
+ // };
+ //
+ // 1) 'ptr->array':
+ //
+ // count = ptr->count;
+ //
+ // array_base_size = sizeof (*ptr->array);
+ // array_size = count * array_base_size;
+ //
+ // result = array_size;
+ //
+ // cmp = (result >= 0)
+ // return cmp ? result : 0;
+ //
+ // 2) '&ptr->array[idx]':
+ //
+ // count = ptr->count;
+ // index = idx;
+ //
+ // array_base_size = sizeof (*ptr->array_base_size);
+ // array_size = count * array_base_size;
+ //
+ // index_size = index * array_base_size;
+ //
+ // result = array_size - index_size;
+ //
+ // cmp = (result >= 0)
+ // if (index)
+ // cmp = (cmp && index > 0)
+ // return cmp ? result : 0;
+
+ bool IsSigned = CountFD->getType()->isSignedIntegerType();
+
+ // count = ptr->count;
+ Value *Count = EmitLoadOfCountedByField(ME, ArrayBaseFD, CountFD);
+ if (!Count)
+ return nullptr;
+ Count = Builder.CreateIntCast(Count, ResType, IsSigned, "count");
+
+ // index = ptr->index;
+ Value *Index = nullptr;
+ if (Idx) {
+ bool IdxSigned = Idx->getType()->isSignedIntegerType();
+ Index = EmitScalarExpr(Idx);
+ Index = Builder.CreateIntCast(Index, ResType, IdxSigned, "index");
+ }
+
+ // array_base_size = sizeof (*ptr->array);
+ ASTContext &Ctx = getContext();
+ QualType ArrayBaseTy = ArrayBaseFD->getType()->getPointeeType();
+ CharUnits BaseSize = Ctx.getTypeSizeInChars(ArrayBaseTy);
+ auto *ArrayBaseSize =
+ llvm::ConstantInt::get(ResType, BaseSize.getQuantity(), IsSigned);
+
+ // array_size = count * array_base_size;
+ Value *ArraySize = Builder.CreateMul(Count, ArrayBaseSize, "array_size",
+ !IsSigned, IsSigned);
+
+ // Option (1) 'ptr->array'
+ // result = array_size
+ Value *Res = ArraySize;
+
+ if (Idx) { // Option (2) '&ptr->array[idx]'
+ // index_size = index * array_base_size;
+ Value *IndexSize = Builder.CreateMul(ArrayBaseSize, Index, "index_size",
+ !IsSigned, IsSigned);
+
+ // result = result - index_size;
+ Res = Builder.CreateSub(Res, IndexSize, "result", !IsSigned, IsSigned);
+ }
+
+ // cmp = (array_size >= 0)
+ Value *Cmp = Builder.CreateIsNotNeg(Res);
+ if (Idx)
+ // cmp = (cmp && index >= 0)
+ Cmp = Builder.CreateAnd(Builder.CreateIsNotNeg(Index), Cmp);
+
+ // return cmp ? result : 0
+ return Builder.CreateSelect(Cmp, Res, ConstantInt::get(ResType, 0, IsSigned));
+}
+
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const MemberExpr *ME, const Expr *Idx,
+ llvm::Value *EmittedE, unsigned Type,
+ llvm::IntegerType *ResType) {
const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
if (!FD)
return nullptr;
+ // Find the flexible array member and check that it has the __counted_by
+ // attribute.
+ ASTContext &Ctx = getContext();
const RecordDecl *RD = FD->getDeclContext()->getOuterLexicalRecordContext();
const FieldDecl *FlexibleArrayMemberFD = nullptr;
@@ -1214,32 +1359,14 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
!FlexibleArrayMemberFD->getType()->isCountAttributedType())
return nullptr;
+ // Get the 'count' FieldDecl.
const FieldDecl *CountFD = FlexibleArrayMemberFD->findCountedByField();
if (!CountFD)
// Can't find the field referenced by the "counted_by" attribute.
return nullptr;
- const Expr *Idx = nullptr;
- if (Visitor.ASE) {
- Idx = Visitor.ASE->getIdx();
-
- if (Idx->HasSideEffects(Ctx))
- // We can't have side-effects.
- return getDefaultBuiltinObjectSizeResult(Type, ResType);
-
- if (const auto *IL = dyn_cast<IntegerLiteral>(Idx)) {
- int64_t Val = IL->getValue().getSExtValue();
- if (Val < 0)
- return getDefaultBuiltinObjectSizeResult(Type, ResType);
-
- // The index is 0, so we don't need to take it into account.
- if (Val == 0)
- Idx = nullptr;
- }
- }
-
- // Calculate the flexible array member's object size using these formulae
- // (note: if the calculation is negative, we return 0.):
+ // Calculate the flexible array member's object size using these formulae.
+ // (Note: if the calculation is negative, we return 0.):
//
// struct p;
// struct s {
@@ -1256,9 +1383,11 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
// flexible_array_member_size =
// count * flexible_array_member_base_size;
//
- // if (flexible_array_member_size < 0)
- // return 0;
- // return flexible_array_member_size;
+ // result = flexible_array_member_size;
+ //
+ // cmp = (result >= 0)
+ //
+ // return cmp ? result : 0;
//
// 2) '&ptr->array[idx]':
//
@@ -1271,9 +1400,13 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
//
// index_size = index * flexible_array_member_base_size;
//
- // if (flexible_array_member_size < 0 || index < 0)
- // return 0;
- // return flexible_array_member_size - index_size;
+ // result = flexible_array_member_size - index_size;
+ //
+ // cmp = (result >= 0)
+ // if (index != 0)
+ // cmp = (cmp && index >= 0)
+ //
+ // return cmp ? result : 0;
//
// 3) '&ptr->field':
//
@@ -1287,9 +1420,11 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
// field_offset = offsetof (struct s, field);
// offset_diff = sizeof_struct - field_offset;
//
- // if (flexible_array_member_size < 0)
- // return 0;
- // return offset_diff + flexible_array_member_size;
+ // result = offset_diff + flexible_array_member_size;
+ //
+ // cmp = (result >= 0)
+ //
+ // return cmp ? result : 0;
//
// 4) '&ptr->field_array[idx]':
//
@@ -1307,21 +1442,24 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
//
// offset_diff = sizeof_struct - field_offset;
//
- // if (flexible_array_member_size < 0 || index < 0)
- // return 0;
- // return offset_diff + flexible_array_member_size;
+ // result = offset_diff + flexible_array_member_size;
+ //
+ // cmp = (result >= 0)
+ // if (index != 0)
+ // cmp = (cmp && index >= 0)
+ //
+ // return cmp ? result : 0;
- QualType CountTy = CountFD->getType();
- bool IsSigned = CountTy->isSignedIntegerType();
+ bool IsSigned = CountFD->getType()->isSignedIntegerType();
QualType FlexibleArrayMemberTy = FlexibleArrayMemberFD->getType();
QualType FieldTy = FD->getType();
// Explicit cast because otherwise the CharWidth will promote an i32's into
- // u64's leading to overflows..
+ // u64's leading to overflows.
int64_t CharWidth = static_cast<int64_t>(CGM.getContext().getCharWidth());
- // size_t field_offset = offsetof (struct s, field);
+ // field_offset = offsetof (struct s, field);
Value *FieldOffset = nullptr;
if (FlexibleArrayMemberFD != FD) {
std::optional<int64_t> Offset = GetFieldOffset(Ctx, RD, FD);
@@ -1331,13 +1469,13 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
llvm::ConstantInt::get(ResType, *Offset / CharWidth, IsSigned);
}
- // size_t count = (size_t) ptr->count;
+ // count = ptr->count;
Value *Count = EmitLoadOfCountedByField(ME, FlexibleArrayMemberFD, CountFD);
if (!Count)
return nullptr;
Count = Builder.CreateIntCast(Count, ResType, IsSigned, "count");
- // size_t index = (size_t) ptr->index;
+ // index = ptr->index;
Value *Index = nullptr;
if (Idx) {
bool IdxSigned = Idx->getType()->isSignedIntegerType();
@@ -1345,13 +1483,13 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
Index = Builder.CreateIntCast(Index, ResType, IdxSigned, "index");
}
- // size_t flexible_array_member_base_size = sizeof (*ptr->array);
+ // flexible_array_member_base_size = sizeof (*ptr->array);
const ArrayType *ArrayTy = Ctx.getAsArrayType(FlexibleArrayMemberTy);
CharUnits BaseSize = Ctx.getTypeSizeInChars(ArrayTy->getElementType());
auto *FlexibleArrayMemberBaseSize =
llvm::ConstantInt::get(ResType, BaseSize.getQuantity(), IsSigned);
- // size_t flexible_array_member_size =
+ // flexible_array_member_size =
// count * flexible_array_member_base_size;
Value *FlexibleArrayMemberSize =
Builder.CreateMul(Count, FlexibleArrayMemberBaseSize,
@@ -1360,19 +1498,19 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
Value *Res = nullptr;
if (FlexibleArrayMemberFD == FD) {
if (Idx) { // Option (2) '&ptr->array[idx]'
- // size_t index_size = index * flexible_array_member_base_size;
+ // index_size = index * flexible_array_member_base_size;
Value *IndexSize = Builder.CreateMul(FlexibleArrayMemberBaseSize, Index,
"index_size", !IsSigned, IsSigned);
- // return flexible_array_member_size - index_size;
+ // result = flexible_array_member_size - index_size;
Res = Builder.CreateSub(FlexibleArrayMemberSize, IndexSize, "result",
!IsSigned, IsSigned);
} else { // Option (1) 'ptr->array'
- // return flexible_array_member_size;
+ // result = flexible_array_member_size;
Res = FlexibleArrayMemberSize;
}
} else {
- // size_t sizeof_struct = sizeof (struct s);
+ // sizeof_struct = sizeof (struct s);
llvm::StructType *StructTy = getTypes().getCGRecordLayout(RD).getLLVMType();
const llvm::DataLayout &Layout = CGM.getDataLayout();
TypeSize Size = Layout.getTypeSizeInBits(StructTy);
@@ -1380,7 +1518,7 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
llvm::ConstantInt::get(ResType, Size.getKnownMinValue() / CharWidth);
if (Idx) { // Option (4) '&ptr->field_array[idx]'
- // size_t field_base_size = sizeof (*ptr->field_array);
+ // field_base_size = sizeof (*ptr->field_array);
const ArrayType *ArrayTy = Ctx.getAsArrayType(FieldTy);
CharUnits BaseSize = Ctx.getTypeSizeInChars(ArrayTy->getElementType());
auto *FieldBaseSize =
@@ -1393,18 +1531,21 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
}
// Option (3) '&ptr->field', and Option (4) continuation.
- // size_t offset_diff = flexible_array_member_offset - field_offset;
+ // offset_diff = flexible_array_member_offset - field_offset;
Value *OffsetDiff = Builder.CreateSub(SizeofStruct, FieldOffset,
"offset_diff", !IsSigned, IsSigned);
- // return offset_diff + flexible_array_member_size;
+ // result = offset_diff + flexible_array_member_size;
Res = Builder.CreateAdd(FlexibleArrayMemberSize, OffsetDiff, "result");
}
+ // cmp = (array_size >= 0)
Value *Cmp = Builder.CreateIsNotNeg(Res);
if (Idx)
+ // cmp = (cmp && index >= 0)
Cmp = Builder.CreateAnd(Builder.CreateIsNotNeg(Index), Cmp);
+ // return cmp ? result : 0
return Builder.CreateSelect(Cmp, Res, ConstantInt::get(ResType, 0, IsSigned));
}
@@ -1453,7 +1594,7 @@ CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type,
if (IsDynamic)
// Emit special code for a flexible array member with the "counted_by"
// attribute.
- if (Value *V = emitCountedByMemberSize(E, Ptr, Type, ResType))
+ if (Value *V = emitCountedBySize(E, Ptr, Type, ResType))
return V;
Function *F =
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 2bbc0791c6587..876eb06772273 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -4248,6 +4248,52 @@ static std::optional<int64_t> getOffsetDifferenceInBits(CodeGenFunction &CGF,
return std::make_optional<int64_t>(FD1Offset - FD2Offset);
}
+/// EmitCountedByBoundsChecking - If the array being accessed has a "counted_by"
+/// attribute, generate bounds checking code. The "count" field is at the top
+/// level of the struct or in an anonymous struct, that's also at the top level.
+/// Future expansions may allow the "count" to reside at any place in the
+/// struct, but the value of "counted_by" will be a "simple" path to the count,
+/// i.e. "a.b.count", so we shouldn't need the full force of EmitLValue or
+/// similar to emit the correct GEP.
+void CodeGenFunction::EmitCountedByBoundsChecking(
+ const Expr *E, llvm::Value *Idx, Address Addr, QualType IdxTy,
+ QualType ArrayTy, bool Accessed, bool FlexibleArray) {
+ const auto *ME = dyn_cast<MemberExpr>(E->IgnoreImpCasts());
+ if (!ME || !ME->getMemberDecl()->getType()->isCountAttributedType())
+ return;
+
+ const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
+ getLangOpts().getStrictFlexArraysLevel();
+ if (FlexibleArray &&
+ !ME->isFlexibleArrayMemberLike(getContext(), StrictFlexArraysLevel))
+ return;
+
+ const FieldDecl *FD = cast<FieldDecl>(ME->getMemberDecl());
+ const FieldDecl *CountFD = FD->findCountedByField();
+ if (!CountFD)
+ return;
+
+ if (std::optional<int64_t> Diff = getOffsetDifferenceInBits(*this, CountFD, FD)) {
+ // FIXME: The 'static_cast' is necessary, otherwise the result turns into a
+ // uint64_t, which messes things up if we have a negative offset difference.
+ Diff = *Diff / static_cast<int64_t>(CGM.getContext().getCharWidth());
+
+ // Create a GEP with the byte offset between the counted object and the
+ // count and use that to load the count value.
+ Addr = Builder.CreatePointerBitCastOrAddrSpaceCast(Addr, Int8PtrTy, Int8Ty);
+
+ llvm::Type *CountTy = ConvertType(CountFD->getType());
+ llvm::Value *Res = Builder.CreateInBoundsGEP(
+ Int8Ty, Addr.emitRawPointer(*this),
+ Builder.getInt32(*Diff), ".counted_by.gep");
+ Res = Builder.CreateAlignedLoad(CountTy, Res, getIntAlign(),
+ ".counted_by.load");
+
+ // Now emit the bounds checking.
+ EmitBoundsCheckImpl(E, Res, Idx, IdxTy, ArrayTy, Accessed);
+ }
+}
+
LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
bool Accessed) {
// The index must always be an integer, which is not an aggregate. Emit it
@@ -4374,46 +4420,10 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
ArrayLV = EmitLValue(Array);
auto *Idx = EmitIdxAfterBase(/*Promote*/true);
- if (SanOpts.has(SanitizerKind::ArrayBounds)) {
- // If the array being accessed has a "counted_by" attribute, generate
- // bounds checking code. The "count" field is at the top level of the
- // struct or in an anonymous struct, that's also at the top level. Future
- // expansions may allow the "count" to reside at any place in the struct,
- // but the value of "counted_by" will be a "simple" path to the count,
- // i.e. "a.b.count", so we shouldn't need the full force of EmitLValue or
- // similar to emit the correct GEP.
- const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
- getLangOpts().getStrictFlexArraysLevel();
-
- if (const auto *ME = dyn_cast<MemberExpr>(Array);
- ME &&
- ME->isFlexibleArrayMemberLike(getContext(), StrictFlexArraysLevel) &&
- ME->getMemberDecl()->getType()->isCountAttributedType()) {
- const FieldDecl *FAMDecl = cast<FieldDecl>(ME->getMemberDecl());
- if (const FieldDecl *CountFD = FAMDecl->findCountedByField()) {
- if (std::optional<int64_t> Diff =
- getOffsetDifferenceInBits(*this, CountFD, FAMDecl)) {
- CharUnits OffsetDiff = CGM.getContext().toCharUnitsFromBits(*Diff);
-
- // Create a GEP with a byte offset between the FAM and count and
- // use that to load the count value.
- Addr = Builder.CreatePointerBitCastOrAddrSpaceCast(
- ArrayLV.getAddress(), Int8PtrTy, Int8Ty);
-
- llvm::Type *CountTy = ConvertType(CountFD->getType());
- llvm::Value *Res = Builder.CreateInBoundsGEP(
- Int8Ty, Addr.emitRawPointer(*this),
- Builder.getInt32(OffsetDiff.getQuantity()), ".counted_by.gep");
- Res = Builder.CreateAlignedLoad(CountTy, Res, getIntAlign(),
- ".counted_by.load");
-
- // Now emit the bounds checking.
- EmitBoundsCheckImpl(E, Res, Idx, E->getIdx()->getType(),
- Array->getType(), Accessed);
- }
- }
- }
- }
+ if (SanOpts.has(SanitizerKind::ArrayBounds))
+ EmitCountedByBoundsChecking(Array, Idx, ArrayLV.getAddress(),
+ E->getIdx()->getType(), Array->getType(),
+ Accessed, /*FlexibleArray=*/true);
// Propagate the alignment from the array itself to the result.
QualType arrayType = Array->getType();
@@ -4425,12 +4435,44 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
EltTBAAInfo = CGM.getTBAAInfoForSubobject(ArrayLV, E->getType());
} else {
// The base must be a pointer; emit it with an estimate of its alignment.
- Addr = EmitPointerWithAlignment(E->getBase(), &EltBaseInfo, &EltTBAAInfo);
+ Address BaseAddr =
+ EmitPointerWithAlignment(E->getBase(), &EltBaseInfo, &EltTBAAInfo);
auto *Idx = EmitIdxAfterBase(/*Promote*/true);
QualType ptrType = E->getBase()->getType();
- Addr = emitArraySubscriptGEP(
- *this, Addr, Idx, E->getType(), !getLangOpts().PointerOverflowDefined,
- SignedIndices, E->getExprLoc(), &ptrType, E->getBase());
+ Addr = emitArraySubscriptGEP(*this, BaseAddr, Idx, E->getType(),
+ !getLangOpts().PointerOverflowDefined,
+ SignedIndices, E->getExprLoc(), &ptrType,
+ E->getBase());
+
+ if (SanOpts.has(SanitizerKind::ArrayBounds)) {
+ // FIXME: There *has* to be a better way to get the base GEP than
+ // crawling back through the LoadInst.
+ const Expr *Base = E->getBase();
+ while (true) {
+ if (const auto *CE = dyn_cast<CastExpr>(Base)) {
+ if (CE->getCastKind() == CK_LValueToRValue)
+ break;
+ Base = CE->getSubExpr();
+ } else if (const auto *PE = dyn_cast<ParenExpr>(Base)) {
+ Base = PE->getSubExpr();
+ } else {
+ break;
+ }
+ }
+
+ if (const auto *CE = dyn_cast<CastExpr>(Base);
+ CE && CE->getCastKind() == CK_LValueToRValue) {
+ // FIXME: This is a bit fragile. See if we can strengthen it a bit
+ // better.
+ if (const auto *ME = dyn_cast<MemberExpr>(CE->getSubExpr());
+ ME && ME->getMemberDecl()->getType()->isCountAttributedType()) {
+ LValue LV = EmitCheckedLValue(Base, TCK_MemberAccess);
+ EmitCountedByBoundsChecking(E->getBase(), Idx, LV.getAddress(),
+ E->getIdx()->getType(), ptrType, Accessed,
+ /*FlexibleArray=*/false);
+ }
+ }
+ }
}
LValue LV = MakeAddrLValue(Addr, E->getType(), EltBaseInfo, EltTBAAInfo);
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 64cd8a3ac55e2..1efe21b70783d 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3313,6 +3313,13 @@ class CodeGenFunction : public CodeGenTypeCache {
llvm::Value *EmitLoadOfCountedByField(const Expr *Base, const FieldDecl *FD,
const FieldDecl *CountDecl);
+ // Emit bounds checking for flexible array and pointer members with the
+ // counted_by attribute.
+ void EmitCountedByBoundsChecking(const Expr *E, llvm::Value *Idx,
+ Address Addr, QualType IdxTy,
+ QualType ArrayTy, bool Accessed,
+ bool FlexibleArray);
+
llvm::Value *EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
bool isInc, bool isPre);
ComplexPairTy EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV,
@@ -5332,10 +5339,18 @@ class CodeGenFunction : public CodeGenTypeCache {
llvm::IntegerType *ResType,
llvm::Value *EmittedE, bool IsDynamic);
- llvm::Value *emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
- unsigned Type,
+ llvm::Value *emitCountedBySize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type, llvm::IntegerType *ResType);
+
+ llvm::Value *emitCountedByMemberSize(const MemberExpr *E, const Expr *Idx,
+ llvm::Value *EmittedE, unsigned Type,
llvm::IntegerType *ResType);
+ llvm::Value *emitCountedByPointerSize(const ImplicitCastExpr *E,
+ const Expr *Idx, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType);
+
void emitZeroOrPatternForAutoVarInit(QualType type, const VarDecl &D,
Address Loc);
diff --git a/clang/test/CodeGen/attr-counted-by-for-pointers.c b/clang/test/CodeGen/attr-counted-by-for-pointers.c
new file mode 100644
index 0000000000000..dc40721806a61
--- /dev/null
+++ b/clang/test/CodeGen/attr-counted-by-for-pointers.c
@@ -0,0 +1,216 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -DCOUNTED_BY -Wall -fsanitize=array-bounds,object-size,local-bounds -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=SANITIZE-WITH-ATTR %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -DCOUNTED_BY -Wall -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=NO-SANITIZE-WITH-ATTR %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -Wall -fsanitize=array-bounds,object-size,local-bounds -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=SANITIZE-WITHOUT-ATTR %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -Wall -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=NO-SANITIZE-WITHOUT-ATTR %s
+
+#if !__has_attribute(counted_by)
+#error "has attribute broken"
+#endif
+
+#ifdef COUNTED_BY
+#define __counted_by(member) __attribute__((__counted_by__(member)))
+#else
+#define __counted_by(member)
+#endif
+
+#define __bdos(P) __builtin_dynamic_object_size(P, 0)
+
+typedef long unsigned int size_t;
+
+struct foo { size_t field; };
+struct annotated_ptr {
+ unsigned long flags;
+ struct foo **buf __counted_by(ptr_count);
+ int ptr_count;
+};
+
+// SANITIZE-WITH-ATTR-LABEL: define dso_local void @test1(
+// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// SANITIZE-WITH-ATTR-NEXT: entry:
+// SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
+// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2:![0-9]+]]
+// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp ult i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP1]], label [[CONT10:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3:![0-9]+]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR: handler.out_of_bounds:
+// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB2:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3:[0-9]+]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR: cont10:
+// SANITIZE-WITH-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA4:![0-9]+]]
+// SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP2]], i64 [[IDXPROM]]
+// SANITIZE-WITH-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA12:![0-9]+]]
+// SANITIZE-WITH-ATTR-NEXT: ret void
+//
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local void @test1(
+// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// NO-SANITIZE-WITH-ATTR-NEXT: entry:
+// NO-SANITIZE-WITH-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2:![0-9]+]]
+// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
+// NO-SANITIZE-WITH-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10:![0-9]+]]
+// NO-SANITIZE-WITH-ATTR-NEXT: ret void
+//
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test1(
+// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// SANITIZE-WITHOUT-ATTR-NEXT: entry:
+// SANITIZE-WITHOUT-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// SANITIZE-WITHOUT-ATTR-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2:![0-9]+]]
+// SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
+// SANITIZE-WITHOUT-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10:![0-9]+]]
+// SANITIZE-WITHOUT-ATTR-NEXT: ret void
+//
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test1(
+// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2:![0-9]+]]
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10:![0-9]+]]
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret void
+//
+void test1(struct annotated_ptr *p, int index, struct foo *value) {
+ p->buf[index] = value;
+}
+
+// SANITIZE-WITH-ATTR-LABEL: define dso_local void @test2(
+// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITH-ATTR-NEXT: entry:
+// SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
+// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT: [[DOTNOT:%.*]] = icmp ugt i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT: br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], label [[CONT10:%.*]], !prof [[PROF14:![0-9]+]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR: handler.out_of_bounds:
+// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB3:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR: cont10:
+// SANITIZE-WITH-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA4]]
+// SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP1]], i64 [[IDXPROM]]
+// SANITIZE-WITH-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA12]]
+// SANITIZE-WITH-ATTR-NEXT: ret void
+//
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local void @test2(
+// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// NO-SANITIZE-WITH-ATTR-NEXT: entry:
+// NO-SANITIZE-WITH-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
+// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
+// NO-SANITIZE-WITH-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10]]
+// NO-SANITIZE-WITH-ATTR-NEXT: ret void
+//
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test2(
+// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITHOUT-ATTR-NEXT: entry:
+// SANITIZE-WITHOUT-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// SANITIZE-WITHOUT-ATTR-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
+// SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
+// SANITIZE-WITHOUT-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10]]
+// SANITIZE-WITHOUT-ATTR-NEXT: ret void
+//
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test2(
+// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10]]
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret void
+//
+void test2(struct annotated_ptr *p, int index, struct foo *value) {
+ *&*&*&p->buf[index] = value;
+}
+
+// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -17179869184, 17179869177) i64 @test3(
+// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITH-ATTR-NEXT: entry:
+// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
+// SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
+// SANITIZE-WITH-ATTR-NEXT: [[ARRAY_SIZE:%.*]] = shl nsw i64 [[COUNT]], 3
+// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp sgt i32 [[COUNTED_BY_LOAD]], -1
+// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = select i1 [[TMP0]], i64 [[ARRAY_SIZE]], i64 0
+// SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP1]]
+//
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -17179869184, 17179869177) i64 @test3(
+// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
+// NO-SANITIZE-WITH-ATTR-NEXT: entry:
+// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
+// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAY_SIZE:%.*]] = shl nsw i64 [[COUNT]], 3
+// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp sgt i32 [[COUNTED_BY_LOAD]], -1
+// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = select i1 [[TMP0]], i64 [[ARRAY_SIZE]], i64 0
+// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP1]]
+//
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test3(
+// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITHOUT-ATTR-NEXT: entry:
+// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
+//
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test3(
+// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
+//
+size_t test3(struct annotated_ptr *p, int index) {
+ return __bdos(p->buf);
+}
+
+// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 17179869177) i64 @test4(
+// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITH-ATTR-NEXT: entry:
+// SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
+// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT: [[DOTNOT:%.*]] = icmp ugt i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT: br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], label [[CONT8:%.*]], !prof [[PROF14]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR: handler.out_of_bounds:
+// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB4:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR: cont8:
+// SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[DOTCOUNTED_BY_LOAD]] to i64
+// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = sub nsw i64 [[COUNT]], [[IDXPROM]]
+// SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = tail call i64 @llvm.smax.i64(i64 [[TMP1]], i64 0)
+// SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = shl nuw nsw i64 [[TMP2]], 3
+// SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP3]]
+//
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -34359738360, 34359738361) i64 @test4(
+// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
+// NO-SANITIZE-WITH-ATTR-NEXT: entry:
+// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
+// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = sub nsw i64 [[COUNT]], [[IDXPROM]]
+// NO-SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = shl nsw i64 [[TMP0]], 3
+// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp sgt i64 [[TMP0]], -1
+// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[INDEX]], -1
+// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = and i1 [[TMP2]], [[TMP1]]
+// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP4:%.*]] = select i1 [[TMP3]], i64 [[RESULT]], i64 0
+// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP4]]
+//
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test4(
+// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITHOUT-ATTR-NEXT: entry:
+// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
+//
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test4(
+// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
+//
+size_t test4(struct annotated_ptr *p, int index) {
+ return __bdos(&p->buf[index]);
+}
>From 638dbc89f4983acc5ba1b8a3b94f4b174dc0959f Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Thu, 13 Feb 2025 12:04:32 -0800
Subject: [PATCH 2/3] Reformat
---
clang/lib/CodeGen/CGExpr.cpp | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 876eb06772273..d2a90c0ddf33a 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -4273,7 +4273,8 @@ void CodeGenFunction::EmitCountedByBoundsChecking(
if (!CountFD)
return;
- if (std::optional<int64_t> Diff = getOffsetDifferenceInBits(*this, CountFD, FD)) {
+ if (std::optional<int64_t> Diff =
+ getOffsetDifferenceInBits(*this, CountFD, FD)) {
// FIXME: The 'static_cast' is necessary, otherwise the result turns into a
// uint64_t, which messes things up if we have a negative offset difference.
Diff = *Diff / static_cast<int64_t>(CGM.getContext().getCharWidth());
@@ -4283,9 +4284,9 @@ void CodeGenFunction::EmitCountedByBoundsChecking(
Addr = Builder.CreatePointerBitCastOrAddrSpaceCast(Addr, Int8PtrTy, Int8Ty);
llvm::Type *CountTy = ConvertType(CountFD->getType());
- llvm::Value *Res = Builder.CreateInBoundsGEP(
- Int8Ty, Addr.emitRawPointer(*this),
- Builder.getInt32(*Diff), ".counted_by.gep");
+ llvm::Value *Res =
+ Builder.CreateInBoundsGEP(Int8Ty, Addr.emitRawPointer(*this),
+ Builder.getInt32(*Diff), ".counted_by.gep");
Res = Builder.CreateAlignedLoad(CountTy, Res, getIntAlign(),
".counted_by.load");
>From f7722fb9a85144c26fd0e70dff140f084e24cb72 Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Wed, 19 Feb 2025 12:34:37 -0800
Subject: [PATCH 3/3] Add support for the __sized_by attribute on a 'void *'.
---
clang/lib/CodeGen/CGBuiltin.cpp | 27 ++++--
.../CodeGen/attr-counted-by-for-pointers.c | 92 ++++++++++++++++++-
2 files changed, 109 insertions(+), 10 deletions(-)
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index f7ff244a519d0..aa800c016cf8f 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -1286,6 +1286,26 @@ llvm::Value *CodeGenFunction::emitCountedByPointerSize(
bool IsSigned = CountFD->getType()->isSignedIntegerType();
+ // array_base_size = sizeof (*ptr->array);
+ ASTContext &Ctx = getContext();
+ QualType ArrayBaseTy = ArrayBaseFD->getType()->getPointeeType();
+ CharUnits BaseSize = Ctx.getTypeSizeInChars(ArrayBaseTy);
+ if (BaseSize.isZero()) {
+ // This might be a __sized_by on a 'void *', which counts bytes, not
+ // elements.
+ auto *CAT = ArrayBaseFD->getType()->getAs<CountAttributedType>();
+ if (CAT->getKind() != CountAttributedType::SizedBy &&
+ CAT->getKind() != CountAttributedType::SizedByOrNull)
+ // Okay, not sure what it is now.
+ // FIXME: Should this be an assert?
+ return nullptr;
+
+ BaseSize = CharUnits::One();
+ }
+
+ auto *ArrayBaseSize =
+ llvm::ConstantInt::get(ResType, BaseSize.getQuantity(), IsSigned);
+
// count = ptr->count;
Value *Count = EmitLoadOfCountedByField(ME, ArrayBaseFD, CountFD);
if (!Count)
@@ -1300,13 +1320,6 @@ llvm::Value *CodeGenFunction::emitCountedByPointerSize(
Index = Builder.CreateIntCast(Index, ResType, IdxSigned, "index");
}
- // array_base_size = sizeof (*ptr->array);
- ASTContext &Ctx = getContext();
- QualType ArrayBaseTy = ArrayBaseFD->getType()->getPointeeType();
- CharUnits BaseSize = Ctx.getTypeSizeInChars(ArrayBaseTy);
- auto *ArrayBaseSize =
- llvm::ConstantInt::get(ResType, BaseSize.getQuantity(), IsSigned);
-
// array_size = count * array_base_size;
Value *ArraySize = Builder.CreateMul(Count, ArrayBaseSize, "array_size",
!IsSigned, IsSigned);
diff --git a/clang/test/CodeGen/attr-counted-by-for-pointers.c b/clang/test/CodeGen/attr-counted-by-for-pointers.c
index dc40721806a61..bf215d506746c 100644
--- a/clang/test/CodeGen/attr-counted-by-for-pointers.c
+++ b/clang/test/CodeGen/attr-counted-by-for-pointers.c
@@ -1,6 +1,6 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -DCOUNTED_BY -Wall -fsanitize=array-bounds,object-size,local-bounds -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=SANITIZE-WITH-ATTR %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -DCOUNTED_BY -Wall -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=NO-SANITIZE-WITH-ATTR %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -DWITH_ATTRS -Wall -fsanitize=array-bounds,object-size,local-bounds -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=SANITIZE-WITH-ATTR %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -DWITH_ATTRS -Wall -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=NO-SANITIZE-WITH-ATTR %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -Wall -fsanitize=array-bounds,object-size,local-bounds -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=SANITIZE-WITHOUT-ATTR %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -Wall -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=NO-SANITIZE-WITHOUT-ATTR %s
@@ -8,10 +8,12 @@
#error "has attribute broken"
#endif
-#ifdef COUNTED_BY
+#ifdef WITH_ATTRS
#define __counted_by(member) __attribute__((__counted_by__(member)))
+#define __sized_by(member) __attribute__((__sized_by__(member)))
#else
#define __counted_by(member)
+#define __sized_by(member)
#endif
#define __bdos(P) __builtin_dynamic_object_size(P, 0)
@@ -214,3 +216,87 @@ size_t test3(struct annotated_ptr *p, int index) {
size_t test4(struct annotated_ptr *p, int index) {
return __bdos(&p->buf[index]);
}
+
+struct annotated_sized_ptr {
+ unsigned long flags;
+ void *buf __sized_by(ptr_count);
+ int ptr_count;
+};
+
+// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test5(
+// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITH-ATTR-NEXT: entry:
+// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
+// SANITIZE-WITH-ATTR-NEXT: [[NARROW:%.*]] = tail call i32 @llvm.smax.i32(i32 [[COUNTED_BY_LOAD]], i32 0)
+// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = zext nneg i32 [[NARROW]] to i64
+// SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP0]]
+//
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test5(
+// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
+// NO-SANITIZE-WITH-ATTR-NEXT: entry:
+// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
+// NO-SANITIZE-WITH-ATTR-NEXT: [[NARROW:%.*]] = tail call i32 @llvm.smax.i32(i32 [[COUNTED_BY_LOAD]], i32 0)
+// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = zext nneg i32 [[NARROW]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP0]]
+//
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test5(
+// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITHOUT-ATTR-NEXT: entry:
+// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
+//
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test5(
+// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
+//
+size_t test5(struct annotated_sized_ptr *p, int index) {
+ return __bdos(p->buf);
+}
+
+// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test6(
+// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITH-ATTR-NEXT: entry:
+// SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
+// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT: [[DOTNOT:%.*]] = icmp ugt i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT: br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], label [[CONT8:%.*]], !prof [[PROF14]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR: handler.out_of_bounds:
+// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB6:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR: cont8:
+// SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[DOTCOUNTED_BY_LOAD]] to i64
+// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nsw i64 [[COUNT]], [[IDXPROM]]
+// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = tail call i64 @llvm.smax.i64(i64 [[RESULT]], i64 0)
+// SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP1]]
+//
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -4294967295, 4294967296) i64 @test6(
+// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
+// NO-SANITIZE-WITH-ATTR-NEXT: entry:
+// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
+// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nsw i64 [[COUNT]], [[IDXPROM]]
+// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp sgt i64 [[RESULT]], -1
+// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[INDEX]], -1
+// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = and i1 [[TMP1]], [[TMP0]]
+// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
+// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP3]]
+//
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test6(
+// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITHOUT-ATTR-NEXT: entry:
+// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
+//
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test6(
+// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
+//
+size_t test6(struct annotated_sized_ptr *p, int index) {
+ return __bdos(&p->buf[index]);
+}
More information about the cfe-commits
mailing list