In my ventures to apply more compiler attributes, I came to alloc_size.
This is to annotate memory allocation functions so that the compiler can
know the size of the allocated memory and so some cross-checking based
on that. This is all pretty straightforward and I think we should do it.
Andres had previously reported some issues he encountered in [0]. (That
message didn't go into details on what attributes or compiler options
were used, but it certainly looked very closely related.)
The issue is that under-allocating a struct is bad. With this
attribute, the compiler flags this, because it looks like a mistake.
For uses where this is legitimate, you'd normally use a struct with a
trailing flexible-array member, and we mostly make use of that. But
there are a couple of cases where we assign to a struct pointer less
memory than what the struct expects. In some cases, this is done with a
union where we only allocate enough memory for one branch of the union,
which seems like an intuitive thing to do but is actually also bad.
In the attached patches I have some fixes and/or workarounds for the
issues that I found.
Note that as documented this attribute and the associated warning
options are for diagnostics only, they don't change compilation. So in
that sense, once all warnings are fixed, this shouldn't create any more
hidden issues.
- varattrib_4b is a union of a normal varlena with a 4-byte header and 0
or more payload bytes, and a compressed format with a 4-byte header, a
4-byte "tcinfo", followed by payload data. The typical coding pattern
val = palloc(VARHDRSZ + len);
SET_VARSIZE(val, VARHDRSZ + len);
could get flagged by -Warray-bounds because the allocation could be too
short to cover both branches of the union. In this case, I think we
don't actually need the union, so I'm replacing this with two separate
structs.
- TRGM in contrib/pg_trgm/ is an instance of what [0] talks about: It
has a trailing flexible array member, but it also has trailing padding,
so allocating palloc(TRGMHDRSIZE) causes a warning, because it's less
than sizeof(TRGM). In my patch I work around this by allocating like
palloc(Max(TRGMHDRSIZE, sizeof(TRGM))).
- There is a bit of a maze of different structs and unions that underlie
the numeric type. The allocation (Numeric) palloc(NUMERIC_HDRSZ_SHORT)
gets flagged by the compiler. In my patch I change this to (Numeric)
palloc(sizeof(struct NumericData)), which allocates a bit more but makes
the sizes and types match. Maybe some more judicious reshuffling of the
underlying data structures could avoid this, or maybe it's okay like this.
- As of commit 6a3da13d376 from this week, FormData_pg_attribute has 4
bytes of trailing padding, and so sizeof(FormData_pg_attribute) is now
different from ATTRIBUTE_FIXED_PART_SIZE. This causes a warning in one
place. We had a discussion about this some time ago [1], where the
opinions diverged between like, it's important to keep this distinction
in case we ever have padding bytes, and, it's already incorrect and
would be even more incorrect if we had padding bytes. ;-) Well, we're
here now, so perhaps this should be revisited.
This is all that I found. [0] lists a lot more sources of potential
problems, but apparently they are not flagged as they are currently used.
[0]:
https://www.postgresql.org/message-id/flat/axxbw7ku4w24hwe6uvkm4vdufo4zr4k5344m6naahkpsoafag5%40llovwgptzbgq
[1]:
https://www.postgresql.org/message-id/flat/7c7d41c1-fd5d-f9a5-afd5-f00168d91fe2%402ndquadrant.comFrom 8feb1a41041ad53385be5b9e07581d15d5b0dd31 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Fri, 13 Nov 2026 11:58:18 +0100
Subject: [PATCH 1/5] Apply alloc_size attribute to memory allocation functions
Add pg_attribute_alloc_size() and use it on palloc, pg_malloc, and
related allocation functions. This tells the compiler the size of the
returned object, which enables __builtin_object_size() and
__builtin_dynamic_object_size() on such memory (and thus
_FORTIFY_SOURCE and similar checks) as well as compile-time bounds
warnings such as -Warray-bounds and -Wstringop-overflow.
For now we only adjust the general-purpose allocation functions in
palloc.h and fe_memutils.h. There are a number of other more
narrow-purpose allocation functions that could be similarly annotated.
To avoid some new warnings, add pg_assume() at a few places where an
int count is passed to palloc_array() and the compiler cannot tell
that it is not negative.
---
src/backend/commands/prepare.c | 1 +
src/backend/statistics/mcv.c | 1 +
src/backend/storage/buffer/bufmgr.c | 2 ++
src/include/c.h | 13 ++++++++
src/include/common/fe_memutils.h | 32 ++++++++++----------
src/include/utils/palloc.h | 46 ++++++++++++++---------------
6 files changed, 56 insertions(+), 39 deletions(-)
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 876aad2100a..8f54559ca8a 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -752,6 +752,7 @@ build_regtype_array(Oid *param_types, int num_params)
ArrayType *result;
int i;
+ pg_assume(num_params >= 0);
tmp_ary = palloc_array(Datum, num_params);
for (i = 0; i < num_params; i++)
diff --git a/src/backend/statistics/mcv.c b/src/backend/statistics/mcv.c
index 10a91c97aeb..6b27b45029d 100644
--- a/src/backend/statistics/mcv.c
+++ b/src/backend/statistics/mcv.c
@@ -2283,6 +2283,7 @@ statext_mcv_import(int elevel, int numattrs,
* VacAttrStats records, but only a few fields within those records have
* to be filled out.
*/
+ pg_assume(numattrs >= 0);
vastats = (VacAttrStats **) palloc0_array(VacAttrStats *, numattrs);
for (int i = 0; i < numattrs; i++)
diff --git a/src/backend/storage/buffer/bufmgr.c
b/src/backend/storage/buffer/bufmgr.c
index c17fc3590f0..8af9a8c1bce 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -4939,6 +4939,8 @@ DropRelationsAllBuffers(SMgrRelation *smgr_reln, int
nlocators)
rels[n++] = smgr_reln[i];
}
+ pg_assume(n >= 0);
+
/*
* If there are no non-local relations, then we're done. Release the
* memory and return.
diff --git a/src/include/c.h b/src/include/c.h
index 4bbb0071289..0b9afcd7d3c 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -293,6 +293,19 @@ extern "C++"
#define pg_attribute_no_sanitize_function()
#endif
+/*
+ * pg_attribute_alloc_size means the function returns a pointer to an object
+ * whose size is given by the listed argument, or by the product of the two
+ * listed arguments. This helps the compiler check object bounds.
+ *
+ *
https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-alloc_005fsize
+ */
+#if __has_attribute (alloc_size)
+#define pg_attribute_alloc_size(...) __attribute__((alloc_size(__VA_ARGS__)))
+#else
+#define pg_attribute_alloc_size(...)
+#endif
+
/*
* pg_attribute_nonnull means the compiler should warn if the function is
* called with the listed arguments set to NULL. If no arguments are
diff --git a/src/include/common/fe_memutils.h b/src/include/common/fe_memutils.h
index 0db64ab4389..e701e38bec5 100644
--- a/src/include/common/fe_memutils.h
+++ b/src/include/common/fe_memutils.h
@@ -35,10 +35,10 @@
* (except pg_malloc_extended with MCXT_ALLOC_NO_OOM)
*/
pg_attribute_returns_nonnull extern char *pg_strdup(const char *in);
-pg_attribute_returns_nonnull extern void *pg_malloc(size_t size);
-pg_attribute_returns_nonnull extern void *pg_malloc0(size_t size);
-extern void *pg_malloc_extended(size_t size, int flags);
-pg_attribute_returns_nonnull extern void *pg_realloc(void *ptr, size_t size);
+pg_attribute_alloc_size(1) pg_attribute_returns_nonnull extern void
*pg_malloc(size_t size);
+pg_attribute_alloc_size(1) pg_attribute_returns_nonnull extern void
*pg_malloc0(size_t size);
+pg_attribute_alloc_size(1) extern void *pg_malloc_extended(size_t size, int
flags);
+pg_attribute_alloc_size(2) pg_attribute_returns_nonnull extern void
*pg_realloc(void *ptr, size_t size);
extern void pg_free(void *ptr);
/*
@@ -46,10 +46,10 @@ extern void pg_free(void *ptr);
*/
extern Size add_size(Size s1, Size s2);
extern Size mul_size(Size s1, Size s2);
-pg_attribute_returns_nonnull extern void *pg_malloc_mul(Size s1, Size s2);
-pg_attribute_returns_nonnull extern void *pg_malloc0_mul(Size s1, Size s2);
-extern void *pg_malloc_mul_extended(Size s1, Size s2, int flags);
-pg_attribute_returns_nonnull extern void *pg_realloc_mul(void *p, Size s1,
Size s2);
+pg_attribute_alloc_size(1, 2) pg_attribute_returns_nonnull extern void
*pg_malloc_mul(Size s1, Size s2);
+pg_attribute_alloc_size(1, 2) pg_attribute_returns_nonnull extern void
*pg_malloc0_mul(Size s1, Size s2);
+pg_attribute_alloc_size(1, 2) extern void *pg_malloc_mul_extended(Size s1,
Size s2, int flags);
+pg_attribute_alloc_size(2, 3) pg_attribute_returns_nonnull extern void
*pg_realloc_mul(void *p, Size s1, Size s2);
/*
* Variants with easier notation and more type safety
@@ -77,15 +77,15 @@ pg_attribute_returns_nonnull extern void
*pg_realloc_mul(void *p, Size s1, Size
/* Equivalent functions, deliberately named the same as backend functions */
pg_attribute_returns_nonnull extern char *pstrdup(const char *in);
pg_attribute_returns_nonnull extern char *pnstrdup(const char *in, Size size);
-pg_attribute_returns_nonnull extern void *palloc(Size size);
-pg_attribute_returns_nonnull extern void *palloc0(Size size);
-extern void *palloc_extended(Size size, int flags);
-pg_attribute_returns_nonnull extern void *repalloc(void *pointer, Size size);
+pg_attribute_alloc_size(1) pg_attribute_returns_nonnull extern void
*palloc(Size size);
+pg_attribute_alloc_size(1) pg_attribute_returns_nonnull extern void
*palloc0(Size size);
+pg_attribute_alloc_size(1) extern void *palloc_extended(Size size, int flags);
+pg_attribute_alloc_size(2) pg_attribute_returns_nonnull extern void
*repalloc(void *pointer, Size size);
extern void pfree(void *pointer);
-pg_attribute_returns_nonnull extern void *palloc_mul(Size s1, Size s2);
-pg_attribute_returns_nonnull extern void *palloc0_mul(Size s1, Size s2);
-extern void *palloc_mul_extended(Size s1, Size s2, int flags);
-pg_attribute_returns_nonnull extern void *repalloc_mul(void *p, Size s1, Size
s2);
+pg_attribute_alloc_size(1, 2) pg_attribute_returns_nonnull extern void
*palloc_mul(Size s1, Size s2);
+pg_attribute_alloc_size(1, 2) pg_attribute_returns_nonnull extern void
*palloc0_mul(Size s1, Size s2);
+pg_attribute_alloc_size(1, 2) extern void *palloc_mul_extended(Size s1, Size
s2, int flags);
+pg_attribute_alloc_size(2, 3) pg_attribute_returns_nonnull extern void
*repalloc_mul(void *p, Size s1, Size s2);
#define palloc_object(type) ((type *) palloc(sizeof(type)))
#define palloc0_object(type) ((type *) palloc0(sizeof(type)))
diff --git a/src/include/utils/palloc.h b/src/include/utils/palloc.h
index 0c7452fe30d..e8a8eefd9c4 100644
--- a/src/include/utils/palloc.h
+++ b/src/include/utils/palloc.h
@@ -68,21 +68,21 @@ extern PGDLLIMPORT MemoryContext CurrentMemoryContext;
/*
* Fundamental memory-allocation operations (more are in utils/memutils.h)
*/
-pg_attribute_returns_nonnull extern void *MemoryContextAlloc(MemoryContext
context, Size size);
-pg_attribute_returns_nonnull extern void *MemoryContextAllocZero(MemoryContext
context, Size size);
-extern void *MemoryContextAllocExtended(MemoryContext context,
-
Size size, int flags);
-extern void *MemoryContextAllocAligned(MemoryContext context,
- Size
size, Size alignto, int flags);
-
-pg_attribute_returns_nonnull extern void *palloc(Size size);
-pg_attribute_returns_nonnull extern void *palloc0(Size size);
-extern void *palloc_extended(Size size, int flags);
-extern void *palloc_aligned(Size size, Size alignto, int flags);
-pg_nodiscard pg_attribute_returns_nonnull extern void *repalloc(void *pointer,
Size size);
-pg_nodiscard extern void *repalloc_extended(void *pointer,
-
Size size, int flags);
-pg_nodiscard pg_attribute_returns_nonnull extern void *repalloc0(void
*pointer, Size oldsize, Size size);
+pg_attribute_alloc_size(2) pg_attribute_returns_nonnull extern void
*MemoryContextAlloc(MemoryContext context, Size size);
+pg_attribute_alloc_size(2) pg_attribute_returns_nonnull extern void
*MemoryContextAllocZero(MemoryContext context, Size size);
+pg_attribute_alloc_size(2) extern void
*MemoryContextAllocExtended(MemoryContext context,
+
Size size, int flags);
+pg_attribute_alloc_size(2) extern void
*MemoryContextAllocAligned(MemoryContext context,
+
Size size, Size alignto, int
flags);
+
+pg_attribute_alloc_size(1) pg_attribute_returns_nonnull extern void
*palloc(Size size);
+pg_attribute_alloc_size(1) pg_attribute_returns_nonnull extern void
*palloc0(Size size);
+pg_attribute_alloc_size(1) extern void *palloc_extended(Size size, int flags);
+pg_attribute_alloc_size(1) extern void *palloc_aligned(Size size, Size
alignto, int flags);
+pg_nodiscard pg_attribute_alloc_size(2) pg_attribute_returns_nonnull extern
void *repalloc(void *pointer, Size size);
+pg_nodiscard pg_attribute_alloc_size(2) extern void *repalloc_extended(void
*pointer,
+
Size size, int
flags);
+pg_nodiscard pg_attribute_alloc_size(3) pg_attribute_returns_nonnull extern
void *repalloc0(void *pointer, Size oldsize, Size size);
extern void pfree(void *pointer);
/*
@@ -90,12 +90,12 @@ extern void pfree(void *pointer);
*/
extern Size add_size(Size s1, Size s2);
extern Size mul_size(Size s1, Size s2);
-pg_attribute_returns_nonnull extern void *palloc_mul(Size s1, Size s2);
-pg_attribute_returns_nonnull extern void *palloc0_mul(Size s1, Size s2);
-extern void *palloc_mul_extended(Size s1, Size s2, int flags);
-pg_nodiscard pg_attribute_returns_nonnull extern void *repalloc_mul(void *p,
Size s1, Size s2);
-pg_nodiscard extern void *repalloc_mul_extended(void *p, Size s1, Size s2,
-
int flags);
+pg_attribute_alloc_size(1, 2) pg_attribute_returns_nonnull extern void
*palloc_mul(Size s1, Size s2);
+pg_attribute_alloc_size(1, 2) pg_attribute_returns_nonnull extern void
*palloc0_mul(Size s1, Size s2);
+pg_attribute_alloc_size(1, 2) extern void *palloc_mul_extended(Size s1, Size
s2, int flags);
+pg_nodiscard pg_attribute_alloc_size(2, 3) pg_attribute_returns_nonnull extern
void *repalloc_mul(void *p, Size s1, Size s2);
+pg_nodiscard pg_attribute_alloc_size(2, 3) extern void
*repalloc_mul_extended(void *p, Size s1, Size s2,
+
int
flags);
/*
* Variants with easier notation and more type safety
@@ -123,8 +123,8 @@ pg_nodiscard extern void *repalloc_mul_extended(void *p,
Size s1, Size s2,
#define repalloc_array_extended(pointer, type, count, flags) ((type *)
repalloc_mul_extended(pointer, sizeof(type), count, flags))
/* Higher-limit allocators. */
-pg_attribute_returns_nonnull extern void *MemoryContextAllocHuge(MemoryContext
context, Size size);
-pg_nodiscard pg_attribute_returns_nonnull extern void *repalloc_huge(void
*pointer, Size size);
+pg_attribute_alloc_size(2) pg_attribute_returns_nonnull extern void
*MemoryContextAllocHuge(MemoryContext context, Size size);
+pg_nodiscard pg_attribute_alloc_size(2) pg_attribute_returns_nonnull extern
void *repalloc_huge(void *pointer, Size size);
/*
* Although this header file is nominally backend-only, certain frontend
--
2.56.0
From 8fd3726b98fcf76f5daecd609b794e31f6947182 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Fri, 13 Nov 2026 11:58:18 +0100
Subject: [PATCH 2/5] Fixes for alloc_size attribute [varatt]
varattrib_4b was a union of a normal varlena with a 4-byte header and
0 or more payload bytes, and a compressed format with a 4-byte header,
a 4-byte "tcinfo", followed by payload data. The typical coding
pattern
val = palloc(VARHDRSZ + len);
SET_VARSIZE(val, VARHDRSZ + len);
could get flagged by gcc -Warray-bounds because the allocation could
be too short to cover both branches of the union, which is invalid C.
We don't actually need the union; no code makes real use of it.
Instead, create two separate structs varattrib_4b_normal and
varattrib_4b_compressed and make use of the appropriate one of these
in each place varattrib_4b was used. This avoids the warnings from
SET_VARSIZE() and SET_VARSIZE_COMPRESSED().
---
contrib/amcheck/verify_heapam.c | 2 +-
src/include/utils/expandeddatum.h | 2 +-
src/include/varatt.h | 55 +++++++++++++++++--------------
src/tools/pgindent/typedefs.list | 3 +-
4 files changed, 34 insertions(+), 28 deletions(-)
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index ea1ad402ea2..86219c6ca68 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -1637,7 +1637,7 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext
*ctx,
else
{
/* should never happen */
- uint32 header = ((varattrib_4b *)
chunk)->va_4byte.va_header;
+ uint32 header = ((varattrib_4b_normal *)
chunk)->va_header;
report_toast_corruption(ctx, ta,
psprintf("toast
value %" PRIu64 " chunk %d has invalid varlena header %0x",
diff --git a/src/include/utils/expandeddatum.h
b/src/include/utils/expandeddatum.h
index 7c2d5bbc46c..f2b151dfefe 100644
--- a/src/include/utils/expandeddatum.h
+++ b/src/include/utils/expandeddatum.h
@@ -128,7 +128,7 @@ struct ExpandedObjectHeader
*/
#define EOH_HEADER_MAGIC (-1)
#define VARATT_IS_EXPANDED_HEADER(PTR) \
- (((varattrib_4b *) (PTR))->va_4byte.va_header == (uint32)
EOH_HEADER_MAGIC)
+ (((varattrib_4b_normal *) (PTR))->va_header == (uint32)
EOH_HEADER_MAGIC)
/*
* Generic support functions for expanded objects.
diff --git a/src/include/varatt.h b/src/include/varatt.h
index c22575a3624..df359d1e30d 100644
--- a/src/include/varatt.h
+++ b/src/include/varatt.h
@@ -170,21 +170,26 @@ VARTAG_SIZE(vartag_external tag)
* compiler might otherwise think it could generate code that assumes
* alignment while touching fields of a 1-byte-header varlena.
*/
-typedef union
+
+/*
+ * Normal varlena (4-byte length)
+ */
+typedef struct
{
- struct /* Normal varlena
(4-byte length) */
- {
- uint32 va_header;
- char va_data[FLEXIBLE_ARRAY_MEMBER];
- } va_4byte;
- struct /* Compressed-in-line
format */
- {
- uint32 va_header;
- uint32 va_tcinfo; /* Original data size (excludes
header) and
+ uint32 va_header;
+ char va_data[FLEXIBLE_ARRAY_MEMBER];
+} varattrib_4b_normal;
+
+/*
+ * Compressed-in-line format
+ */
+typedef struct
+{
+ uint32 va_header;
+ uint32 va_tcinfo; /* Original data size (excludes
header) and
* compression
method; see va_extinfo */
- char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed
data */
- } va_compressed;
-} varattrib_4b;
+ char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
+} varattrib_4b_compressed;
typedef struct
{
@@ -254,16 +259,16 @@ typedef struct
/* VARSIZE_4B() should only be used on known-aligned data */
#define VARSIZE_4B(PTR) \
- (((const varattrib_4b *) (PTR))->va_4byte.va_header & 0x3FFFFFFF)
+ (((const varattrib_4b_normal *) (PTR))->va_header & 0x3FFFFFFF)
#define VARSIZE_1B(PTR) \
(((const varattrib_1b *) (PTR))->va_header & 0x7F)
#define VARTAG_1B_E(PTR) \
((vartag_external) ((const varattrib_1b_e *) (PTR))->va_tag)
#define SET_VARSIZE_4B(PTR,len) \
- (((varattrib_4b *) (PTR))->va_4byte.va_header = (len) & 0x3FFFFFFF)
+ (((varattrib_4b_normal *) (PTR))->va_header = (len) & 0x3FFFFFFF)
#define SET_VARSIZE_4B_C(PTR,len) \
- (((varattrib_4b *) (PTR))->va_4byte.va_header = ((len) & 0x3FFFFFFF) |
0x40000000)
+ (((varattrib_4b_normal *) (PTR))->va_4byte.va_header = ((len) &
0x3FFFFFFF) | 0x40000000)
#define SET_VARSIZE_1B(PTR,len) \
(((varattrib_1b *) (PTR))->va_header = (len) | 0x80)
#define SET_VARTAG_1B_E(PTR,tag) \
@@ -287,16 +292,16 @@ typedef struct
/* VARSIZE_4B() should only be used on known-aligned data */
#define VARSIZE_4B(PTR) \
- ((((const varattrib_4b *) (PTR))->va_4byte.va_header >> 2) & 0x3FFFFFFF)
+ ((((const varattrib_4b_normal *) (PTR))->va_header >> 2) & 0x3FFFFFFF)
#define VARSIZE_1B(PTR) \
((((const varattrib_1b *) (PTR))->va_header >> 1) & 0x7F)
#define VARTAG_1B_E(PTR) \
((vartag_external) ((const varattrib_1b_e *) (PTR))->va_tag)
#define SET_VARSIZE_4B(PTR,len) \
- (((varattrib_4b *) (PTR))->va_4byte.va_header = (((uint32) (len)) << 2))
+ (((varattrib_4b_normal *) (PTR))->va_header = (((uint32) (len)) << 2))
#define SET_VARSIZE_4B_C(PTR,len) \
- (((varattrib_4b *) (PTR))->va_4byte.va_header = (((uint32) (len)) << 2)
| 0x02)
+ (((varattrib_4b_normal *) (PTR))->va_header = (((uint32) (len)) << 2) |
0x02)
#define SET_VARSIZE_1B(PTR,len) \
(((varattrib_1b *) (PTR))->va_header = (((uint8) (len)) << 1) | 0x01)
#define SET_VARTAG_1B_E(PTR,tag) \
@@ -305,8 +310,8 @@ typedef struct
#endif /* WORDS_BIGENDIAN */
-#define VARDATA_4B(PTR) (((varattrib_4b *)
(PTR))->va_4byte.va_data)
-#define VARDATA_4B_C(PTR) (((varattrib_4b *)
(PTR))->va_compressed.va_data)
+#define VARDATA_4B(PTR) (((varattrib_4b_normal *)
(PTR))->va_data)
+#define VARDATA_4B_C(PTR) (((varattrib_4b_compressed *) (PTR))->va_data)
#define VARDATA_1B(PTR) (((varattrib_1b *) (PTR))->va_data)
#define VARDATA_1B_E(PTR) (((varattrib_1b_e *) (PTR))->va_data)
@@ -321,7 +326,7 @@ typedef struct
*/
#define VARHDRSZ_EXTERNAL offsetof(varattrib_1b_e, va_data)
-#define VARHDRSZ_COMPRESSED offsetof(varattrib_4b,
va_compressed.va_data)
+#define VARHDRSZ_COMPRESSED offsetof(varattrib_4b_compressed,
va_data)
#define VARHDRSZ_SHORT offsetof(varattrib_1b, va_data)
#define VARATT_SHORT_MAX 0x7F
@@ -544,14 +549,14 @@ VARDATA_ANY(const void *PTR)
static inline Size
VARDATA_COMPRESSED_GET_EXTSIZE(const void *PTR)
{
- return ((const varattrib_4b *) PTR)->va_compressed.va_tcinfo &
VARLENA_EXTSIZE_MASK;
+ return ((const varattrib_4b_compressed *) PTR)->va_tcinfo &
VARLENA_EXTSIZE_MASK;
}
/* Compression method of a compressed-in-line varlena datum */
static inline uint32
VARDATA_COMPRESSED_GET_COMPRESS_METHOD(const void *PTR)
{
- return ((const varattrib_4b *) PTR)->va_compressed.va_tcinfo >>
VARLENA_EXTSIZE_BITS;
+ return ((const varattrib_4b_compressed *) PTR)->va_tcinfo >>
VARLENA_EXTSIZE_BITS;
}
/*
@@ -565,7 +570,7 @@ VARDATA_COMPRESSED_SET_TCINFO(void *PTR, uint32 extsize,
uint32 cmethod)
Assert(extsize > 0 && extsize <= VARLENA_EXTSIZE_MASK);
Assert(cmethod < (1U << (32 - VARLENA_EXTSIZE_BITS)));
- ((varattrib_4b *) PTR)->va_compressed.va_tcinfo =
+ ((varattrib_4b_compressed *) PTR)->va_tcinfo =
extsize | (cmethod << VARLENA_EXTSIZE_BITS);
}
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index f10eb469266..b4a06eea230 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -4423,7 +4423,8 @@ varatt_external_oid8
varatt_indirect
varattrib_1b
varattrib_1b_e
-varattrib_4b
+varattrib_4b_compressed
+varattrib_4b_normal
varlena
vartag_external
vbits
--
2.56.0
From c1607b0a0724f8970c72fabe349773d9ab737a7f Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Fri, 13 Nov 2026 11:58:18 +0100
Subject: [PATCH 3/5] Fixes for alloc_size attribute [pg_trgm]
Header-only pg_trgm values are now allocated with at least the size of
the struct, to avoid -Warray-bounds warnings.
---
contrib/pg_trgm/trgm.h | 5 +++++
contrib/pg_trgm/trgm_gist.c | 5 ++++-
contrib/pg_trgm/trgm_op.c | 3 ++-
3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/contrib/pg_trgm/trgm.h b/contrib/pg_trgm/trgm.h
index ca23aad4dd9..7728aea84d0 100644
--- a/contrib/pg_trgm/trgm.h
+++ b/contrib/pg_trgm/trgm.h
@@ -55,6 +55,11 @@ extern int (*CMPTRGM) (const void *a, const void *b);
#define ISWILDCARDCHAR(x) (*(x) == '_' || *(x) == '%') /* Wildcard
* meta-character */
+/*
+ * Note: This structure has trailing padding. When allocating a header-only
+ * value, at least sizeof(TRGM) must be allocated, to avoid -Warray-bounds
+ * compiler warnings.
+ */
typedef struct
{
int32 vl_len_; /* varlena header (do not touch
directly!) */
diff --git a/contrib/pg_trgm/trgm_gist.c b/contrib/pg_trgm/trgm_gist.c
index dfa2f968d77..85c7da37125 100644
--- a/contrib/pg_trgm/trgm_gist.c
+++ b/contrib/pg_trgm/trgm_gist.c
@@ -78,7 +78,10 @@ gtrgm_alloc(bool isalltrue, int siglen, BITVECP sign)
{
int flag = SIGNKEY | (isalltrue ? ALLISTRUE : 0);
int size = CALCGTSIZE(flag, siglen);
- TRGM *res = palloc(size);
+ TRGM *res;
+
+ /* allocate at least sizeof(TRGM), to cover padding */
+ res = palloc(Max(size, sizeof(TRGM)));
SET_VARSIZE(res, size);
res->flag = flag;
diff --git a/contrib/pg_trgm/trgm_op.c b/contrib/pg_trgm/trgm_op.c
index 22bcc3c3361..2655257c2ad 100644
--- a/contrib/pg_trgm/trgm_op.c
+++ b/contrib/pg_trgm/trgm_op.c
@@ -1097,7 +1097,8 @@ generate_wildcard_trgm(const char *str, int slen)
if (slen + LPADDING + RPADDING < 3 || slen == 0)
{
- trg = (TRGM *) palloc(TRGMHDRSIZE);
+ /* allocate at least sizeof(TRGM), to cover padding */
+ trg = (TRGM *) palloc(Max(TRGMHDRSIZE, sizeof(TRGM)));
trg->flag = ARRKEY;
SET_VARSIZE(trg, TRGMHDRSIZE);
return trg;
--
2.56.0
From 759e2be23ade06659c9e2d152796d226bfc02b83 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Fri, 13 Nov 2026 11:58:18 +0100
Subject: [PATCH 4/5] Fixes for alloc_size attribute [numeric]
"Short" Numeric values are now allocated with at least the size of the
struct, so that accessing fields through the struct stays within the
allocation.
---
src/backend/utils/adt/numeric.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 7deea2046f1..9f0913e2326 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -7620,7 +7620,11 @@ make_result_safe(const NumericVar *var, Node *escontext)
sign == NUMERIC_NINF))
elog(ERROR, "invalid numeric sign value 0x%x", sign);
- result = (Numeric) palloc(NUMERIC_HDRSZ_SHORT);
+ /*
+ * We must allocate at least the full struct size, even though
the
+ * datum only uses NUMERIC_HDRSZ_SHORT bytes.
+ */
+ result = (Numeric) palloc(sizeof(struct NumericData));
SET_VARSIZE(result, NUMERIC_HDRSZ_SHORT);
result->choice.n_header = sign;
--
2.56.0
From 703a9e47c45bce23c90dc95bacb4aa1cd255fcab Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Fri, 13 Nov 2026 11:58:18 +0100
Subject: [PATCH 5/5] XXX ATTRIBUTE_FIXED_PART_SIZE fix
As of commit 6a3da13d376, FormData_pg_attribute has 4 bytes of
trailing padding, and so sizeof(FormData_pg_attribute) is different
from ATTRIBUTE_FIXED_PART_SIZE. The compiler flags it in this one
place, but this probably could use a larger cleanup.
Discussion:
https://www.postgresql.org/message-id/flat/7c7d41c1-fd5d-f9a5-afd5-f00168d91fe2%402ndquadrant.com
---
src/backend/bootstrap/bootstrap.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/backend/bootstrap/bootstrap.c
b/src/backend/bootstrap/bootstrap.c
index 00786d8c0fa..8b926b06401 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1114,7 +1114,7 @@ static Form_pg_attribute
AllocateAttribute(void)
{
return (Form_pg_attribute)
- MemoryContextAllocZero(TopMemoryContext,
ATTRIBUTE_FIXED_PART_SIZE);
+ MemoryContextAllocZero(TopMemoryContext,
sizeof(FormData_pg_attribute));
}
/*
--
2.56.0