aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHitendra Prajapati <hprajapati@mvista.com>2022-09-23 09:54:15 +0530
committerArmin Kuster <akuster808@gmail.com>2022-10-30 14:47:35 -0400
commit62842aac9810b9d430ca3621d9e909be653954ae (patch)
treefabea392f0554e1437d380bc6ca6cfdf8355c56f
parent6792ebdd966aa0fb662989529193a0940fbfee00 (diff)
downloadmeta-openembedded-contrib-62842aac9810b9d430ca3621d9e909be653954ae.tar.gz
postgresql: CVE-2022-1552 Autovacuum, REINDEX, and others omit "security restricted operation" sandbox
Source: https://git.postgresql.org/gitweb/?p=postgresql.git; MR: 121822 Type: Security Fix Disposition: Backport from https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=ab49ce7c3414ac19e4afb386d7843ce2d2fb8bda && https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=677a494789062ca88e0142a17bedd5415f6ab0aa ChangeID: 5011e2e09f30f76fc27dc4cb5fa98a504d1aaec9 Description: CVE-2022-1552 postgresql: Autovacuum, REINDEX, and others omit "security restricted operation" sandbox. Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
-rw-r--r--meta-oe/recipes-dbs/postgresql/files/CVE-2022-1552.patch947
-rw-r--r--meta-oe/recipes-dbs/postgresql/postgresql_12.9.bb1
2 files changed, 948 insertions, 0 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
new file mode 100644
index 0000000000..6f0d5ac06f
--- /dev/null
+++ b/meta-oe/recipes-dbs/postgresql/files/CVE-2022-1552.patch
@@ -0,0 +1,947 @@
+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
+
diff --git a/meta-oe/recipes-dbs/postgresql/postgresql_12.9.bb b/meta-oe/recipes-dbs/postgresql/postgresql_12.9.bb
index 67bf2b9604..1344d4eb00 100644
--- a/meta-oe/recipes-dbs/postgresql/postgresql_12.9.bb
+++ b/meta-oe/recipes-dbs/postgresql/postgresql_12.9.bb
@@ -7,6 +7,7 @@ SRC_URI += "\
file://0001-Add-support-for-RISC-V.patch \
file://0001-Improve-reproducibility.patch \
file://remove_duplicate.patch \
+ file://CVE-2022-1552.patch \
"
SRC_URI[sha256sum] = "89fda2de33ed04a98548e43f3ee5f15b882be17505d631fe0dd1a540a2b56dce"