Skip to content

Commit ce9cebc

Browse files
author
git apple-llvm automerger
committed
Merge commit '9a954c693573' from llvm.org/main into next
2 parents 4611142 + 9a954c6 commit ce9cebc

18 files changed

+770
-77
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ C Language Changes
132132
- ``structs``, ``unions``, and ``arrays`` that are const may now be used as
133133
constant expressions. This change is more consistent with the behavior of
134134
GCC.
135+
- Clang now supports the C-only attribute ``counted_by``. When applied to a
136+
struct's flexible array member, it points to the struct field that holds the
137+
number of elements in the flexible array member. This information can improve
138+
the results of the array bound sanitizer and the
139+
``__builtin_dynamic_object_size`` builtin.
135140

136141
C23 Feature Support
137142
^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/Decl.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4304,6 +4304,30 @@ class RecordDecl : public TagDecl {
43044304
return field_begin() == field_end();
43054305
}
43064306

4307+
FieldDecl *getLastField() {
4308+
FieldDecl *FD = nullptr;
4309+
for (FieldDecl *Field : fields())
4310+
FD = Field;
4311+
return FD;
4312+
}
4313+
const FieldDecl *getLastField() const {
4314+
return const_cast<RecordDecl *>(this)->getLastField();
4315+
}
4316+
4317+
template <typename Functor>
4318+
const FieldDecl *findFieldIf(Functor &Pred) const {
4319+
for (const Decl *D : decls()) {
4320+
if (const auto *FD = dyn_cast<FieldDecl>(D); FD && Pred(FD))
4321+
return FD;
4322+
4323+
if (const auto *RD = dyn_cast<RecordDecl>(D))
4324+
if (const FieldDecl *FD = RD->findFieldIf(Pred))
4325+
return FD;
4326+
}
4327+
4328+
return nullptr;
4329+
}
4330+
43074331
/// Note that the definition of this type is now complete.
43084332
virtual void completeDefinition();
43094333

clang/include/clang/AST/DeclBase.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "clang/AST/DeclarationName.h"
1919
#include "clang/Basic/IdentifierTable.h"
2020
#include "clang/Basic/LLVM.h"
21+
#include "clang/Basic/LangOptions.h"
2122
#include "clang/Basic/SourceLocation.h"
2223
#include "clang/Basic/Specifiers.h"
2324
#include "llvm/ADT/ArrayRef.h"
@@ -477,6 +478,15 @@ class alignas(8) Decl {
477478
// Return true if this is a FileContext Decl.
478479
bool isFileContextDecl() const;
479480

481+
/// Whether it resembles a flexible array member. This is a static member
482+
/// because we want to be able to call it with a nullptr. That allows us to
483+
/// perform non-Decl specific checks based on the object's type and strict
484+
/// flex array level.
485+
static bool isFlexibleArrayMemberLike(
486+
ASTContext &Context, const Decl *D, QualType Ty,
487+
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel,
488+
bool IgnoreTemplateOrMacroSubstitution);
489+
480490
ASTContext &getASTContext() const LLVM_READONLY;
481491

482492
/// Helper to get the language options from the ASTContext.

clang/include/clang/Basic/Attr.td

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4314,3 +4314,21 @@ def AvailableOnlyInDefaultEvalMethod : InheritableAttr {
43144314
let Subjects = SubjectList<[TypedefName], ErrorDiag>;
43154315
let Documentation = [Undocumented];
43164316
}
4317+
4318+
def CountedBy : InheritableAttr {
4319+
let Spellings = [Clang<"counted_by">];
4320+
let Subjects = SubjectList<[Field]>;
4321+
let Args = [IdentifierArgument<"CountedByField">];
4322+
let Documentation = [CountedByDocs];
4323+
let LangOpts = [COnly];
4324+
// FIXME: This is ugly. Let using a DeclArgument would be nice, but a Decl
4325+
// isn't yet available due to the fact that we're still parsing the
4326+
// structure. Maybe that code could be changed sometime in the future.
4327+
code AdditionalMembers = [{
4328+
private:
4329+
SourceRange CountedByFieldLoc;
4330+
public:
4331+
SourceRange getCountedByFieldLoc() const { return CountedByFieldLoc; }
4332+
void setCountedByFieldLoc(SourceRange Loc) { CountedByFieldLoc = Loc; }
4333+
}];
4334+
}

clang/include/clang/Basic/AttrDocs.td

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7494,3 +7494,69 @@ relative ordering of values is important. For example:
74947494
attribute, they default to the value ``65535``.
74957495
}];
74967496
}
7497+
7498+
def CountedByDocs : Documentation {
7499+
let Category = DocCatField;
7500+
let Content = [{
7501+
Clang supports the ``counted_by`` attribute on the flexible array member of a
7502+
structure in C. The argument for the attribute is the name of a field member in
7503+
the same structure holding the count of elements in the flexible array. This
7504+
information can be used to improve the results of the array bound sanitizer and
7505+
the ``__builtin_dynamic_object_size`` builtin.
7506+
7507+
For example, the following code:
7508+
7509+
.. code-block:: c
7510+
7511+
struct bar;
7512+
7513+
struct foo {
7514+
size_t count;
7515+
char other;
7516+
struct bar *array[] __attribute__((counted_by(count)));
7517+
};
7518+
7519+
specifies that the flexible array member ``array`` has the number of elements
7520+
allocated for it stored in ``count``. This establishes a relationship between
7521+
``array`` and ``count``. Specifically, ``p->array`` must have at least
7522+
``p->count`` number of elements available. It's the user's responsibility to
7523+
ensure that this relationship is maintained through changes to the structure.
7524+
7525+
In the following example, the allocated array erroneously has fewer elements
7526+
than what's specified by ``p->count``. This would result in an out-of-bounds
7527+
access not being detected.
7528+
7529+
.. code-block:: c
7530+
7531+
#define SIZE_INCR 42
7532+
7533+
struct foo *p;
7534+
7535+
void foo_alloc(size_t count) {
7536+
p = malloc(MAX(sizeof(struct foo),
7537+
offsetof(struct foo, array[0]) + count * sizeof(struct bar *)));
7538+
p->count = count + SIZE_INCR;
7539+
}
7540+
7541+
The next example updates ``p->count``, breaking the relationship requirement
7542+
that ``p->array`` must have at least ``p->count`` number of elements available:
7543+
7544+
.. code-block:: c
7545+
7546+
#define SIZE_INCR 42
7547+
7548+
struct foo *p;
7549+
7550+
void foo_alloc(size_t count) {
7551+
p = malloc(MAX(sizeof(struct foo),
7552+
offsetof(struct foo, array[0]) + count * sizeof(struct bar *)));
7553+
p->count = count;
7554+
}
7555+
7556+
void use_foo(int index) {
7557+
p->count += SIZE_INCR + 1; /* 'count' is now larger than the number of elements of 'array'. */
7558+
p->array[index] = 0; /* the sanitizer can't properly check if this is an out-of-bounds access. */
7559+
}
7560+
7561+
}];
7562+
}

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6455,6 +6455,21 @@ def warn_superclass_variable_sized_type_not_at_end : Warning<
64556455
"field %0 can overwrite instance variable %1 with variable sized type %2"
64566456
" in superclass %3">, InGroup<ObjCFlexibleArray>;
64576457

