Hi, Tristan

Thanks for updating the patches.

On Wed, 23 Sep 2026 at 07:26, "Tristan Partin" <[email protected]> wrote:
> On Tue Sep 22, 2026 at 5:28 AM UTC, Peter Eisentraut wrote:
>> On 30.07.26 00:07, Tristan Partin wrote:
>>> The counted_by[0] compiler attribute is fairly new. It was added in GCC
>>> 15 and Clang 18. It has been used fairly extensively in the Linux
>>> kernel[0].
>>> 
>>> To summarize the benefits of the attribute:
>>> 
>>> - Runtime bounds checking with -DFORTIFY_SOURCE=3 and -fsanitize-bounds
>>> - Accurate reporting of __builtin_dynamic_object_size()
>>> 
>>> While we don't use __builtin_dynamic_object_size(), I think the runtime
>>> bounds checking improvements are easily worth the little bit of effort
>>> to add the attribute in various locations and review the code. I think
>>> it will improve things for buildfarm animals using ASan due to expanded
>>> coverage.
>>
>> I took a closer look at this.  There are several problems with the 
>> proposed patches.
>>
>> 1) In C++, both gcc and clang have __has_attribute(counted_by) return 1 
>> (true), but the compiler actually rejects the attribute with a warning. 
>> This is not immediately evident in your patch, but it would show up 
>> under cpluspluscheck and whenever we extend this attribute to header 
>> files that happen to get pulled in by C++ source files. 
>> (access/tupdesc.h is an obvious candidate.) Therefore, there needs to be 
>> some #ifndef __cplusplus somewhere.
>
> I would love to understand the rationale for returning 1 when the 
> compiler will just throw a warning anyway. Fixed.
>
>> 2) gcc 15 and clang 18 accept the counted_by attribute only for flexible 
>> array members, not for pointers members. (Using it on a pointer causes 
>> an error.) If you want to apply this to pointer members, as your patch 
>> does in buffile.c, you'd have to write a configure test. Or else 
>> restrict it to flexible array members for now.
>
> I like the idea of restricting it to flexible array members for now. 
> It'll make for an easier review. Maybe in a subsequent patch we can 
> raise the minimum compiler versions of using pg_attribute_counted_by() 
> to GCC 16 and Clang 21.
>
>> 3) The counted_by attribute requires that, when extending the counted 
>> array, the count field is increased before writing into the new element 
>> at the end. The code dealing with struct BufFile currently doesn't do 
>> that, and so your change in buffile.c fails under -fsanitize=bounds:
>>
>> ../src/backend/storage/file/buffile.c:919:3: runtime error: index 1 out 
>> of bounds for type 'File * __counted_by(numFiles)' (aka 'int *')
>>
>> (Reproduce with meson configure -Db_sanitize=bounds and meson test ... 
>> --suite regress.)
>>
>> The code needs to be carefully analyzed and adjusted to fix this. (The 
>> code for the tuplesort.c change appears to be ok.)
>
> Good catch. In the upcoming changes, I ran test suites with 
> -fsanitize=bounds, and found one place that needed a fix. Note that 
> changes to buffile.c are not currently in scope for this patchset since 
> it wasn't a flexible array member.
>
>> 4) Although the compilers are flexible with the placement, the most 
>> correct placement of the attribute is at the beginning of the 
>> declaration, like
>>
>> pg_attribute_counted_by(nTapes) TapeShare tapes[FLEXIBLE_ARRAY_MEMBER];
>>
>> (Note that the gcc documentation effectively writes it this way.)
>
> The second patch uses postfix notation, but subsequent patches enable 
> support for prefix notation. I'll let you be the judge of whether to 
> commit prefix or postfix. Commits 3 & 4 are genuine improvements, though 
> they do also enable prefix support.
>
>> Additionally, with this arrangement, we could also make use of the MSVC 
>> _Field_size_ annotation.
>
> This is good motivation.
>
>> 5) Minor: The counted_by attribute only takes a single argument, so the 
>> use of __VA_ARGS__ seems excessive.
>
> I think I just blindly copied surrounding macro code and forgot to 
> change it. Fixed in this new version.
>
>> 6) Minor: Awkward wording in comment: "This provides the compiler to 
>> improve ..." -> "enables the compiler ..."?
>
> Fixed.
>
>> Suggestion:
>> - Add C++ guard. (Maybe add annotation in access/tupdesc.h to test.)
>> - Skip use of the attribute on pointer members for now.
>> - Make sure cpluspluscheck and -fsanitize=bounds pass.
>> - Consider the cosmetic adjustments mentioned.
>
> Thanks for the review.
>

I tested it locally, and all tests passed.

I found some places that could use the new pg_attribute_counted_by() attribute
e.g., in heapam_xlog.h, xact.h, etc. Are those intentional omissions?

Was this an oversight, or is pg_attribute_counted_by not needed here?

Haven't checked everywhere yet.  If it is oversight, I'll check it later.

-- 
Regards,
Japin Li
ChengDu WenWu Information Technology Co., Ltd.

