diff options
Diffstat (limited to 'meta-oe/recipes-dbs/postgresql/files/CVE-2022-1552.patch')
-rw-r--r-- | meta-oe/recipes-dbs/postgresql/files/CVE-2022-1552.patch | 947 |
1 files changed, 0 insertions, 947 deletions
diff --git a/meta-oe/recipes-dbs/postgresql/files/CVE-2022-1552.patch b/meta-oe/recipes-dbs/postgresql/files/CVE-2022-1552.patch deleted file mode 100644 index 6f0d5ac06f..0000000000 --- a/meta-oe/recipes-dbs/postgresql/files/CVE-2022-1552.patch +++ /dev/null @@ -1,947 +0,0 @@ -From 31eefa1efc8eecb6ab91c8835d2952d44a3b1ae1 Mon Sep 17 00:00:00 2001 -From: Hitendra Prajapati <hprajapati@mvista.com> -Date: Thu, 22 Sep 2022 11:20:41 +0530 -Subject: [PATCH] CVE-2022-1552 - -Upstream-Status: Backport [https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=ab49ce7c3414ac19e4afb386d7843ce2d2fb8bda && https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=677a494789062ca88e0142a17bedd5415f6ab0aa] - -CVE: CVE-2022-1552 -Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> ---- - contrib/amcheck/expected/check_btree.out | 23 ++++++ - contrib/amcheck/sql/check_btree.sql | 21 +++++ - contrib/amcheck/verify_nbtree.c | 27 +++++++ - src/backend/access/brin/brin.c | 29 ++++++- - src/backend/catalog/index.c | 65 ++++++++++++---- - src/backend/commands/cluster.c | 37 ++++++--- - src/backend/commands/indexcmds.c | 98 ++++++++++++++++++++---- - src/backend/commands/matview.c | 30 +++----- - src/backend/utils/init/miscinit.c | 24 +++--- - src/test/regress/expected/privileges.out | 71 +++++++++++++++++ - src/test/regress/sql/privileges.sql | 64 ++++++++++++++++ - 11 files changed, 422 insertions(+), 67 deletions(-) - -diff --git a/contrib/amcheck/expected/check_btree.out b/contrib/amcheck/expected/check_btree.out -index 59a805d..0fd6ea0 100644 ---- a/contrib/amcheck/expected/check_btree.out -+++ b/contrib/amcheck/expected/check_btree.out -@@ -168,11 +168,34 @@ SELECT bt_index_check('toasty', true); - - (1 row) - -+-- -+-- Check that index expressions and predicates are run as the table's owner -+-- -+TRUNCATE bttest_a; -+INSERT INTO bttest_a SELECT * FROM generate_series(1, 1000); -+ALTER TABLE bttest_a OWNER TO regress_bttest_role; -+-- A dummy index function checking current_user -+CREATE FUNCTION ifun(int8) RETURNS int8 AS $$ -+BEGIN -+ ASSERT current_user = 'regress_bttest_role', -+ format('ifun(%s) called by %s', $1, current_user); -+ RETURN $1; -+END; -+$$ LANGUAGE plpgsql IMMUTABLE; -+CREATE INDEX bttest_a_expr_idx ON bttest_a ((ifun(id) + ifun(0))) -+ WHERE ifun(id + 10) > ifun(10); -+SELECT bt_index_check('bttest_a_expr_idx', true); -+ bt_index_check -+---------------- -+ -+(1 row) -+ - -- cleanup - DROP TABLE bttest_a; - DROP TABLE bttest_b; - DROP TABLE bttest_multi; - DROP TABLE delete_test_table; - DROP TABLE toast_bug; -+DROP FUNCTION ifun(int8); - DROP OWNED BY regress_bttest_role; -- permissions - DROP ROLE regress_bttest_role; -diff --git a/contrib/amcheck/sql/check_btree.sql b/contrib/amcheck/sql/check_btree.sql -index 99acbc8..3248187 100644 ---- a/contrib/amcheck/sql/check_btree.sql -+++ b/contrib/amcheck/sql/check_btree.sql -@@ -110,11 +110,32 @@ INSERT INTO toast_bug SELECT repeat('a', 2200); - -- Should not get false positive report of corruption: - SELECT bt_index_check('toasty', true); - -+-- -+-- Check that index expressions and predicates are run as the table's owner -+-- -+TRUNCATE bttest_a; -+INSERT INTO bttest_a SELECT * FROM generate_series(1, 1000); -+ALTER TABLE bttest_a OWNER TO regress_bttest_role; -+-- A dummy index function checking current_user -+CREATE FUNCTION ifun(int8) RETURNS int8 AS $$ -+BEGIN -+ ASSERT current_user = 'regress_bttest_role', -+ format('ifun(%s) called by %s', $1, current_user); -+ RETURN $1; -+END; -+$$ LANGUAGE plpgsql IMMUTABLE; -+ -+CREATE INDEX bttest_a_expr_idx ON bttest_a ((ifun(id) + ifun(0))) -+ WHERE ifun(id + 10) > ifun(10); -+ -+SELECT bt_index_check('bttest_a_expr_idx', true); -+ - -- cleanup - DROP TABLE bttest_a; - DROP TABLE bttest_b; - DROP TABLE bttest_multi; - DROP TABLE delete_test_table; - DROP TABLE toast_bug; -+DROP FUNCTION ifun(int8); - DROP OWNED BY regress_bttest_role; -- permissions - DROP ROLE regress_bttest_role; -diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c -index 700a02f..cb6475d 100644 ---- a/contrib/amcheck/verify_nbtree.c -+++ b/contrib/amcheck/verify_nbtree.c -@@ -228,6 +228,9 @@ bt_index_check_internal(Oid indrelid, bool parentcheck, bool heapallindexed, - Relation indrel; - Relation heaprel; - LOCKMODE lockmode; -+ Oid save_userid; -+ int save_sec_context; -+ int save_nestlevel; - - if (parentcheck) - lockmode = ShareLock; -@@ -244,9 +247,27 @@ bt_index_check_internal(Oid indrelid, bool parentcheck, bool heapallindexed, - */ - heapid = IndexGetRelation(indrelid, true); - if (OidIsValid(heapid)) -+ { - heaprel = table_open(heapid, lockmode); -+ -+ /* -+ * Switch to the table owner's userid, so that any index functions are -+ * run as that user. Also lock down security-restricted operations -+ * and arrange to make GUC variable changes local to this command. -+ */ -+ GetUserIdAndSecContext(&save_userid, &save_sec_context); -+ SetUserIdAndSecContext(heaprel->rd_rel->relowner, -+ save_sec_context | SECURITY_RESTRICTED_OPERATION); -+ save_nestlevel = NewGUCNestLevel(); -+ } - else -+ { - heaprel = NULL; -+ /* for "gcc -Og" https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78394 */ -+ save_userid = InvalidOid; -+ save_sec_context = -1; -+ save_nestlevel = -1; -+ } - - /* - * Open the target index relations separately (like relation_openrv(), but -@@ -293,6 +314,12 @@ bt_index_check_internal(Oid indrelid, bool parentcheck, bool heapallindexed, - heapallindexed, rootdescend); - } - -+ /* Roll back any GUC changes executed by index functions */ -+ AtEOXact_GUC(false, save_nestlevel); -+ -+ /* Restore userid and security context */ -+ SetUserIdAndSecContext(save_userid, save_sec_context); -+ - /* - * Release locks early. That's ok here because nothing in the called - * routines will trigger shared cache invalidations to be sent, so we can -diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c -index c7b403b..781cac2 100644 ---- a/src/backend/access/brin/brin.c -+++ b/src/backend/access/brin/brin.c -@@ -873,6 +873,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) - Oid heapoid; - Relation indexRel; - Relation heapRel; -+ Oid save_userid; -+ int save_sec_context; -+ int save_nestlevel; - double numSummarized = 0; - - if (RecoveryInProgress()) -@@ -899,7 +902,22 @@ brin_summarize_range(PG_FUNCTION_ARGS) - */ - heapoid = IndexGetRelation(indexoid, true); - if (OidIsValid(heapoid)) -+ { - heapRel = table_open(heapoid, ShareUpdateExclusiveLock); -+ -+ /* -+ * Autovacuum calls us. For its benefit, switch to the table owner's -+ * userid, so that any index functions are run as that user. Also -+ * lock down security-restricted operations and arrange to make GUC -+ * variable changes local to this command. This is harmless, albeit -+ * unnecessary, when called from SQL, because we fail shortly if the -+ * user does not own the index. -+ */ -+ GetUserIdAndSecContext(&save_userid, &save_sec_context); -+ SetUserIdAndSecContext(heapRel->rd_rel->relowner, -+ save_sec_context | SECURITY_RESTRICTED_OPERATION); -+ save_nestlevel = NewGUCNestLevel(); -+ } - else - heapRel = NULL; - -@@ -914,7 +932,7 @@ brin_summarize_range(PG_FUNCTION_ARGS) - RelationGetRelationName(indexRel)))); - - /* User must own the index (comparable to privileges needed for VACUUM) */ -- if (!pg_class_ownercheck(indexoid, GetUserId())) -+ if (heapRel != NULL && !pg_class_ownercheck(indexoid, save_userid)) - aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, - RelationGetRelationName(indexRel)); - -@@ -932,6 +950,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) - /* OK, do it */ - brinsummarize(indexRel, heapRel, heapBlk, true, &numSummarized, NULL); - -+ /* Roll back any GUC changes executed by index functions */ -+ AtEOXact_GUC(false, save_nestlevel); -+ -+ /* Restore userid and security context */ -+ SetUserIdAndSecContext(save_userid, save_sec_context); -+ - relation_close(indexRel, ShareUpdateExclusiveLock); - relation_close(heapRel, ShareUpdateExclusiveLock); - -@@ -973,6 +997,9 @@ brin_desummarize_range(PG_FUNCTION_ARGS) - * passed indexoid isn't an index then IndexGetRelation() will fail. - * Rather than emitting a not-very-helpful error message, postpone - * complaining, expecting that the is-it-an-index test below will fail. -+ * -+ * Unlike brin_summarize_range(), autovacuum never calls this. Hence, we -+ * don't switch userid. - */ - heapoid = IndexGetRelation(indexoid, true); - if (OidIsValid(heapoid)) -diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c -index 3ece136..0333bfd 100644 ---- a/src/backend/catalog/index.c -+++ b/src/backend/catalog/index.c -@@ -1400,6 +1400,9 @@ index_concurrently_build(Oid heapRelationId, - Oid indexRelationId) - { - Relation heapRel; -+ Oid save_userid; -+ int save_sec_context; -+ int save_nestlevel; - Relation indexRelation; - IndexInfo *indexInfo; - -@@ -1409,7 +1412,16 @@ index_concurrently_build(Oid heapRelationId, - /* Open and lock the parent heap relation */ - heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); - -- /* And the target index relation */ -+ /* -+ * Switch to the table owner's userid, so that any index functions are run -+ * as that user. Also lock down security-restricted operations and -+ * arrange to make GUC variable changes local to this command. -+ */ -+ GetUserIdAndSecContext(&save_userid, &save_sec_context); -+ SetUserIdAndSecContext(heapRel->rd_rel->relowner, -+ save_sec_context | SECURITY_RESTRICTED_OPERATION); -+ save_nestlevel = NewGUCNestLevel(); -+ - indexRelation = index_open(indexRelationId, RowExclusiveLock); - - /* -@@ -1425,6 +1437,12 @@ index_concurrently_build(Oid heapRelationId, - /* Now build the index */ - index_build(heapRel, indexRelation, indexInfo, false, true); - -+ /* Roll back any GUC changes executed by index functions */ -+ AtEOXact_GUC(false, save_nestlevel); -+ -+ /* Restore userid and security context */ -+ SetUserIdAndSecContext(save_userid, save_sec_context); -+ - /* Close both the relations, but keep the locks */ - table_close(heapRel, NoLock); - index_close(indexRelation, NoLock); -@@ -3271,7 +3289,17 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) - - /* Open and lock the parent heap relation */ - heapRelation = table_open(heapId, ShareUpdateExclusiveLock); -- /* And the target index relation */ -+ -+ /* -+ * Switch to the table owner's userid, so that any index functions are run -+ * as that user. Also lock down security-restricted operations and -+ * arrange to make GUC variable changes local to this command. -+ */ -+ GetUserIdAndSecContext(&save_userid, &save_sec_context); -+ SetUserIdAndSecContext(heapRelation->rd_rel->relowner, -+ save_sec_context | SECURITY_RESTRICTED_OPERATION); -+ save_nestlevel = NewGUCNestLevel(); -+ - indexRelation = index_open(indexId, RowExclusiveLock); - - /* -@@ -3284,16 +3312,6 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) - /* mark build is concurrent just for consistency */ - indexInfo->ii_Concurrent = true; - -- /* -- * Switch to the table owner's userid, so that any index functions are run -- * as that user. Also lock down security-restricted operations and -- * arrange to make GUC variable changes local to this command. -- */ -- GetUserIdAndSecContext(&save_userid, &save_sec_context); -- SetUserIdAndSecContext(heapRelation->rd_rel->relowner, -- save_sec_context | SECURITY_RESTRICTED_OPERATION); -- save_nestlevel = NewGUCNestLevel(); -- - /* - * Scan the index and gather up all the TIDs into a tuplesort object. - */ -@@ -3497,6 +3515,9 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence, - Relation iRel, - heapRelation; - Oid heapId; -+ Oid save_userid; -+ int save_sec_context; -+ int save_nestlevel; - IndexInfo *indexInfo; - volatile bool skipped_constraint = false; - PGRUsage ru0; -@@ -3527,6 +3548,16 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence, - */ - iRel = index_open(indexId, AccessExclusiveLock); - -+ /* -+ * Switch to the table owner's userid, so that any index functions are run -+ * as that user. Also lock down security-restricted operations and -+ * arrange to make GUC variable changes local to this command. -+ */ -+ GetUserIdAndSecContext(&save_userid, &save_sec_context); -+ SetUserIdAndSecContext(heapRelation->rd_rel->relowner, -+ save_sec_context | SECURITY_RESTRICTED_OPERATION); -+ save_nestlevel = NewGUCNestLevel(); -+ - if (progress) - pgstat_progress_update_param(PROGRESS_CREATEIDX_ACCESS_METHOD_OID, - iRel->rd_rel->relam); -@@ -3684,12 +3715,18 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence, - errdetail_internal("%s", - pg_rusage_show(&ru0)))); - -- if (progress) -- pgstat_progress_end_command(); -+ /* Roll back any GUC changes executed by index functions */ -+ AtEOXact_GUC(false, save_nestlevel); -+ -+ /* Restore userid and security context */ -+ SetUserIdAndSecContext(save_userid, save_sec_context); - - /* Close rels, but keep locks */ - index_close(iRel, NoLock); - table_close(heapRelation, NoLock); -+ -+ if (progress) -+ pgstat_progress_end_command(); - } - - /* -diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c -index bd6f408..74db03e 100644 ---- a/src/backend/commands/cluster.c -+++ b/src/backend/commands/cluster.c -@@ -266,6 +266,9 @@ void - cluster_rel(Oid tableOid, Oid indexOid, int options) - { - Relation OldHeap; -+ Oid save_userid; -+ int save_sec_context; -+ int save_nestlevel; - bool verbose = ((options & CLUOPT_VERBOSE) != 0); - bool recheck = ((options & CLUOPT_RECHECK) != 0); - -@@ -295,6 +298,16 @@ cluster_rel(Oid tableOid, Oid indexOid, int options) - return; - } - -+ /* -+ * Switch to the table owner's userid, so that any index functions are run -+ * as that user. Also lock down security-restricted operations and -+ * arrange to make GUC variable changes local to this command. -+ */ -+ GetUserIdAndSecContext(&save_userid, &save_sec_context); -+ SetUserIdAndSecContext(OldHeap->rd_rel->relowner, -+ save_sec_context | SECURITY_RESTRICTED_OPERATION); -+ save_nestlevel = NewGUCNestLevel(); -+ - /* - * Since we may open a new transaction for each relation, we have to check - * that the relation still is what we think it is. -@@ -309,11 +322,10 @@ cluster_rel(Oid tableOid, Oid indexOid, int options) - Form_pg_index indexForm; - - /* Check that the user still owns the relation */ -- if (!pg_class_ownercheck(tableOid, GetUserId())) -+ if (!pg_class_ownercheck(tableOid, save_userid)) - { - relation_close(OldHeap, AccessExclusiveLock); -- pgstat_progress_end_command(); -- return; -+ goto out; - } - - /* -@@ -327,8 +339,7 @@ cluster_rel(Oid tableOid, Oid indexOid, int options) - if (RELATION_IS_OTHER_TEMP(OldHeap)) - { - relation_close(OldHeap, AccessExclusiveLock); -- pgstat_progress_end_command(); -- return; -+ goto out; - } - - if (OidIsValid(indexOid)) -@@ -339,8 +350,7 @@ cluster_rel(Oid tableOid, Oid indexOid, int options) - if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(indexOid))) - { - relation_close(OldHeap, AccessExclusiveLock); -- pgstat_progress_end_command(); -- return; -+ goto out; - } - - /* -@@ -350,8 +360,7 @@ cluster_rel(Oid tableOid, Oid indexOid, int options) - if (!HeapTupleIsValid(tuple)) /* probably can't happen */ - { - relation_close(OldHeap, AccessExclusiveLock); -- pgstat_progress_end_command(); -- return; -+ goto out; - } - indexForm = (Form_pg_index) GETSTRUCT(tuple); - if (!indexForm->indisclustered) -@@ -413,8 +422,7 @@ cluster_rel(Oid tableOid, Oid indexOid, int options) - !RelationIsPopulated(OldHeap)) - { - relation_close(OldHeap, AccessExclusiveLock); -- pgstat_progress_end_command(); -- return; -+ goto out; - } - - /* -@@ -430,6 +438,13 @@ cluster_rel(Oid tableOid, Oid indexOid, int options) - - /* NB: rebuild_relation does table_close() on OldHeap */ - -+out: -+ /* Roll back any GUC changes executed by index functions */ -+ AtEOXact_GUC(false, save_nestlevel); -+ -+ /* Restore userid and security context */ -+ SetUserIdAndSecContext(save_userid, save_sec_context); -+ - pgstat_progress_end_command(); - } - -diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c -index be1cf8c..167b377 100644 ---- a/src/backend/commands/indexcmds.c -+++ b/src/backend/commands/indexcmds.c -@@ -470,21 +470,22 @@ DefineIndex(Oid relationId, - LOCKTAG heaplocktag; - LOCKMODE lockmode; - Snapshot snapshot; -- int save_nestlevel = -1; -+ Oid root_save_userid; -+ int root_save_sec_context; -+ int root_save_nestlevel; - int i; - -+ root_save_nestlevel = NewGUCNestLevel(); -+ - /* - * Some callers need us to run with an empty default_tablespace; this is a - * necessary hack to be able to reproduce catalog state accurately when - * recreating indexes after table-rewriting ALTER TABLE. - */ - if (stmt->reset_default_tblspc) -- { -- save_nestlevel = NewGUCNestLevel(); - (void) set_config_option("default_tablespace", "", - PGC_USERSET, PGC_S_SESSION, - GUC_ACTION_SAVE, true, 0, false); -- } - - /* - * Force non-concurrent build on temporary relations, even if CONCURRENTLY -@@ -563,6 +564,15 @@ DefineIndex(Oid relationId, - lockmode = concurrent ? ShareUpdateExclusiveLock : ShareLock; - rel = table_open(relationId, lockmode); - -+ /* -+ * Switch to the table owner's userid, so that any index functions are run -+ * as that user. Also lock down security-restricted operations. We -+ * already arranged to make GUC variable changes local to this command. -+ */ -+ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); -+ SetUserIdAndSecContext(rel->rd_rel->relowner, -+ root_save_sec_context | SECURITY_RESTRICTED_OPERATION); -+ - namespaceId = RelationGetNamespace(rel); - - /* Ensure that it makes sense to index this kind of relation */ -@@ -648,7 +658,7 @@ DefineIndex(Oid relationId, - { - AclResult aclresult; - -- aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), -+ aclresult = pg_namespace_aclcheck(namespaceId, root_save_userid, - ACL_CREATE); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, OBJECT_SCHEMA, -@@ -680,7 +690,7 @@ DefineIndex(Oid relationId, - { - AclResult aclresult; - -- aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(), -+ aclresult = pg_tablespace_aclcheck(tablespaceId, root_save_userid, - ACL_CREATE); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, OBJECT_TABLESPACE, -@@ -1066,15 +1076,17 @@ DefineIndex(Oid relationId, - - ObjectAddressSet(address, RelationRelationId, indexRelationId); - -- /* -- * Revert to original default_tablespace. Must do this before any return -- * from this function, but after index_create, so this is a good time. -- */ -- if (save_nestlevel >= 0) -- AtEOXact_GUC(true, save_nestlevel); -- - if (!OidIsValid(indexRelationId)) - { -+ /* -+ * Roll back any GUC changes executed by index functions. Also revert -+ * to original default_tablespace if we changed it above. -+ */ -+ AtEOXact_GUC(false, root_save_nestlevel); -+ -+ /* Restore userid and security context */ -+ SetUserIdAndSecContext(root_save_userid, root_save_sec_context); -+ - table_close(rel, NoLock); - - /* If this is the top-level index, we're done */ -@@ -1084,6 +1096,17 @@ DefineIndex(Oid relationId, - return address; - } - -+ /* -+ * Roll back any GUC changes executed by index functions, and keep -+ * subsequent changes local to this command. It's barely possible that -+ * some index function changed a behavior-affecting GUC, e.g. xmloption, -+ * that affects subsequent steps. This improves bug-compatibility with -+ * older PostgreSQL versions. They did the AtEOXact_GUC() here for the -+ * purpose of clearing the above default_tablespace change. -+ */ -+ AtEOXact_GUC(false, root_save_nestlevel); -+ root_save_nestlevel = NewGUCNestLevel(); -+ - /* Add any requested comment */ - if (stmt->idxcomment != NULL) - CreateComments(indexRelationId, RelationRelationId, 0, -@@ -1130,6 +1153,9 @@ DefineIndex(Oid relationId, - { - Oid childRelid = part_oids[i]; - Relation childrel; -+ Oid child_save_userid; -+ int child_save_sec_context; -+ int child_save_nestlevel; - List *childidxs; - ListCell *cell; - AttrNumber *attmap; -@@ -1138,6 +1164,12 @@ DefineIndex(Oid relationId, - - childrel = table_open(childRelid, lockmode); - -+ GetUserIdAndSecContext(&child_save_userid, -+ &child_save_sec_context); -+ SetUserIdAndSecContext(childrel->rd_rel->relowner, -+ child_save_sec_context | SECURITY_RESTRICTED_OPERATION); -+ child_save_nestlevel = NewGUCNestLevel(); -+ - /* - * Don't try to create indexes on foreign tables, though. Skip - * those if a regular index, or fail if trying to create a -@@ -1153,6 +1185,9 @@ DefineIndex(Oid relationId, - errdetail("Table \"%s\" contains partitions that are foreign tables.", - RelationGetRelationName(rel)))); - -+ AtEOXact_GUC(false, child_save_nestlevel); -+ SetUserIdAndSecContext(child_save_userid, -+ child_save_sec_context); - table_close(childrel, lockmode); - continue; - } -@@ -1226,6 +1261,9 @@ DefineIndex(Oid relationId, - } - - list_free(childidxs); -+ AtEOXact_GUC(false, child_save_nestlevel); -+ SetUserIdAndSecContext(child_save_userid, -+ child_save_sec_context); - table_close(childrel, NoLock); - - /* -@@ -1280,12 +1318,21 @@ DefineIndex(Oid relationId, - if (found_whole_row) - elog(ERROR, "cannot convert whole-row table reference"); - -+ /* -+ * Recurse as the starting user ID. Callee will use that -+ * for permission checks, then switch again. -+ */ -+ Assert(GetUserId() == child_save_userid); -+ SetUserIdAndSecContext(root_save_userid, -+ root_save_sec_context); - DefineIndex(childRelid, childStmt, - InvalidOid, /* no predefined OID */ - indexRelationId, /* this is our child */ - createdConstraintId, - is_alter_table, check_rights, check_not_in_use, - skip_build, quiet); -+ SetUserIdAndSecContext(child_save_userid, -+ child_save_sec_context); - } - - pgstat_progress_update_param(PROGRESS_CREATEIDX_PARTITIONS_DONE, -@@ -1322,12 +1369,17 @@ DefineIndex(Oid relationId, - * Indexes on partitioned tables are not themselves built, so we're - * done here. - */ -+ AtEOXact_GUC(false, root_save_nestlevel); -+ SetUserIdAndSecContext(root_save_userid, root_save_sec_context); - table_close(rel, NoLock); - if (!OidIsValid(parentIndexId)) - pgstat_progress_end_command(); - return address; - } - -+ AtEOXact_GUC(false, root_save_nestlevel); -+ SetUserIdAndSecContext(root_save_userid, root_save_sec_context); -+ - if (!concurrent) - { - /* Close the heap and we're done, in the non-concurrent case */ -@@ -3040,6 +3092,9 @@ ReindexRelationConcurrently(Oid relationOid, int options) - Oid newIndexId; - Relation indexRel; - Relation heapRel; -+ Oid save_userid; -+ int save_sec_context; -+ int save_nestlevel; - Relation newIndexRel; - LockRelId *lockrelid; - -@@ -3047,6 +3102,16 @@ ReindexRelationConcurrently(Oid relationOid, int options) - heapRel = table_open(indexRel->rd_index->indrelid, - ShareUpdateExclusiveLock); - -+ /* -+ * Switch to the table owner's userid, so that any index functions are -+ * run as that user. Also lock down security-restricted operations -+ * and arrange to make GUC variable changes local to this command. -+ */ -+ GetUserIdAndSecContext(&save_userid, &save_sec_context); -+ SetUserIdAndSecContext(heapRel->rd_rel->relowner, -+ save_sec_context | SECURITY_RESTRICTED_OPERATION); -+ save_nestlevel = NewGUCNestLevel(); -+ - /* This function shouldn't be called for temporary relations. */ - if (indexRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP) - elog(ERROR, "cannot reindex a temporary table concurrently"); -@@ -3101,6 +3166,13 @@ ReindexRelationConcurrently(Oid relationOid, int options) - - index_close(indexRel, NoLock); - index_close(newIndexRel, NoLock); -+ -+ /* Roll back any GUC changes executed by index functions */ -+ AtEOXact_GUC(false, save_nestlevel); -+ -+ /* Restore userid and security context */ -+ SetUserIdAndSecContext(save_userid, save_sec_context); -+ - table_close(heapRel, NoLock); - } - -diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c -index 80e9ec0..e485661 100644 ---- a/src/backend/commands/matview.c -+++ b/src/backend/commands/matview.c -@@ -167,6 +167,17 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString, - lockmode, 0, - RangeVarCallbackOwnsTable, NULL); - matviewRel = table_open(matviewOid, NoLock); -+ relowner = matviewRel->rd_rel->relowner; -+ -+ /* -+ * Switch to the owner's userid, so that any functions are run as that -+ * user. Also lock down security-restricted operations and arrange to -+ * make GUC variable changes local to this command. -+ */ -+ GetUserIdAndSecContext(&save_userid, &save_sec_context); -+ SetUserIdAndSecContext(relowner, -+ save_sec_context | SECURITY_RESTRICTED_OPERATION); -+ save_nestlevel = NewGUCNestLevel(); - - /* Make sure it is a materialized view. */ - if (matviewRel->rd_rel->relkind != RELKIND_MATVIEW) -@@ -268,19 +279,6 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString, - */ - SetMatViewPopulatedState(matviewRel, !stmt->skipData); - -- relowner = matviewRel->rd_rel->relowner; -- -- /* -- * Switch to the owner's userid, so that any functions are run as that -- * user. Also arrange to make GUC variable changes local to this command. -- * Don't lock it down too tight to create a temporary table just yet. We -- * will switch modes when we are about to execute user code. -- */ -- GetUserIdAndSecContext(&save_userid, &save_sec_context); -- SetUserIdAndSecContext(relowner, -- save_sec_context | SECURITY_LOCAL_USERID_CHANGE); -- save_nestlevel = NewGUCNestLevel(); -- - /* Concurrent refresh builds new data in temp tablespace, and does diff. */ - if (concurrent) - { -@@ -303,12 +301,6 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString, - LockRelationOid(OIDNewHeap, AccessExclusiveLock); - dest = CreateTransientRelDestReceiver(OIDNewHeap); - -- /* -- * Now lock down security-restricted operations. -- */ -- SetUserIdAndSecContext(relowner, -- save_sec_context | SECURITY_RESTRICTED_OPERATION); -- - /* Generate the data, if wanted. */ - if (!stmt->skipData) - processed = refresh_matview_datafill(dest, dataQuery, queryString); -diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c -index de554e2..c9f858e 100644 ---- a/src/backend/utils/init/miscinit.c -+++ b/src/backend/utils/init/miscinit.c -@@ -455,15 +455,21 @@ GetAuthenticatedUserId(void) - * with guc.c's internal state, so SET ROLE has to be disallowed. - * - * SECURITY_RESTRICTED_OPERATION indicates that we are inside an operation -- * that does not wish to trust called user-defined functions at all. This -- * bit prevents not only SET ROLE, but various other changes of session state -- * that normally is unprotected but might possibly be used to subvert the -- * calling session later. An example is replacing an existing prepared -- * statement with new code, which will then be executed with the outer -- * session's permissions when the prepared statement is next used. Since -- * these restrictions are fairly draconian, we apply them only in contexts -- * where the called functions are really supposed to be side-effect-free -- * anyway, such as VACUUM/ANALYZE/REINDEX. -+ * that does not wish to trust called user-defined functions at all. The -+ * policy is to use this before operations, e.g. autovacuum and REINDEX, that -+ * enumerate relations of a database or schema and run functions associated -+ * with each found relation. The relation owner is the new user ID. Set this -+ * as soon as possible after locking the relation. Restore the old user ID as -+ * late as possible before closing the relation; restoring it shortly after -+ * close is also tolerable. If a command has both relation-enumerating and -+ * non-enumerating modes, e.g. ANALYZE, both modes set this bit. This bit -+ * prevents not only SET ROLE, but various other changes of session state that -+ * normally is unprotected but might possibly be used to subvert the calling -+ * session later. An example is replacing an existing prepared statement with -+ * new code, which will then be executed with the outer session's permissions -+ * when the prepared statement is next used. These restrictions are fairly -+ * draconian, but the functions called in relation-enumerating operations are -+ * really supposed to be side-effect-free anyway. - * - * SECURITY_NOFORCE_RLS indicates that we are inside an operation which should - * ignore the FORCE ROW LEVEL SECURITY per-table indication. This is used to -diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out -index 186d2fb..0f0c1b3 100644 ---- a/src/test/regress/expected/privileges.out -+++ b/src/test/regress/expected/privileges.out -@@ -1336,6 +1336,61 @@ SELECT has_table_privilege('regress_priv_user1', 'atest4', 'SELECT WITH GRANT OP - -- security-restricted operations - \c - - CREATE ROLE regress_sro_user; -+-- Check that index expressions and predicates are run as the table's owner -+-- A dummy index function checking current_user -+CREATE FUNCTION sro_ifun(int) RETURNS int AS $$ -+BEGIN -+ -- Below we set the table's owner to regress_sro_user -+ ASSERT current_user = 'regress_sro_user', -+ format('sro_ifun(%s) called by %s', $1, current_user); -+ RETURN $1; -+END; -+$$ LANGUAGE plpgsql IMMUTABLE; -+-- Create a table owned by regress_sro_user -+CREATE TABLE sro_tab (a int); -+ALTER TABLE sro_tab OWNER TO regress_sro_user; -+INSERT INTO sro_tab VALUES (1), (2), (3); -+-- Create an expression index with a predicate -+CREATE INDEX sro_idx ON sro_tab ((sro_ifun(a) + sro_ifun(0))) -+ WHERE sro_ifun(a + 10) > sro_ifun(10); -+DROP INDEX sro_idx; -+-- Do the same concurrently -+CREATE INDEX CONCURRENTLY sro_idx ON sro_tab ((sro_ifun(a) + sro_ifun(0))) -+ WHERE sro_ifun(a + 10) > sro_ifun(10); -+-- REINDEX -+REINDEX TABLE sro_tab; -+REINDEX INDEX sro_idx; -+REINDEX TABLE CONCURRENTLY sro_tab; -+DROP INDEX sro_idx; -+-- CLUSTER -+CREATE INDEX sro_cluster_idx ON sro_tab ((sro_ifun(a) + sro_ifun(0))); -+CLUSTER sro_tab USING sro_cluster_idx; -+DROP INDEX sro_cluster_idx; -+-- BRIN index -+CREATE INDEX sro_brin ON sro_tab USING brin ((sro_ifun(a) + sro_ifun(0))); -+SELECT brin_desummarize_range('sro_brin', 0); -+ brin_desummarize_range -+------------------------ -+ -+(1 row) -+ -+SELECT brin_summarize_range('sro_brin', 0); -+ brin_summarize_range -+---------------------- -+ 1 -+(1 row) -+ -+DROP TABLE sro_tab; -+-- Check with a partitioned table -+CREATE TABLE sro_ptab (a int) PARTITION BY RANGE (a); -+ALTER TABLE sro_ptab OWNER TO regress_sro_user; -+CREATE TABLE sro_part PARTITION OF sro_ptab FOR VALUES FROM (1) TO (10); -+ALTER TABLE sro_part OWNER TO regress_sro_user; -+INSERT INTO sro_ptab VALUES (1), (2), (3); -+CREATE INDEX sro_pidx ON sro_ptab ((sro_ifun(a) + sro_ifun(0))) -+ WHERE sro_ifun(a + 10) > sro_ifun(10); -+REINDEX TABLE sro_ptab; -+REINDEX INDEX CONCURRENTLY sro_pidx; - SET SESSION AUTHORIZATION regress_sro_user; - CREATE FUNCTION unwanted_grant() RETURNS void LANGUAGE sql AS - 'GRANT regress_priv_group2 TO regress_sro_user'; -@@ -1373,6 +1428,22 @@ CONTEXT: SQL function "unwanted_grant" statement 1 - SQL statement "SELECT unwanted_grant()" - PL/pgSQL function sro_trojan() line 1 at PERFORM - SQL function "mv_action" statement 1 -+-- REFRESH MATERIALIZED VIEW CONCURRENTLY use of eval_const_expressions() -+SET SESSION AUTHORIZATION regress_sro_user; -+CREATE FUNCTION unwanted_grant_nofail(int) RETURNS int -+ IMMUTABLE LANGUAGE plpgsql AS $$ -+BEGIN -+ PERFORM unwanted_grant(); -+ RAISE WARNING 'owned'; -+ RETURN 1; -+EXCEPTION WHEN OTHERS THEN -+ RETURN 2; -+END$$; -+CREATE MATERIALIZED VIEW sro_index_mv AS SELECT 1 AS c; -+CREATE UNIQUE INDEX ON sro_index_mv (c) WHERE unwanted_grant_nofail(1) > 0; -+\c - -+REFRESH MATERIALIZED VIEW CONCURRENTLY sro_index_mv; -+REFRESH MATERIALIZED VIEW sro_index_mv; - DROP OWNED BY regress_sro_user; - DROP ROLE regress_sro_user; - -- Admin options -diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql -index 34fbf0e..c0b88a6 100644 ---- a/src/test/regress/sql/privileges.sql -+++ b/src/test/regress/sql/privileges.sql -@@ -826,6 +826,53 @@ SELECT has_table_privilege('regress_priv_user1', 'atest4', 'SELECT WITH GRANT OP - \c - - CREATE ROLE regress_sro_user; - -+-- Check that index expressions and predicates are run as the table's owner -+ -+-- A dummy index function checking current_user -+CREATE FUNCTION sro_ifun(int) RETURNS int AS $$ -+BEGIN -+ -- Below we set the table's owner to regress_sro_user -+ ASSERT current_user = 'regress_sro_user', -+ format('sro_ifun(%s) called by %s', $1, current_user); -+ RETURN $1; -+END; -+$$ LANGUAGE plpgsql IMMUTABLE; -+-- Create a table owned by regress_sro_user -+CREATE TABLE sro_tab (a int); -+ALTER TABLE sro_tab OWNER TO regress_sro_user; -+INSERT INTO sro_tab VALUES (1), (2), (3); -+-- Create an expression index with a predicate -+CREATE INDEX sro_idx ON sro_tab ((sro_ifun(a) + sro_ifun(0))) -+ WHERE sro_ifun(a + 10) > sro_ifun(10); -+DROP INDEX sro_idx; -+-- Do the same concurrently -+CREATE INDEX CONCURRENTLY sro_idx ON sro_tab ((sro_ifun(a) + sro_ifun(0))) -+ WHERE sro_ifun(a + 10) > sro_ifun(10); -+-- REINDEX -+REINDEX TABLE sro_tab; -+REINDEX INDEX sro_idx; -+REINDEX TABLE CONCURRENTLY sro_tab; -+DROP INDEX sro_idx; -+-- CLUSTER -+CREATE INDEX sro_cluster_idx ON sro_tab ((sro_ifun(a) + sro_ifun(0))); -+CLUSTER sro_tab USING sro_cluster_idx; -+DROP INDEX sro_cluster_idx; -+-- BRIN index -+CREATE INDEX sro_brin ON sro_tab USING brin ((sro_ifun(a) + sro_ifun(0))); -+SELECT brin_desummarize_range('sro_brin', 0); -+SELECT brin_summarize_range('sro_brin', 0); -+DROP TABLE sro_tab; -+-- Check with a partitioned table -+CREATE TABLE sro_ptab (a int) PARTITION BY RANGE (a); -+ALTER TABLE sro_ptab OWNER TO regress_sro_user; -+CREATE TABLE sro_part PARTITION OF sro_ptab FOR VALUES FROM (1) TO (10); -+ALTER TABLE sro_part OWNER TO regress_sro_user; -+INSERT INTO sro_ptab VALUES (1), (2), (3); -+CREATE INDEX sro_pidx ON sro_ptab ((sro_ifun(a) + sro_ifun(0))) -+ WHERE sro_ifun(a + 10) > sro_ifun(10); -+REINDEX TABLE sro_ptab; -+REINDEX INDEX CONCURRENTLY sro_pidx; -+ - SET SESSION AUTHORIZATION regress_sro_user; - CREATE FUNCTION unwanted_grant() RETURNS void LANGUAGE sql AS - 'GRANT regress_priv_group2 TO regress_sro_user'; -@@ -852,6 +899,23 @@ REFRESH MATERIALIZED VIEW sro_mv; - REFRESH MATERIALIZED VIEW sro_mv; - BEGIN; SET CONSTRAINTS ALL IMMEDIATE; REFRESH MATERIALIZED VIEW sro_mv; COMMIT; - -+-- REFRESH MATERIALIZED VIEW CONCURRENTLY use of eval_const_expressions() -+SET SESSION AUTHORIZATION regress_sro_user; -+CREATE FUNCTION unwanted_grant_nofail(int) RETURNS int -+ IMMUTABLE LANGUAGE plpgsql AS $$ -+BEGIN -+ PERFORM unwanted_grant(); -+ RAISE WARNING 'owned'; -+ RETURN 1; -+EXCEPTION WHEN OTHERS THEN -+ RETURN 2; -+END$$; -+CREATE MATERIALIZED VIEW sro_index_mv AS SELECT 1 AS c; -+CREATE UNIQUE INDEX ON sro_index_mv (c) WHERE unwanted_grant_nofail(1) > 0; -+\c - -+REFRESH MATERIALIZED VIEW CONCURRENTLY sro_index_mv; -+REFRESH MATERIALIZED VIEW sro_index_mv; -+ - DROP OWNED BY regress_sro_user; - DROP ROLE regress_sro_user; - --- -2.25.1 - |