6458+
def err_counted_by_attr_not_on_flexible_array_member : Error<
6459+
"'counted_by' only applies to flexible array members">;
6460+
def err_flexible_array_counted_by_attr_field_not_found : Error<
6461+
"field %0 in 'counted_by' not found">;
6462+
def err_flexible_array_counted_by_attr_field_not_found_suggest : Error<
6463+
"field %0 in 'counted_by' not found; did you mean %1?">;
6464+
def err_flexible_array_counted_by_attr_field_not_found_in_struct : Error<
6465+
"field %0 in 'counted_by' is not found in struct">;
6466+
def err_flexible_array_counted_by_attr_refers_to_self : Error<
6467+
"field %0 in 'counted_by' cannot refer to the flexible array">;
6468+
def err_flexible_array_counted_by_attr_field_not_integer : Error<
6469+
"field %0 in 'counted_by' is not a non-boolean integer type">;
6470+
def note_flexible_array_counted_by_attr_field : Note<
6471+
"field %0 declared here">;
6472+
64586473
let CategoryName = "ARC Semantic Issue" in {
64596474

64606475
// ARC-mode diagnostics.

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4823,6 +4823,8 @@ class Sema final {
48234823
bool CheckAlwaysInlineAttr(const Stmt *OrigSt, const Stmt *CurSt,
48244824
const AttributeCommonInfo &A);
48254825

4826+
bool CheckCountedByAttr(Scope *Scope, const FieldDecl *FD);
4827+
48264828
/// Adjust the calling convention of a method to be the ABI default if it
48274829
/// wasn't specified explicitly. This handles method types formed from
48284830
/// function type typedefs and typename template arguments.

clang/lib/AST/ASTImporter.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8977,6 +8977,10 @@ class AttrImporter {
89778977
public:
89788978
AttrImporter(ASTImporter &I) : Importer(I), NImporter(I) {}
89798979

8980+
// Useful for accessing the imported attribute.
8981+
template <typename T> T *castAttrAs() { return cast<T>(ToAttr); }
8982+
template <typename T> const T *castAttrAs() const { return cast<T>(ToAttr); }
8983+
89808984
// Create an "importer" for an attribute parameter.
89818985
// Result of the 'value()' of that object is to be passed to the function
89828986
// 'importAttr', in the order that is expected by the attribute class.
@@ -9183,6 +9187,15 @@ Expected<Attr *> ASTImporter::Import(const Attr *FromAttr) {
91839187
From->args_size());
91849188
break;
91859189
}
9190+
case attr::CountedBy: {
9191+
AI.cloneAttr(FromAttr);
9192+
const auto *CBA = cast<CountedByAttr>(FromAttr);
9193+
Expected<SourceRange> SR = Import(CBA->getCountedByFieldLoc()).get();
9194+
if (!SR)
9195+
return SR.takeError();
9196+
AI.castAttrAs<CountedByAttr>()->setCountedByFieldLoc(SR.get());
9197+
break;
9198+
}
91869199

91879200
default: {
91889201
// The default branch works for attributes that have no arguments to import.

clang/lib/AST/DeclBase.cpp

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
#include "clang/AST/Type.h"
3030
#include "clang/Basic/IdentifierTable.h"
3131
#include "clang/Basic/LLVM.h"
32-
#include "clang/Basic/LangOptions.h"
3332
#include "clang/Basic/Module.h"
3433
#include "clang/Basic/ObjCRuntime.h"
3534
#include "clang/Basic/PartialDiagnostic.h"
@@ -411,6 +410,81 @@ bool Decl::isFileContextDecl() const {
411410
return DC && DC->isFileContext();
412411
}
413412

413+
bool Decl::isFlexibleArrayMemberLike(
414+
ASTContext &Ctx, const Decl *D, QualType Ty,
415+
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel,
416+
bool IgnoreTemplateOrMacroSubstitution) {
417+
// For compatibility with existing code, we treat arrays of length 0 or
418+
// 1 as flexible array members.
419+
const auto *CAT = Ctx.getAsConstantArrayType(Ty);
420+
if (CAT) {
421+
using FAMKind = LangOptions::StrictFlexArraysLevelKind;
422+
423+
llvm::APInt Size = CAT->getSize();
424+
FAMKind StrictFlexArraysLevel =
425+
Ctx.getLangOpts().getStrictFlexArraysLevel();
426+
427+
if (StrictFlexArraysLevel == FAMKind::IncompleteOnly)
428+
return false;
429+
430+
// GCC extension, only allowed to represent a FAM.
431+
if (Size.isZero())
432+
return true;
433+
434+
if (StrictFlexArraysLevel == FAMKind::ZeroOrIncomplete && Size.uge(1))
435+
return false;
436+
437+
if (StrictFlexArraysLevel == FAMKind::OneZeroOrIncomplete && Size.uge(2))
438+
return false;
439+
} else if (!Ctx.getAsIncompleteArrayType(Ty)) {
440+
return false;
441+
}
442+
443+
if (const auto *OID = dyn_cast_if_present<ObjCIvarDecl>(D))
444+
return OID->getNextIvar() == nullptr;
445+
446+
const auto *FD = dyn_cast_if_present<FieldDecl>(D);
447+
if (!FD)
448+
return false;
449+
450+
if (CAT) {
451+
// GCC treats an array memeber of a union as an FAM if the size is one or
452+
// zero.
453+
llvm::APInt Size = CAT->getSize();
454+
if (FD->getParent()->isUnion() && (Size.isZero() || Size.isOne()))
455+
return true;
456+
}
457+
458+
// Don't consider sizes resulting from macro expansions or template argument
459+
// substitution to form C89 tail-padded arrays.
460+
if (IgnoreTemplateOrMacroSubstitution) {
461+
TypeSourceInfo *TInfo = FD->getTypeSourceInfo();
462+
while (TInfo) {
463+
TypeLoc TL = TInfo->getTypeLoc();
464+
465+
// Look through typedefs.
466+
if (TypedefTypeLoc TTL = TL.getAsAdjusted<TypedefTypeLoc>()) {
467+
const TypedefNameDecl *TDL = TTL.getTypedefNameDecl();
468+
TInfo = TDL->getTypeSourceInfo();
469+
continue;
470+
}
471+
472+
if (auto CTL = TL.getAs<ConstantArrayTypeLoc>()) {
473+
const Expr *SizeExpr = dyn_cast<IntegerLiteral>(CTL.getSizeExpr());
474+
if (!SizeExpr || SizeExpr->getExprLoc().isMacroID())
475+
return false;
476+
}
477+
478+
break;
479+
}
480+
}
481+
482+
// Test that the field is the last in the structure.
483+
RecordDecl::field_iterator FI(
484+
DeclContext::decl_iterator(const_cast<FieldDecl *>(FD)));
485+
return ++FI == FD->getParent()->field_end();
486+
}
487+
414488
TranslationUnitDecl *Decl::getTranslationUnitDecl() {
415489
if (auto *TUD = dyn_cast<TranslationUnitDecl>(this))
416490
return TUD;

0 commit comments

Comments
 (0)