diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c
index f012e99c9d7..a91082efb9c 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -787,6 +787,14 @@ ReplicationSlotRelease(void)
 		 * decoding be disabled.
 		 */
 		ReplicationSlotDropAcquired(is_logical);
+
+		/*
+		 * The slot's array entry is now free and may be reused by another
+		 * backend at any moment.  This injection point lets tests open that
+		 * window deterministically and verify we never touch the (now stale)
+		 * slot pointer afterwards.
+		 */
+		INJECTION_POINT("ephemeral-slot-release-after-drop", NULL);
 	}
 
 	/*
diff --git a/src/test/recovery/t/006_logical_decoding.pl b/src/test/recovery/t/006_logical_decoding.pl
index 97d11f98b59..fa5a0222dcd 100644
--- a/src/test/recovery/t/006_logical_decoding.pl
+++ b/src/test/recovery/t/006_logical_decoding.pl
@@ -275,6 +275,80 @@ is( $node_primary->safe_psql(
 	qq(Check that reset timestamp is later after resetting stats for slot '$stats_test_slot1' again.)
 );
 
+# Regression test for the race in ReplicationSlotRelease() when releasing an
+# ephemeral slot.  Releasing an ephemeral slot first drops it via
+# ReplicationSlotDropAcquired(), which frees the slot's array entry for reuse.
+# ReplicationSlotRelease() must not touch that (now stale) slot pointer
+# afterwards, or it can corrupt an unrelated slot that grabbed the freed entry.
+# This needs an injection point, so it only runs on builds that support them.
+SKIP:
+{
+	skip "Injection points not supported by this build", 2
+	  if $ENV{enable_injection_points} ne 'yes';
+	skip "Extension injection_points not installed", 2
+	  unless $node_primary->check_extension('injection_points');
+
+	$node_primary->safe_psql('postgres', q(CREATE EXTENSION injection_points));
+
+	# Freeze a backend right after it drops an ephemeral slot, i.e. once the
+	# slot's array entry has been freed and is available for reuse.
+	$node_primary->safe_psql('postgres',
+		q{SELECT injection_points_attach('ephemeral-slot-release-after-drop', 'wait')}
+	);
+
+	# Create a logical slot with a non-existent output plugin.  The slot is
+	# created as RS_EPHEMERAL; the bogus plugin makes creation ERROR out, and
+	# the error path calls ReplicationSlotRelease(), which drops the ephemeral
+	# slot and then blocks on the injection point.  on_error_stop => 0 keeps the
+	# session alive past the ERROR so it can still report 'done_release'.
+	my $dropper =
+	  $node_primary->background_psql('postgres', on_error_stop => 0);
+	$dropper->query_until(
+		qr/start_drop/, q(
+\echo start_drop
+SELECT pg_create_logical_replication_slot('test_slot_dropped', 'no_such_plugin', false);
+\echo done_release
+));
+
+	# Wait until the backend is parked on the injection point, with the array
+	# entry of 'test_slot_dropped' freed and up for grabs.
+	$node_primary->wait_for_event('client backend',
+		'ephemeral-slot-release-after-drop');
+
+	# A second backend creates a new persistent slot, which reuses the just
+	# freed array entry -- the very entry the frozen backend still points at.
+	$node_primary->safe_psql('postgres',
+		q{SELECT pg_create_physical_replication_slot('test_slot_created', true, false)}
+	);
+
+	my $before = $node_primary->safe_psql('postgres',
+		q{SELECT inactive_since FROM pg_replication_slots WHERE slot_name = 'test_slot_created'}
+	);
+
+	# Let the frozen backend finish releasing.  With the bug it now scribbles on
+	# the reused entry; with the fix it leaves it untouched.
+	$node_primary->safe_psql('postgres',
+		q{SELECT injection_points_wakeup('ephemeral-slot-release-after-drop')});
+	$dropper->query_until(qr/done_release/, '');
+
+	my $after = $node_primary->safe_psql('postgres',
+		q{SELECT inactive_since FROM pg_replication_slots WHERE slot_name = 'test_slot_created'}
+	);
+
+	is($after, $before,
+		'releasing an ephemeral slot must not modify a slot that reused its entry'
+	);
+
+	my $slot = $node_primary->safe_psql('postgres',
+		q{SELECT slot_name, slot_type FROM pg_replication_slots WHERE slot_name = 'test_slot_created'}
+	);
+	is($slot, "test_slot_created|physical", 'reused slot is intact');
+
+	$dropper->quit;
+	$node_primary->safe_psql('postgres',
+		q{SELECT injection_points_detach('ephemeral-slot-release-after-drop')});
+}
+
 # done with the node
 $node_primary->stop;
 
