Given that we now have a tree that compiles fine against -Werror=mismatched-dealloc, we need to make sure that we don't regress. By adding the malloc attribute[0], we can protect against regressions, enable more accurate code coverage with -fanalyzer, and allow the compiler to do some optimizations.
Let me know if I missed any allocators. Additionally, does it make sense to add the attribute to the various memory context allocator functions like AllocSetAlloc(), or only on the higher level functions. [0]: https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-malloc -- Tristan Partin PostgreSQL Contributors Team AWS (https://aws.amazon.com)
From 743eca63166373f67c456c4fbb010f5ae61d2b83 Mon Sep 17 00:00:00 2001 From: Tristan Partin <[email protected]> Date: Wed, 1 Jul 2026 16:34:14 +0000 Subject: [PATCH v1] Add malloc attribute to memory allocation functions GCC supports a malloc attribute that takes an optional argument of the allocating functions deallocation function. With this additional information, GCC can do a few things for us: - Warn if the deallocation function is not matched to the allocation function (-Wmismatched-dealloc, -Wanalyzer-mismatching-deallocation) - Warn if there is a path would double free memory (-Wanalyzer-double-free) - Warn if the returned memory is not checked for NULL and is subsequently passed to a non-NULL argument (-Wanalyzer-possible-null-dereference, -Wanalyzer-possible-null-argument) - Warn if the memory was used after it was freed (-Wanalyzer-use-after-free) - Warn if the memory is ever leaked (-Wanalyzer-malloc-leak) - Warn if a deallocation function is called on global on-stack memory (-Wanalyzer-free-of-non-heap) - Optimize callers because GCC can assume based on the annotation that memory allocation will fail extremely infrequently, like malloc(3) Signed-off-by: Tristan Partin <[email protected]> --- src/include/c.h | 12 +++++++++++ src/include/common/fe_memutils.h | 36 ++++++++++++++++---------------- src/include/utils/palloc.h | 36 ++++++++++++++++---------------- 3 files changed, 48 insertions(+), 36 deletions(-) diff --git a/src/include/c.h b/src/include/c.h index 0e4aea5d5a..2dd5e713ad 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -274,6 +274,18 @@ extern "C++" #define pg_attribute_no_sanitize_alignment() #endif +/* + * The malloc function attribute indicates that the function is malloc-like. + * Clang does not currently make use of this function attribute. + * + * https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-malloc + */ +#if __has_attribute(malloc) && !defined(__clang__) +#define pg_attribute_malloc(...) __attribute__((malloc(__VA_ARGS__))) +#else +#define pg_attribute_malloc(...) +#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 d5c6d37bb6..72caf489d3 100644 --- a/src/include/common/fe_memutils.h +++ b/src/include/common/fe_memutils.h @@ -34,21 +34,21 @@ * "Safe" memory allocation functions --- these exit(1) on failure * (except pg_malloc_extended with MCXT_ALLOC_NO_OOM) */ -extern char *pg_strdup(const char *in); -extern void *pg_malloc(size_t size); -extern void *pg_malloc0(size_t size); -extern void *pg_malloc_extended(size_t size, int flags); -extern void *pg_realloc(void *ptr, size_t size); extern void pg_free(void *ptr); +extern char *pg_strdup(const char *in) pg_attribute_malloc(pg_free); +extern void *pg_malloc(size_t size) pg_attribute_malloc(pg_free); +extern void *pg_malloc0(size_t size) pg_attribute_malloc(pg_free); +extern void *pg_malloc_extended(size_t size, int flags) pg_attribute_malloc(pg_free); +extern void *pg_realloc(void *ptr, size_t size); /* * Support for safe calculation of memory request sizes */ extern Size add_size(Size s1, Size s2); extern Size mul_size(Size s1, Size s2); -extern void *pg_malloc_mul(Size s1, Size s2); -extern void *pg_malloc0_mul(Size s1, Size s2); -extern void *pg_malloc_mul_extended(Size s1, Size s2, int flags); +extern void *pg_malloc_mul(Size s1, Size s2) pg_attribute_malloc(pg_free); +extern void *pg_malloc0_mul(Size s1, Size s2) pg_attribute_malloc(pg_free); +extern void *pg_malloc_mul_extended(Size s1, Size s2, int flags) pg_attribute_malloc(pg_free); extern void *pg_realloc_mul(void *p, Size s1, Size s2); /* @@ -75,16 +75,16 @@ extern void *pg_realloc_mul(void *p, Size s1, Size s2); #define pg_realloc_array(pointer, type, count) ((type *) pg_realloc_mul(pointer, sizeof(type), count)) /* Equivalent functions, deliberately named the same as backend functions */ -extern char *pstrdup(const char *in); -extern char *pnstrdup(const char *in, Size size); -extern void *palloc(Size size); -extern void *palloc0(Size size); -extern void *palloc_extended(Size size, int flags); -extern void *repalloc(void *pointer, Size size); extern void pfree(void *pointer); -extern void *palloc_mul(Size s1, Size s2); -extern void *palloc0_mul(Size s1, Size s2); -extern void *palloc_mul_extended(Size s1, Size s2, int flags); +extern char *pstrdup(const char *in) pg_attribute_malloc(pfree); +extern char *pnstrdup(const char *in, Size size) pg_attribute_malloc(pfree); +extern void *palloc(Size size) pg_attribute_malloc(pfree); +extern void *palloc0(Size size) pg_attribute_malloc(pfree); +extern void *palloc_extended(Size size, int flags) pg_attribute_malloc(pfree); +extern void *repalloc(void *pointer, Size size); +extern void *palloc_mul(Size s1, Size s2) pg_attribute_malloc(pfree); +extern void *palloc0_mul(Size s1, Size s2) pg_attribute_malloc(pfree); +extern void *palloc_mul_extended(Size s1, Size s2, int flags) pg_attribute_malloc(pfree); extern void *repalloc_mul(void *p, Size s1, Size s2); #define palloc_object(type) ((type *) palloc(sizeof(type))) @@ -95,7 +95,7 @@ extern void *repalloc_mul(void *p, Size s1, Size s2); #define repalloc_array(pointer, type, count) ((type *) repalloc_mul(pointer, sizeof(type), count)) /* sprintf into a palloc'd buffer --- these are in psprintf.c */ -extern char *psprintf(const char *fmt, ...) pg_attribute_printf(1, 2); +extern char *psprintf(const char *fmt, ...) pg_attribute_printf(1, 2) pg_attribute_malloc(pfree); extern size_t pvsnprintf(char *buf, size_t len, const char *fmt, va_list args) pg_attribute_printf(3, 0); #endif /* FE_MEMUTILS_H */ diff --git a/src/include/utils/palloc.h b/src/include/utils/palloc.h index 0e934158b6..8180272628 100644 --- a/src/include/utils/palloc.h +++ b/src/include/utils/palloc.h @@ -68,31 +68,31 @@ extern PGDLLIMPORT MemoryContext CurrentMemoryContext; /* * Fundamental memory-allocation operations (more are in utils/memutils.h) */ -extern void *MemoryContextAlloc(MemoryContext context, Size size); -extern void *MemoryContextAllocZero(MemoryContext context, Size size); +extern void *MemoryContextAlloc(MemoryContext context, Size size) pg_attribute_malloc(); +extern void *MemoryContextAllocZero(MemoryContext context, Size size) pg_attribute_malloc(); extern void *MemoryContextAllocExtended(MemoryContext context, - Size size, int flags); + Size size, int flags) pg_attribute_malloc(); extern void *MemoryContextAllocAligned(MemoryContext context, - Size size, Size alignto, int flags); + Size size, Size alignto, int flags) pg_attribute_malloc(); -extern void *palloc(Size size); -extern void *palloc0(Size size); -extern void *palloc_extended(Size size, int flags); -extern void *palloc_aligned(Size size, Size alignto, int flags); +extern void pfree(void *pointer); +extern void *palloc(Size size) pg_attribute_malloc(pfree); +extern void *palloc0(Size size) pg_attribute_malloc(pfree); +extern void *palloc_extended(Size size, int flags) pg_attribute_malloc(pfree); +extern void *palloc_aligned(Size size, Size alignto, int flags) pg_attribute_malloc(pfree); pg_nodiscard extern void *repalloc(void *pointer, Size size); pg_nodiscard extern void *repalloc_extended(void *pointer, Size size, int flags); pg_nodiscard extern void *repalloc0(void *pointer, Size oldsize, Size size); -extern void pfree(void *pointer); /* * Support for safe calculation of memory request sizes */ extern Size add_size(Size s1, Size s2); extern Size mul_size(Size s1, Size s2); -extern void *palloc_mul(Size s1, Size s2); -extern void *palloc0_mul(Size s1, Size s2); -extern void *palloc_mul_extended(Size s1, Size s2, int flags); +extern void *palloc_mul(Size s1, Size s2) pg_attribute_malloc(pfree); +extern void *palloc0_mul(Size s1, Size s2) pg_attribute_malloc(pfree); +extern void *palloc_mul_extended(Size s1, Size s2, int flags) pg_attribute_malloc(pfree); pg_nodiscard 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); @@ -123,7 +123,7 @@ 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. */ -extern void *MemoryContextAllocHuge(MemoryContext context, Size size); +extern void *MemoryContextAllocHuge(MemoryContext context, Size size) pg_attribute_malloc(); pg_nodiscard extern void *repalloc_huge(void *pointer, Size size); /* @@ -154,14 +154,14 @@ extern void MemoryContextUnregisterResetCallback(MemoryContext context, * These are like standard strdup() except the copied string is * allocated in a context, not with malloc(). */ -extern char *MemoryContextStrdup(MemoryContext context, const char *string); -extern char *pstrdup(const char *in); -extern char *pnstrdup(const char *in, Size len); +extern char *MemoryContextStrdup(MemoryContext context, const char *string) pg_attribute_malloc(); +extern char *pstrdup(const char *in) pg_attribute_malloc(pfree); +extern char *pnstrdup(const char *in, Size len) pg_attribute_malloc(pfree); -extern char *pchomp(const char *in); +extern char *pchomp(const char *in) pg_attribute_malloc(pfree); /* sprintf into a palloc'd buffer --- these are in psprintf.c */ -extern char *psprintf(const char *fmt, ...) pg_attribute_printf(1, 2); +extern char *psprintf(const char *fmt, ...) pg_attribute_printf(1, 2) pg_attribute_malloc(pfree); extern size_t pvsnprintf(char *buf, size_t len, const char *fmt, va_list args) pg_attribute_printf(3, 0); #endif /* PALLOC_H */ -- Tristan Partin https://tristan.partin.io
