From 8feb1a41041ad53385be5b9e07581d15d5b0dd31 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut 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