diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index a90dba0662f..bc04d9dfc13 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -3745,6 +3745,16 @@ my_shmem_init(void *arg)
areas are left in an abandoned state and any attempt to attach or
re-initialize them will fail until the server is restarted.
+
+ A backend attaching to an already allocated shared memory area must
+ normally pass the same size that was used to allocate
+ it to ShmemRequestStruct(); a mismatch results in
+ an error. If the attaching backend does not know the size in advance,
+ it can pass SHMEM_ATTACH_UNKNOWN_SIZE instead, to
+ skip this cross-check. SHMEM_ATTACH_UNKNOWN_SIZE can
+ only be used to attach to an area that already exists; using it before
+ allocating the area results in an error.
+
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index f971ee24192..3345735dff0 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -131,6 +131,7 @@
#include
#include "access/slru.h"
+#include "common/int.h"
#include "fmgr.h"
#include "funcapi.h"
#include "miscadmin.h"
@@ -535,6 +536,9 @@ InitShmemIndexEntry(ShmemRequest *request)
size_t allocated_size;
void *structPtr;
+ /* Size must be known at this point. */
+ Assert(request->options->size != SHMEM_ATTACH_UNKNOWN_SIZE);
+
/* look it up in the shmem index */
index_entry = (ShmemIndexEnt *)
hash_search(ShmemIndex, name, HASH_ENTER_NULL, &found);
@@ -836,6 +840,8 @@ ShmemAllocNoError(Size size)
*
* Also sets *allocated_size to the number of bytes allocated, which will
* be equal to the number requested plus any padding we choose to add.
+ *
+ * Returns NULL in case space can not be allocated.
*/
static void *
ShmemAllocRaw(Size size, Size alignment, Size *allocated_size)
@@ -866,8 +872,13 @@ ShmemAllocRaw(Size size, Size alignment, Size *allocated_size)
rawStart = ShmemAllocator->free_offset;
newStart = TYPEALIGN(alignment, rawStart);
- newFree = newStart + size;
- if (newFree <= ShmemSegHdr->totalsize)
+ /*
+ * newFree = newStart + size, which is the start of the remaining space
+ * after the allocation. If it exceeds the shmem segment size, we don't
+ * have enough space available.
+ */
+ if (!pg_add_size_overflow(newStart, size, &newFree) &&
+ newFree <= ShmemSegHdr->totalsize)
{
newSpace = (char *) ShmemBase + newStart;
ShmemAllocator->free_offset = newFree;
@@ -1024,7 +1035,14 @@ ProcessShmemRequestsAfterStartup(const ShmemCallbacks *callbacks)
found_any = true;
}
else
+ {
+ if (request->options->size == SHMEM_ATTACH_UNKNOWN_SIZE)
+ ereport(ERROR,
+ (errmsg("cannot attach to shared memory struct \"%s\" because it does not exist",
+ request->options->name),
+ errdetail("SHMEM_ATTACH_UNKNOWN_SIZE can only be used to attach to an existing shared memory structure.")));
notfound_any = true;
+ }
}
if (found_any && notfound_any)
elog(ERROR, "some of the requested shmem areas have already been initialized");
diff --git a/src/test/modules/test_shmem/t/001_late_shmem_alloc.pl b/src/test/modules/test_shmem/t/001_late_shmem_alloc.pl
index 9432b42181b..7d961fe2790 100644
--- a/src/test/modules/test_shmem/t/001_late_shmem_alloc.pl
+++ b/src/test/modules/test_shmem/t/001_late_shmem_alloc.pl
@@ -31,6 +31,23 @@ cmp_ok($attach_count2, '>', $attach_count1,
$node->stop;
+###
+# Test that trying to allocate a new shmem area with size =
+# SHMEM_ATTACH_UNKNOWN_SIZE (-1) fails.
+###
+$node->append_conf('postgresql.conf', "test_shmem.area_size = -1");
+$node->start;
+
+my (undef, undef, $stderr) =
+ $node->psql("postgres", "SELECT get_test_shmem_attach_count();");
+like(
+ $stderr,
+ qr/cannot attach to shared memory struct "test_shmem area" because it does not exist/,
+ "unknown size request for a nonexistent area fails");
+
+$node->stop;
+$node->adjust_conf('postgresql.conf', 'test_shmem.area_size', undef);
+
###
# Test allocating memory after startup in single-user mode
###
diff --git a/src/test/modules/test_shmem/test_shmem.c b/src/test/modules/test_shmem/test_shmem.c
index 6cf47dc8968..2a7e13512bf 100644
--- a/src/test/modules/test_shmem/test_shmem.c
+++ b/src/test/modules/test_shmem/test_shmem.c
@@ -35,12 +35,11 @@ typedef struct TestShmemData
static TestShmemData *TestShmem;
-#define MIN_TEST_AREA_BYTES sizeof(TestShmemData)
-#define DEFAULT_TEST_AREA_BYTES MIN_TEST_AREA_BYTES
+#define DEFAULT_TEST_AREA_BYTES sizeof(TestShmemData)
#define MAX_TEST_AREA_BYTES 1000000
static bool attached_or_initialized = false;
-static int test_shmem_area_size = MIN_TEST_AREA_BYTES;
+static int test_shmem_area_size = DEFAULT_TEST_AREA_BYTES;
static bool test_shmem_guc_defined = false;
static void test_shmem_request(void *arg);
@@ -100,12 +99,16 @@ _PG_init(void)
if (!test_shmem_guc_defined)
{
+ /*
+ * The minimum size that makes sense is sizeof(TestShmemData), but we
+ * allow -1 so that we can test passing SHMEM_ATTACH_UNKNOWN_SIZE.
+ */
DefineCustomIntVariable("test_shmem.area_size",
"Size of the shmem area to request.",
NULL,
&test_shmem_area_size,
DEFAULT_TEST_AREA_BYTES,
- MIN_TEST_AREA_BYTES,
+ -1,
MAX_TEST_AREA_BYTES,
PGC_USERSET,
GUC_UNIT_BYTE,