}
-/*
- * Add two Size values, checking for overflow
- */
-Size
-add_size(Size s1, Size s2)
-{
- Size result;
-
- result = s1 + s2;
- /* We are assuming Size is an unsigned type here... */
- if (result < s1 || result < s2)
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("requested shared memory size overflows size_t")));
- return result;
-}
-
-/*
- * Multiply two Size values, checking for overflow
- */
-Size
-mul_size(Size s1, Size s2)
-{
- Size result;
-
- if (s1 == 0 || s2 == 0)
- return 0;
- result = s1 * s2;
- /* We are assuming Size is an unsigned type here... */
- if (result / s2 != s1)
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("requested shared memory size overflows size_t")));
- return result;
-}
-
/* SQL SRF showing allocated shared memory */
Datum
pg_get_shmem_allocations(PG_FUNCTION_ARGS)
#include "postgres.h"
+#include "common/int.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "storage/proc.h"
static void MemoryContextStatsPrint(MemoryContext context, void *passthru,
const char *stats_string,
bool print_to_stderr);
+static pg_noinline void add_size_error(Size s1, Size s2) pg_attribute_noreturn();
+static pg_noinline void mul_size_error(Size s1, Size s2) pg_attribute_noreturn();
/*
* You should not do memory allocations within a critical section, because
return ret;
}
+/*
+ * repalloc_extended
+ * Adjust the size of a previously allocated chunk,
+ * with HUGE and NO_OOM options.
+ */
+void *
+repalloc_extended(void *pointer, Size size, int flags)
+{
+ MemoryContext context = GetMemoryChunkContext(pointer);
+ void *ret;
+
+ if (!((flags & MCXT_ALLOC_HUGE) != 0 ? AllocHugeSizeIsValid(size) :
+ AllocSizeIsValid(size)))
+ elog(ERROR, "invalid memory alloc request size %zu", size);
+
+ AssertNotInCriticalSection(context);
+
+ /* isReset must be false already */
+ Assert(!context->isReset);
+
+ ret = context->methods->realloc(context, pointer, size);
+ if (unlikely(ret == NULL))
+ {
+ if ((flags & MCXT_ALLOC_NO_OOM) == 0)
+ {
+ MemoryContextStats(TopMemoryContext);
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("out of memory"),
+ errdetail("Failed on request of size %zu in memory context \"%s\".",
+ size, context->name)));
+ }
+ return NULL;
+ }
+
+ VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size);
+
+ return ret;
+}
+
+/*
+ * Support for safe calculation of memory request sizes
+ *
+ * These functions perform the requested calculation, but throw error if the
+ * result overflows.
+ *
+ * An important property of these functions is that if an argument was a
+ * negative signed int before promotion (implying overflow in calculating it)
+ * we will detect that as an error. That happens because we reject results
+ * larger than SIZE_MAX / 2 later on, in the actual allocation step.
+ */
+Size
+add_size(Size s1, Size s2)
+{
+ Size result;
+
+ if (unlikely(pg_add_size_overflow(s1, s2, &result)))
+ add_size_error(s1, s2);
+ return result;
+}
+
+static pg_noinline void
+add_size_error(Size s1, Size s2)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("invalid memory allocation request size %zu + %zu",
+ s1, s2)));
+}
+
+Size
+mul_size(Size s1, Size s2)
+{
+ Size result;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &result)))
+ mul_size_error(s1, s2);
+ return result;
+}
+
+static pg_noinline void
+mul_size_error(Size s1, Size s2)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("invalid memory allocation request size %zu * %zu",
+ s1, s2)));
+}
+
+/*
+ * palloc_mul
+ * Equivalent to palloc(mul_size(s1, s2)).
+ */
+void *
+palloc_mul(Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req)))
+ mul_size_error(s1, s2);
+ return palloc(req);
+}
+
+/*
+ * palloc0_mul
+ * Equivalent to palloc0(mul_size(s1, s2)).
+ *
+ * This is comparable to standard calloc's behavior.
+ */
+void *
+palloc0_mul(Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req)))
+ mul_size_error(s1, s2);
+ return palloc0(req);
+}
+
+/*
+ * palloc_mul_extended
+ * Equivalent to palloc_extended(mul_size(s1, s2), flags).
+ */
+void *
+palloc_mul_extended(Size s1, Size s2, int flags)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req)))
+ mul_size_error(s1, s2);
+ return palloc_extended(req, flags);
+}
+
+/*
+ * repalloc_mul
+ * Equivalent to repalloc(p, mul_size(s1, s2)).
+ */
+void *
+repalloc_mul(void *p, Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req)))
+ mul_size_error(s1, s2);
+ return repalloc(p, req);
+}
+
+/*
+ * repalloc_mul_extended
+ * Equivalent to repalloc_extended(p, mul_size(s1, s2), flags).
+ */
+void *
+repalloc_mul_extended(void *p, Size s1, Size s2, int flags)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req)))
+ mul_size_error(s1, s2);
+ return repalloc_extended(p, req, flags);
+}
+
/*
* MemoryContextAllocHuge
* Allocate (possibly-expansive) space within the specified context.
void *
repalloc_huge(void *pointer, Size size)
{
- MemoryContext context = GetMemoryChunkContext(pointer);
- void *ret;
-
- if (!AllocHugeSizeIsValid(size))
- elog(ERROR, "invalid memory alloc request size %zu", size);
-
- AssertNotInCriticalSection(context);
-
- /* isReset must be false already */
- Assert(!context->isReset);
-
- ret = context->methods->realloc(context, pointer, size);
- if (unlikely(ret == NULL))
- {
- MemoryContextStats(TopMemoryContext);
- ereport(ERROR,
- (errcode(ERRCODE_OUT_OF_MEMORY),
- errmsg("out of memory"),
- errdetail("Failed on request of size %zu in memory context \"%s\".",
- size, context->name)));
- }
-
- VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size);
-
- return ret;
+ /* this one seems not worth its own implementation */
+ return repalloc_extended(pointer, size, MCXT_ALLOC_HUGE);
}
/*
#include "postgres_fe.h"
+#include "common/int.h"
+
+static pg_noinline void add_size_error(Size s1, Size s2) pg_attribute_noreturn();
+static pg_noinline void mul_size_error(Size s1, Size s2) pg_attribute_noreturn();
+
+
static inline void *
pg_malloc_internal(size_t size, int flags)
{
{
return pg_realloc(pointer, size);
}
+
+/*
+ * Support for safe calculation of memory request sizes
+ *
+ * These functions perform the requested calculation, but throw error if the
+ * result overflows.
+ *
+ * An important property of these functions is that if an argument was a
+ * negative signed int before promotion (implying overflow in calculating it)
+ * we will detect that as an error. That happens because we reject results
+ * larger than SIZE_MAX / 2. In the backend we rely on later checks to do
+ * that, but in frontend we must do it here.
+ */
+Size
+add_size(Size s1, Size s2)
+{
+ Size result;
+
+ if (unlikely(pg_add_size_overflow(s1, s2, &result) ||
+ result > (SIZE_MAX / 2)))
+ add_size_error(s1, s2);
+ return result;
+}
+
+static pg_noinline void
+add_size_error(Size s1, Size s2)
+{
+ fprintf(stderr, _("invalid memory allocation request size %zu + %zu\n"),
+ s1, s2);
+ exit(EXIT_FAILURE);
+}
+
+Size
+mul_size(Size s1, Size s2)
+{
+ Size result;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &result) ||
+ result > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return result;
+}
+
+static pg_noinline void
+mul_size_error(Size s1, Size s2)
+{
+ fprintf(stderr, _("invalid memory allocation request size %zu * %zu\n"),
+ s1, s2);
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * pg_malloc_mul
+ * Equivalent to pg_malloc(mul_size(s1, s2)).
+ */
+void *
+pg_malloc_mul(Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
+ req > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return pg_malloc(req);
+}
+
+/*
+ * pg_malloc0_mul
+ * Equivalent to pg_malloc0(mul_size(s1, s2)).
+ *
+ * This is comparable to standard calloc's behavior.
+ */
+void *
+pg_malloc0_mul(Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
+ req > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return pg_malloc0(req);
+}
+
+/*
+ * pg_malloc_mul_extended
+ * Equivalent to pg_malloc_extended(mul_size(s1, s2), flags).
+ */
+void *
+pg_malloc_mul_extended(Size s1, Size s2, int flags)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
+ req > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return pg_malloc_extended(req, flags);
+}
+
+/*
+ * pg_realloc_mul
+ * Equivalent to pg_realloc(p, mul_size(s1, s2)).
+ */
+void *
+pg_realloc_mul(void *p, Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
+ req > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return pg_realloc(p, req);
+}
+
+/*
+ * palloc_mul
+ * Equivalent to palloc(mul_size(s1, s2)).
+ */
+void *
+palloc_mul(Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
+ req > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return palloc(req);
+}
+
+/*
+ * palloc0_mul
+ * Equivalent to palloc0(mul_size(s1, s2)).
+ *
+ * This is comparable to standard calloc's behavior.
+ */
+void *
+palloc0_mul(Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
+ req > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return palloc0(req);
+}
+
+/*
+ * palloc_mul_extended
+ * Equivalent to palloc_extended(mul_size(s1, s2), flags).
+ */
+void *
+palloc_mul_extended(Size s1, Size s2, int flags)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
+ req > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return palloc_extended(req, flags);
+}
+
+/*
+ * repalloc_mul
+ * Equivalent to repalloc(p, mul_size(s1, s2)).
+ */
+void *
+repalloc_mul(void *p, Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
+ req > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return repalloc(p, req);
+}
extern void *pg_realloc(void *pointer, size_t size);
extern void pg_free(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 *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_realloc_mul(void *p, Size s1, Size s2);
+
/*
* Variants with easier notation and more type safety
*/
/*
* Allocate space for "count" objects of type "type"
*/
-#define pg_malloc_array(type, count) ((type *) pg_malloc(sizeof(type) * (count)))
-#define pg_malloc0_array(type, count) ((type *) pg_malloc0(sizeof(type) * (count)))
+#define pg_malloc_array(type, count) ((type *) pg_malloc_mul(sizeof(type), count))
+#define pg_malloc0_array(type, count) ((type *) pg_malloc0_mul(sizeof(type), count))
+#define pg_malloc_array_extended(type, count, flags) ((type *) pg_malloc_mul_extended(sizeof(type), count, flags))
/*
* Change size of allocation pointed to by "pointer" to have space for "count"
* objects of type "type"
*/
-#define pg_realloc_array(pointer, type, count) ((type *) pg_realloc(pointer, sizeof(type) * (count)))
+#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 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 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)))
-#define palloc_array(type, count) ((type *) palloc(sizeof(type) * (count)))
-#define palloc0_array(type, count) ((type *) palloc0(sizeof(type) * (count)))
-#define repalloc_array(pointer, type, count) ((type *) repalloc(pointer, sizeof(type) * (count)))
+#define palloc_array(type, count) ((type *) palloc_mul(sizeof(type), count))
+#define palloc0_array(type, count) ((type *) palloc0_mul(sizeof(type), count))
+#define palloc_array_extended(type, count, flags) ((type *) palloc_mul_extended(sizeof(type), count, flags))
+#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 HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
HASHCTL *infoP, int hash_flags);
extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
-extern Size add_size(Size s1, Size s2);
-extern Size mul_size(Size s1, Size s2);
/* ipci.c */
extern void RequestAddinShmemSpace(Size size);
#define AllocSizeIsValid(size) ((Size) (size) <= MaxAllocSize)
+/* Do not make this any bigger; see add_size() and mul_size() */
#define MaxAllocHugeSize (SIZE_MAX / 2)
#define AllocHugeSizeIsValid(size) ((Size) (size) <= MaxAllocHugeSize)
extern void *palloc0(Size size);
extern void *palloc_extended(Size size, int flags);
extern pg_nodiscard void *repalloc(void *pointer, Size size);
+extern pg_nodiscard void *repalloc_extended(void *pointer,
+ Size size, int flags);
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);
+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);
+
/*
* Variants with easier notation and more type safety
*/
/*
* Allocate space for "count" objects of type "type"
*/
-#define palloc_array(type, count) ((type *) palloc(sizeof(type) * (count)))
-#define palloc0_array(type, count) ((type *) palloc0(sizeof(type) * (count)))
+#define palloc_array(type, count) ((type *) palloc_mul(sizeof(type), count))
+#define palloc0_array(type, count) ((type *) palloc0_mul(sizeof(type), count))
+#define palloc_array_extended(type, count, flags) ((type *) palloc_mul_extended(sizeof(type), count, flags))
/*
* Change size of allocation pointed to by "pointer" to have space for "count"
* objects of type "type"
*/
-#define repalloc_array(pointer, type, count) ((type *) repalloc(pointer, sizeof(type) * (count)))
+#define repalloc_array(pointer, type, count) ((type *) repalloc_mul(pointer, sizeof(type), count))
+#define repalloc_array_extended(pointer, type, count, flags) ((type *) repalloc_mul_extended(pointer, sizeof(type), count, flags))
/*
* The result of palloc() is always word-aligned, so we can skip testing