Hi, On Fri, Apr 10, 2026 at 5:29 PM Jim Jones <[email protected]> wrote: > > > BTW, what do you think about making this comment less "concrete"? : > > # SELECT via index scan from other session. > > # Sequential scans are blocked at read_stream_begin_relation(); index scans > > # bypass that path entirely and reach ReadBufferExtended() in bufmgr.c > > # (nbtree's _bt_getbuf calls ReadBuffer directly for individual page > > fetches). > > # enable_seqscan=off forces the planner to use the index. > > > > I mean that if the described logic changes, this comment will become > > confusing. > > We can describe the test in general words. For example : > > # Index scans can use a different code path from the one sequential scans > > are > > # following. Make sure that we cannot access other sessions' temp tables > > during > > # index scan either. > > +1 > > Yeah, it's indeed too verbose. I guess these comments were originally > just for me so I wouldn't get too confused along the way :)
OK :) > > I don't have anything else to add at this point. Unless there are any > objections, I'll mark the CF entry as 'Ready for Committer.' > Great, thank you! Please, see an updated set of patches (only perl test has been changed) : 1) Rephrase the discussed comment. 2) Use safe_psql whenever possible. 3) Run pgperltidy. -- Best regards, Daniil Davydov
From 9045b387f7722193d4239dc2dbb73793f3978866 Mon Sep 17 00:00:00 2001 From: Daniil Davidov <[email protected]> Date: Fri, 10 Apr 2026 13:51:14 +0700 Subject: [PATCH v17 1/2] Prevent access to other sessions' temp tables --- src/backend/storage/aio/read_stream.c | 10 ++++++++++ src/backend/storage/buffer/bufmgr.c | 16 ++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/backend/storage/aio/read_stream.c b/src/backend/storage/aio/read_stream.c index b6fce4e7cc6..213f6206ba2 100644 --- a/src/backend/storage/aio/read_stream.c +++ b/src/backend/storage/aio/read_stream.c @@ -776,6 +776,16 @@ read_stream_begin_impl(int flags, uint32 max_possible_buffer_limit; Oid tablespace_id; + /* + * Reject attempts to read non-local temporary relations; we would be + * likely to get wrong data since we have no visibility into the owning + * session's local buffers. + */ + if (rel && RELATION_IS_OTHER_TEMP(rel)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot access temporary relations of other sessions"))); + /* * Decide how many I/Os we will allow to run at the same time. That * currently means advice to the kernel to tell it that we will soon read. diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 3cc0b0bdd92..70353f1f8e3 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -795,7 +795,7 @@ PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum) if (RELATION_IS_OTHER_TEMP(reln)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot access temporary tables of other sessions"))); + errmsg("cannot access temporary relations of other sessions"))); /* pass it off to localbuf.c */ return PrefetchLocalBuffer(RelationGetSmgr(reln), forkNum, blockNum); @@ -936,7 +936,7 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, if (RELATION_IS_OTHER_TEMP(reln)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot access temporary tables of other sessions"))); + errmsg("cannot access temporary relations of other sessions"))); /* * Read the buffer, and update pgstat counters to reflect a cache hit or @@ -1292,6 +1292,12 @@ ReadBuffer_common(Relation rel, SMgrRelation smgr, char smgr_persistence, int flags; char persistence; + /* see comments in ReadBufferExtended */ + if (rel && RELATION_IS_OTHER_TEMP(rel)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot access temporary relations of other sessions"))); + /* * Backward compatibility path, most code should use ExtendBufferedRel() * instead, as acquiring the extension lock inside ExtendBufferedRel() @@ -1382,6 +1388,12 @@ StartReadBuffersImpl(ReadBuffersOperation *operation, Assert(*nblocks > 0); Assert(*nblocks <= MAX_IO_COMBINE_LIMIT); + /* see comments in ReadBufferExtended */ + if (operation->rel && RELATION_IS_OTHER_TEMP(operation->rel)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot access temporary relations of other sessions"))); + if (operation->persistence == RELPERSISTENCE_TEMP) { io_context = IOCONTEXT_NORMAL; -- 2.43.0
From 91a6caa33c68afd73e0418972d5a582fe6fd7bdf Mon Sep 17 00:00:00 2001 From: Daniil Davidov <[email protected]> Date: Fri, 10 Apr 2026 22:21:23 +0700 Subject: [PATCH v17 2/2] Test cross session access on temporary tables --- src/test/modules/test_misc/meson.build | 1 + .../test_misc/t/012_temp_obj_multisession.pl | 109 ++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 src/test/modules/test_misc/t/012_temp_obj_multisession.pl diff --git a/src/test/modules/test_misc/meson.build b/src/test/modules/test_misc/meson.build index 1b25d98f7f3..a54599cc301 100644 --- a/src/test/modules/test_misc/meson.build +++ b/src/test/modules/test_misc/meson.build @@ -20,6 +20,7 @@ tests += { 't/009_log_temp_files.pl', 't/010_index_concurrently_upsert.pl', 't/011_lock_stats.pl', + 't/012_temp_obj_multisession.pl', ], # The injection points are cluster-wide, so disable installcheck 'runningcheck': false, diff --git a/src/test/modules/test_misc/t/012_temp_obj_multisession.pl b/src/test/modules/test_misc/t/012_temp_obj_multisession.pl new file mode 100644 index 00000000000..fa8037e4d1e --- /dev/null +++ b/src/test/modules/test_misc/t/012_temp_obj_multisession.pl @@ -0,0 +1,109 @@ +# Copyright (c) 2026, PostgreSQL Global Development Group + +use strict; +use warnings; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use PostgreSQL::Test::BackgroundPsql; +use Test::More; + +# Set up a fresh node +my $node = PostgreSQL::Test::Cluster->new('temp_lock'); +$node->init; +$node->start; + +# Create a long-lived session +my $psql1 = $node->background_psql('postgres'); + +$psql1->query_safe(q(CREATE TEMP TABLE foo AS SELECT 42 AS val;)); + +$psql1->query_safe(q(CREATE INDEX ON foo(val);)); + +my $tempschema = $node->safe_psql( + 'postgres', + q{ + SELECT n.nspname + FROM pg_class c + JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE relname = 'foo' AND relpersistence = 't'; + } +); +chomp $tempschema; +ok($tempschema =~ /^pg_temp_\d+$/, "got temp schema: $tempschema"); + + +# SELECT TEMPORARY TABLE from other session +my ($stdout, $stderr); +$node->psql( + 'postgres', + "SELECT val FROM $tempschema.foo;", + stdout => \$stdout, + stderr => \$stderr); +like( + $stderr, + qr/cannot access temporary relations of other sessions/, + 'SELECT on other session temp table is not allowed'); + +# UPDATE TEMPORARY TABLE from other session +$node->psql( + 'postgres', + "UPDATE $tempschema.foo SET val = NULL;", + stderr => \$stderr); +like( + $stderr, + qr/cannot access temporary relations of other sessions/, + 'UPDATE on other session temp table is not allowed'); + +# DELETE records from TEMPORARY TABLE from other session +$node->psql('postgres', "DELETE FROM $tempschema.foo;", stderr => \$stderr); +like( + $stderr, + qr/cannot access temporary relations of other sessions/, + 'DELETE on other session temp table is not allowed'); + +# TRUNCATE TEMPORARY TABLE from other session +$node->psql('postgres', "TRUNCATE TABLE $tempschema.foo;", + stderr => \$stderr); +like( + $stderr, + qr/cannot truncate temporary tables of other sessions/, + 'TRUNCATE on other session temp table is not allowed'); + +# INSERT INTO TEMPORARY TABLE from other session +$node->psql( + 'postgres', + "INSERT INTO $tempschema.foo VALUES (73);", + stderr => \$stderr); +like( + $stderr, + qr/cannot access temporary relations of other sessions/, + 'INSERT INTO on other session temp table is not allowed'); + +# COPY TEMPORARY TABLE from other session +$node->psql('postgres', "COPY $tempschema.foo TO STDOUT;", + stderr => \$stderr); +like( + $stderr, + qr/cannot access temporary relations of other sessions/, + 'COPY on other session temp table is blocked'); + +# Index scans can use a different code path from the one sequential scans are +# following. Make sure that we cannot access other sessions' temp tables during +# index scan either. +$node->psql( + 'postgres', + "SET enable_seqscan = off; SELECT val FROM $tempschema.foo WHERE val = 42;", + stderr => \$stderr); +like( + $stderr, + qr/cannot access temporary relations of other sessions/, + 'index scan on other session temp table is not allowed (exercises ReadBufferExtended path)' +); + +# DROP TEMPORARY TABLE from other session +$node->safe_psql('postgres', "DROP TABLE $tempschema.foo;"); + +# Clean up +$psql1->quit; + +done_testing(); -- 2.43.0
