From db3cff366aca0d14bd5f6d69ee0cac9b2acabd83 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Sun, 20 Sep 2026 00:29:44 +0530 Subject: [PATCH v1 2/2] Allow unknown-size shmem attachments in single-user mode ShmemRequestInternal() rejects SHMEM_ATTACH_UNKNOWN_SIZE whenever IsUnderPostmaster is false. That also rejects an after-startup attachment in a standalone backend, even if the area exists. Distinguish after-startup request callbacks from startup requests in the request state machine. This permits unknown-size attaches in single-user mode without permitting unknown-size allocations during standalone or postmaster startup. Preserve acceptance of requests made by EXEC_BACKEND children during attachment. Test attachment to an existing area in normal and single-user backends, and retain rejection of unknown-size requests during single-user startup. Backpatch-through: 19 --- src/backend/storage/ipc/shmem.c | 20 ++++++++------ .../test_shmem/t/001_late_shmem_alloc.pl | 24 +++++++++++++++++ .../modules/test_shmem/test_shmem--1.0.sql | 4 +++ src/test/modules/test_shmem/test_shmem.c | 27 +++++++++++++++++++ 4 files changed, 67 insertions(+), 8 deletions(-) diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c index c0a8fb39e47a7ff773d0ac3740d34cdec2c03339..69f38389368bd9f037e8dbf04917eb23c688979f 100644 --- a/src/backend/storage/ipc/shmem.c +++ b/src/backend/storage/ipc/shmem.c @@ -187,7 +187,7 @@ static List *pending_shmem_requests; /* List of ShmemRequests */ * INITIAL -> REQUESTING -> ATTACHING -> DONE * * Late request: - * DONE -> REQUESTING -> AFTER_STARTUP_ATTACH_OR_INIT -> DONE + * DONE -> REQUESTING_AFTER_STARTUP -> AFTER_STARTUP_ATTACH_OR_INIT -> DONE */ enum shmem_request_state { @@ -195,9 +195,8 @@ enum shmem_request_state SRS_INITIAL, /* - * When we start calling the shmem_request callbacks, we enter the - * SRS_REQUESTING phase. All ShmemRequestStruct calls happen in this - * state. + * When we start calling the shmem_request callbacks during startup, we + * enter the SRS_REQUESTING phase. */ SRS_REQUESTING, @@ -213,6 +212,9 @@ enum shmem_request_state */ SRS_ATTACHING, + /* An after-startup request callback is running */ + SRS_REQUESTING_AFTER_STARTUP, + /* An after-startup allocation or attachment is in progress */ SRS_AFTER_STARTUP_ATTACH_OR_INIT, @@ -352,7 +354,7 @@ ShmemRequestInternal(ShmemStructOpts *options, ShmemRequestKind kind) if (options->name == NULL) elog(ERROR, "shared memory request is missing 'name' option"); - if (IsUnderPostmaster) + if (IsUnderPostmaster || shmem_request_state == SRS_REQUESTING_AFTER_STARTUP) { if (options->size <= 0 && options->size != SHMEM_ATTACH_UNKNOWN_SIZE) elog(ERROR, "invalid size %zd for shared memory request for \"%s\"", @@ -372,7 +374,8 @@ ShmemRequestInternal(ShmemStructOpts *options, ShmemRequestKind kind) options->alignment, options->name); /* Check that we're in the right state */ - if (shmem_request_state != SRS_REQUESTING) + if (shmem_request_state != SRS_REQUESTING && + shmem_request_state != SRS_REQUESTING_AFTER_STARTUP) elog(ERROR, "ShmemRequestStruct can only be called from a shmem_request callback"); /* Check that it's not already registered in this process */ @@ -963,7 +966,7 @@ CallShmemCallbacksAfterStartup(const ShmemCallbacks *callbacks) PG_TRY(); { - shmem_request_state = SRS_REQUESTING; + shmem_request_state = SRS_REQUESTING_AFTER_STARTUP; /* * Call the request callback first. The callback makes @@ -1127,7 +1130,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr) Assert(shmem_request_state == SRS_DONE || shmem_request_state == SRS_INITIALIZING || - shmem_request_state == SRS_REQUESTING); + shmem_request_state == SRS_REQUESTING || + shmem_request_state == SRS_REQUESTING_AFTER_STARTUP); LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE); 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 cd0077229484ee3a400da5d8d389ca9416b1616f..0934eeb105ef45ce6e691ff5406ba5c5eb0b3e46 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 @@ -13,6 +13,8 @@ my $node = PostgreSQL::Test::Cluster->new('main'); $node->init; $node->start; $node->safe_psql("postgres", "CREATE EXTENSION test_shmem"); +is($node->safe_psql("postgres", "SELECT test_shmem_unknown_size();"), 't', + "unknown-size attachment works in a normal backend"); $node->stop; ### @@ -51,6 +53,28 @@ like( $node->stop; $node->adjust_conf('postgresql.conf', 'test_shmem.area_size', undef); +SKIP: +{ + skip 'single-user test is not supported by this platform', 2 + if $windows_os; + my @command = ( + 'postgres', '--single', '-F', + '-c' => 'exit_on_error=true', + '-D' => $node->data_dir); + my $query = "SELECT test_shmem_unknown_size();\n"; + my $result = run_log([@command, 'postgres'], '<' => \$query); + ok($result, "unknown-size attachment works in single-user mode"); + + my $stderr; + $result = run_log( + [@command, + '-c' => 'shared_preload_libraries=test_shmem', + '-c' => 'test_shmem.area_size=-1', 'postgres'], + '<' => \$query, '2>' => \$stderr); + ok(!$result && $stderr =~ /SHMEM_ATTACH_UNKNOWN_SIZE cannot be used during startup/, + "unknown-size requests are rejected during single-user startup"); +} + ### # Test allocating memory after startup in single-user mode ### diff --git a/src/test/modules/test_shmem/test_shmem--1.0.sql b/src/test/modules/test_shmem/test_shmem--1.0.sql index 04ead79e3482465ad4c71e441f3b1043bca5f5a5..a936c0a0674c57b1d957990e2c3731c45fb11389 100644 --- a/src/test/modules/test_shmem/test_shmem--1.0.sql +++ b/src/test/modules/test_shmem/test_shmem--1.0.sql @@ -11,3 +11,7 @@ AS 'MODULE_PATHNAME' LANGUAGE C; CREATE FUNCTION get_test_shmem_attach_count() RETURNS pg_catalog.int4 STRICT AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_shmem_unknown_size() +RETURNS pg_catalog.bool STRICT +AS 'MODULE_PATHNAME' LANGUAGE C; diff --git a/src/test/modules/test_shmem/test_shmem.c b/src/test/modules/test_shmem/test_shmem.c index d2f1e004886e2a0560450381a270fbcd579616b5..45e87a50bf4e3d1d8e759c645a7ca26678ce8e46 100644 --- a/src/test/modules/test_shmem/test_shmem.c +++ b/src/test/modules/test_shmem/test_shmem.c @@ -45,6 +45,7 @@ static bool test_shmem_guc_defined = false; static void test_shmem_request(void *arg); static void test_shmem_init(void *arg); static void test_shmem_attach(void *arg); +static void test_shmem_unknown_request(void *arg); static const ShmemCallbacks TestShmemCallbacks = { .flags = SHMEM_CALLBACKS_ALLOW_AFTER_STARTUP, @@ -63,6 +64,14 @@ test_shmem_request(void *arg) .ptr = (void **) &TestShmem); } +static void +test_shmem_unknown_request(void *arg) +{ + ShmemRequestStruct(.name = "test_shmem area", + .size = SHMEM_ATTACH_UNKNOWN_SIZE, + .ptr = (void **) arg); +} + static void test_shmem_init(void *arg) { @@ -119,6 +128,24 @@ _PG_init(void) RegisterShmemCallbacks(&TestShmemCallbacks); } +PG_FUNCTION_INFO_V1(test_shmem_unknown_size); +Datum +test_shmem_unknown_size(PG_FUNCTION_ARGS) +{ + TestShmemData *attached = NULL; + ShmemCallbacks callbacks = { + .flags = SHMEM_CALLBACKS_ALLOW_AFTER_STARTUP, + .request_fn = test_shmem_unknown_request, + .opaque_arg = &attached, + }; + + RegisterShmemCallbacks(&callbacks); + if (attached == NULL || attached != TestShmem || !attached->initialized) + elog(ERROR, "could not attach to shared memory with unknown size"); + + PG_RETURN_BOOL(true); +} + PG_FUNCTION_INFO_V1(get_test_shmem_attach_count); Datum get_test_shmem_attach_count(PG_FUNCTION_ARGS) -- 2.34.1