diff --git a/src/include/access/heapam_xlog.h b/src/include/access/heapam_xlog.h
index 3f79c389a90..33805f13afe 100644
--- a/src/include/access/heapam_xlog.h
+++ b/src/include/access/heapam_xlog.h
@@ -141,7 +141,7 @@ typedef struct xl_heap_truncate
 	Oid			dbId;
 	uint32		nrelids;
 	uint8		flags;
-	Oid			relids[FLEXIBLE_ARRAY_MEMBER];
+	pg_attribute_counted_by(nrelids) Oid			relids[FLEXIBLE_ARRAY_MEMBER];
 } xl_heap_truncate;
 
 #define SizeOfHeapTruncate	(offsetof(xl_heap_truncate, relids))
@@ -194,7 +194,7 @@ typedef struct xl_heap_multi_insert
 {
 	uint8		flags;
 	uint16		ntuples;
-	OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER];
+	pg_attribute_counted_by(ntuples) OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER];
 } xl_heap_multi_insert;
 
 #define SizeOfHeapMultiInsert	offsetof(xl_heap_multi_insert, offsets)
@@ -402,7 +402,7 @@ typedef struct xlhp_freeze_plan
 typedef struct xlhp_freeze_plans
 {
 	uint16		nplans;
-	xlhp_freeze_plan plans[FLEXIBLE_ARRAY_MEMBER];
+	pg_attribute_counted_by(nplans) xlhp_freeze_plan plans[FLEXIBLE_ARRAY_MEMBER];
 } xlhp_freeze_plans;
 
 /*
@@ -415,7 +415,7 @@ typedef struct xlhp_freeze_plans
 typedef struct xlhp_prune_items
 {
 	uint16		ntargets;
-	OffsetNumber data[FLEXIBLE_ARRAY_MEMBER];
+	pg_attribute_counted_by(ntargets) OffsetNumber data[FLEXIBLE_ARRAY_MEMBER];
 } xlhp_prune_items;
 
 
@@ -470,7 +470,7 @@ typedef struct xl_heap_inplace
 	Oid			tsId;			/* MyDatabaseTableSpace */
 	bool		relcacheInitFileInval;	/* invalidate relcache init files */
 	int			nmsgs;			/* number of shared inval msgs */
-	SharedInvalidationMessage msgs[FLEXIBLE_ARRAY_MEMBER];
+	pg_attribute_counted_by(nmsgs) SharedInvalidationMessage msgs[FLEXIBLE_ARRAY_MEMBER];
 } xl_heap_inplace;
 
 #define MinSizeOfHeapInplace	(offsetof(xl_heap_inplace, nmsgs) + sizeof(int))
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index a8cbdf247c8..1d94e38b7c3 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -220,7 +220,7 @@ typedef struct xl_xact_assignment
 {
 	TransactionId xtop;			/* assigned XID's top-level XID */
 	int			nsubxacts;		/* number of subtransaction XIDs */
-	TransactionId xsub[FLEXIBLE_ARRAY_MEMBER];	/* assigned subxids */
+	pg_attribute_counted_by(nsubxacts) TransactionId xsub[FLEXIBLE_ARRAY_MEMBER];	/* assigned subxids */
 } xl_xact_assignment;
 
 #define MinSizeOfXactAssignment offsetof(xl_xact_assignment, xsub)
@@ -262,14 +262,14 @@ typedef struct xl_xact_dbinfo
 typedef struct xl_xact_subxacts
 {
 	int			nsubxacts;		/* number of subtransaction XIDs */
-	TransactionId subxacts[FLEXIBLE_ARRAY_MEMBER];
+	pg_attribute_counted_by(nsubxacts) TransactionId subxacts[FLEXIBLE_ARRAY_MEMBER];
 } xl_xact_subxacts;
 #define MinSizeOfXactSubxacts offsetof(xl_xact_subxacts, subxacts)
 
 typedef struct xl_xact_relfilelocators
 {
 	int			nrels;			/* number of relations */
-	RelFileLocator xlocators[FLEXIBLE_ARRAY_MEMBER];
+	pg_attribute_counted_by(nrels) RelFileLocator xlocators[FLEXIBLE_ARRAY_MEMBER];
 } xl_xact_relfilelocators;
 #define MinSizeOfXactRelfileLocators offsetof(xl_xact_relfilelocators, xlocators)
 
@@ -296,14 +296,14 @@ typedef struct xl_xact_stats_item
 typedef struct xl_xact_stats_items
 {
 	int			nitems;
-	xl_xact_stats_item items[FLEXIBLE_ARRAY_MEMBER];
+	pg_attribute_counted_by(nitems) xl_xact_stats_item items[FLEXIBLE_ARRAY_MEMBER];
 } xl_xact_stats_items;
 #define MinSizeOfXactStatsItems offsetof(xl_xact_stats_items, items)
 
 typedef struct xl_xact_invals
 {
 	int			nmsgs;			/* number of shared inval msgs */
-	SharedInvalidationMessage msgs[FLEXIBLE_ARRAY_MEMBER];
+	pg_attribute_counted_by(nmsgs) SharedInvalidationMessage msgs[FLEXIBLE_ARRAY_MEMBER];
 } xl_xact_invals;
 #define MinSizeOfXactInvals offsetof(xl_xact_invals, msgs)
 

Reply via email to