postgresql: Update to 12.16

This is a minor release to address CVEs and other bug fixes without new
features. Remove patches that are fixed in this release. Release notes are
available at:

https://www.postgresql.org/docs/release/12.10/
https://www.postgresql.org/docs/release/12.11/
https://www.postgresql.org/docs/release/12.12/
https://www.postgresql.org/docs/release/12.13/
https://www.postgresql.org/docs/release/12.14/
https://www.postgresql.org/docs/release/12.15/
https://www.postgresql.org/docs/release/12.16/

License-Update: Copyright year updated

Signed-off-by: Robert Joslyn <robert.joslyn@redrectangle.org>
Signed-off-by: Armin Kuster <akuster808@gmail.com>
This commit is contained in:
Robert Joslyn 2023-08-20 18:18:30 -07:00 committed by Armin Kuster
parent 1ff41cb9c6
commit d96f8c4853
8 changed files with 11 additions and 2307 deletions

View File

@ -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

View File

@ -1,904 +0,0 @@
From 84375c1db25ef650902cf80712495fc514b0ff63 Mon Sep 17 00:00:00 2001
From: Hitendra Prajapati <hprajapati@mvista.com>
Date: Thu, 13 Oct 2022 10:35:32 +0530
Subject: [PATCH] CVE-2022-2625
Upstream-Status: Backport [https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=5579726bd60a6e7afb04a3548bced348cd5ffd89]
CVE: CVE-2022-2625
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
---
doc/src/sgml/extend.sgml | 11 --
src/backend/catalog/pg_collation.c | 49 ++++--
src/backend/catalog/pg_depend.c | 74 ++++++++-
src/backend/catalog/pg_operator.c | 2 +-
src/backend/catalog/pg_type.c | 7 +-
src/backend/commands/createas.c | 18 ++-
src/backend/commands/foreigncmds.c | 19 ++-
src/backend/commands/schemacmds.c | 25 ++-
src/backend/commands/sequence.c | 8 +
src/backend/commands/statscmds.c | 4 +
src/backend/commands/view.c | 16 +-
src/backend/parser/parse_utilcmd.c | 10 ++
src/include/catalog/dependency.h | 2 +
src/test/modules/test_extensions/Makefile | 5 +-
.../expected/test_extensions.out | 153 ++++++++++++++++++
.../test_extensions/sql/test_extensions.sql | 110 +++++++++++++
.../test_ext_cine--1.0--1.1.sql | 26 +++
.../test_extensions/test_ext_cine--1.0.sql | 25 +++
.../test_extensions/test_ext_cine.control | 3 +
.../test_extensions/test_ext_cor--1.0.sql | 20 +++
.../test_extensions/test_ext_cor.control | 3 +
21 files changed, 540 insertions(+), 50 deletions(-)
create mode 100644 src/test/modules/test_extensions/test_ext_cine--1.0--1.1.sql
create mode 100644 src/test/modules/test_extensions/test_ext_cine--1.0.sql
create mode 100644 src/test/modules/test_extensions/test_ext_cine.control
create mode 100644 src/test/modules/test_extensions/test_ext_cor--1.0.sql
create mode 100644 src/test/modules/test_extensions/test_ext_cor.control
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 53f2638..bcc7a80 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -1109,17 +1109,6 @@ SELECT * FROM pg_extension_update_paths('<replaceable>extension_name</replaceabl
<varname>search_path</varname>. However, no mechanism currently exists
to require that.
</para>
-
- <para>
- Do <emphasis>not</emphasis> use <command>CREATE OR REPLACE
- FUNCTION</command>, except in an update script that must change the
- definition of a function that is known to be an extension member
- already. (Likewise for other <literal>OR REPLACE</literal> options.)
- Using <literal>OR REPLACE</literal> unnecessarily not only has a risk
- of accidentally overwriting someone else's function, but it creates a
- security hazard since the overwritten function would still be owned by
- its original owner, who could modify it.
- </para>
</sect3>
</sect2>
diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c
index dd99d53..ba4c3ef 100644
--- a/src/backend/catalog/pg_collation.c
+++ b/src/backend/catalog/pg_collation.c
@@ -78,15 +78,25 @@ CollationCreate(const char *collname, Oid collnamespace,
* friendlier error message. The unique index provides a backstop against
* race conditions.
*/
- if (SearchSysCacheExists3(COLLNAMEENCNSP,
- PointerGetDatum(collname),
- Int32GetDatum(collencoding),
- ObjectIdGetDatum(collnamespace)))
+ oid = GetSysCacheOid3(COLLNAMEENCNSP,
+ Anum_pg_collation_oid,
+ PointerGetDatum(collname),
+ Int32GetDatum(collencoding),
+ ObjectIdGetDatum(collnamespace));
+ if (OidIsValid(oid))
{
if (quiet)
return InvalidOid;
else if (if_not_exists)
{
+ /*
+ * If we are in an extension script, insist that the pre-existing
+ * object be a member of the extension, to avoid security risks.
+ */
+ ObjectAddressSet(myself, CollationRelationId, oid);
+ checkMembershipInCurrentExtension(&myself);
+
+ /* OK to skip */
ereport(NOTICE,
(errcode(ERRCODE_DUPLICATE_OBJECT),
collencoding == -1
@@ -116,16 +126,19 @@ CollationCreate(const char *collname, Oid collnamespace,
* so we take a ShareRowExclusiveLock earlier, to protect against
* concurrent changes fooling this check.
*/
- if ((collencoding == -1 &&
- SearchSysCacheExists3(COLLNAMEENCNSP,
- PointerGetDatum(collname),
- Int32GetDatum(GetDatabaseEncoding()),
- ObjectIdGetDatum(collnamespace))) ||
- (collencoding != -1 &&
- SearchSysCacheExists3(COLLNAMEENCNSP,
- PointerGetDatum(collname),
- Int32GetDatum(-1),
- ObjectIdGetDatum(collnamespace))))
+ if (collencoding == -1)
+ oid = GetSysCacheOid3(COLLNAMEENCNSP,
+ Anum_pg_collation_oid,
+ PointerGetDatum(collname),
+ Int32GetDatum(GetDatabaseEncoding()),
+ ObjectIdGetDatum(collnamespace));
+ else
+ oid = GetSysCacheOid3(COLLNAMEENCNSP,
+ Anum_pg_collation_oid,
+ PointerGetDatum(collname),
+ Int32GetDatum(-1),
+ ObjectIdGetDatum(collnamespace));
+ if (OidIsValid(oid))
{
if (quiet)
{
@@ -134,6 +147,14 @@ CollationCreate(const char *collname, Oid collnamespace,
}
else if (if_not_exists)
{
+ /*
+ * If we are in an extension script, insist that the pre-existing
+ * object be a member of the extension, to avoid security risks.
+ */
+ ObjectAddressSet(myself, CollationRelationId, oid);
+ checkMembershipInCurrentExtension(&myself);
+
+ /* OK to skip */
table_close(rel, NoLock);
ereport(NOTICE,
(errcode(ERRCODE_DUPLICATE_OBJECT),
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 9ffadbb..71c7cef 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -124,15 +124,23 @@ recordMultipleDependencies(const ObjectAddress *depender,
/*
* If we are executing a CREATE EXTENSION operation, mark the given object
- * as being a member of the extension. Otherwise, do nothing.
+ * as being a member of the extension, or check that it already is one.
+ * Otherwise, do nothing.
*
* This must be called during creation of any user-definable object type
* that could be a member of an extension.
*
- * If isReplace is true, the object already existed (or might have already
- * existed), so we must check for a pre-existing extension membership entry.
- * Passing false is a guarantee that the object is newly created, and so
- * could not already be a member of any extension.
+ * isReplace must be true if the object already existed, and false if it is
+ * newly created. In the former case we insist that it already be a member
+ * of the current extension. In the latter case we can skip checking whether
+ * it is already a member of any extension.
+ *
+ * Note: isReplace = true is typically used when updating a object in
+ * CREATE OR REPLACE and similar commands. We used to allow the target
+ * object to not already be an extension member, instead silently absorbing
+ * it into the current extension. However, this was both error-prone
+ * (extensions might accidentally overwrite free-standing objects) and
+ * a security hazard (since the object would retain its previous ownership).
*/
void
recordDependencyOnCurrentExtension(const ObjectAddress *object,
@@ -150,6 +158,12 @@ recordDependencyOnCurrentExtension(const ObjectAddress *object,
{
Oid oldext;
+ /*
+ * Side note: these catalog lookups are safe only because the
+ * object is a pre-existing one. In the not-isReplace case, the
+ * caller has most likely not yet done a CommandCounterIncrement
+ * that would make the new object visible.
+ */
oldext = getExtensionOfObject(object->classId, object->objectId);
if (OidIsValid(oldext))
{
@@ -163,6 +177,13 @@ recordDependencyOnCurrentExtension(const ObjectAddress *object,
getObjectDescription(object),
get_extension_name(oldext))));
}
+ /* It's a free-standing object, so reject */
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("%s is not a member of extension \"%s\"",
+ getObjectDescription(object),
+ get_extension_name(CurrentExtensionObject)),
+ errdetail("An extension is not allowed to replace an object that it does not own.")));
}
/* OK, record it as a member of CurrentExtensionObject */
@@ -174,6 +195,49 @@ recordDependencyOnCurrentExtension(const ObjectAddress *object,
}
}
+/*
+ * If we are executing a CREATE EXTENSION operation, check that the given
+ * object is a member of the extension, and throw an error if it isn't.
+ * Otherwise, do nothing.
+ *
+ * This must be called whenever a CREATE IF NOT EXISTS operation (for an
+ * object type that can be an extension member) has found that an object of
+ * the desired name already exists. It is insecure for an extension to use
+ * IF NOT EXISTS except when the conflicting object is already an extension
+ * member; otherwise a hostile user could substitute an object with arbitrary
+ * properties.
+ */
+void
+checkMembershipInCurrentExtension(const ObjectAddress *object)
+{
+ /*
+ * This is actually the same condition tested in
+ * recordDependencyOnCurrentExtension; but we want to issue a
+ * differently-worded error, and anyway it would be pretty confusing to
+ * call recordDependencyOnCurrentExtension in these circumstances.
+ */
+
+ /* Only whole objects can be extension members */
+ Assert(object->objectSubId == 0);
+
+ if (creating_extension)
+ {
+ Oid oldext;
+
+ oldext = getExtensionOfObject(object->classId, object->objectId);
+ /* If already a member of this extension, OK */
+ if (oldext == CurrentExtensionObject)
+ return;
+ /* Else complain */
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("%s is not a member of extension \"%s\"",
+ getObjectDescription(object),
+ get_extension_name(CurrentExtensionObject)),
+ errdetail("An extension may only use CREATE ... IF NOT EXISTS to skip object creation if the conflicting object is one that it already owns.")));
+ }
+}
+
/*
* deleteDependencyRecordsFor -- delete all records with given depender
* classId/objectId. Returns the number of records deleted.
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index bcaa26c..84784e6 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -867,7 +867,7 @@ makeOperatorDependencies(HeapTuple tuple, bool isUpdate)
oper->oprowner);
/* Dependency on extension */
- recordDependencyOnCurrentExtension(&myself, true);
+ recordDependencyOnCurrentExtension(&myself, isUpdate);
return myself;
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 2a51501..3ff017f 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -528,10 +528,9 @@ TypeCreate(Oid newTypeOid,
* If rebuild is true, we remove existing dependencies and rebuild them
* from scratch. This is needed for ALTER TYPE, and also when replacing
* a shell type. We don't remove an existing extension dependency, though.
- * (That means an extension can't absorb a shell type created in another
- * extension, nor ALTER a type created by another extension. Also, if it
- * replaces a free-standing shell type or ALTERs a free-standing type,
- * that type will become a member of the extension.)
+ * That means an extension can't absorb a shell type that is free-standing
+ * or belongs to another extension, nor ALTER a type that is free-standing or
+ * belongs to another extension.
*/
void
GenerateTypeDependencies(Oid typeObjectId,
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 4c1d909..a68d945 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -243,15 +243,27 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
if (stmt->if_not_exists)
{
Oid nspid;
+ Oid oldrelid;
- nspid = RangeVarGetCreationNamespace(stmt->into->rel);
+ nspid = RangeVarGetCreationNamespace(into->rel);
- if (get_relname_relid(stmt->into->rel->relname, nspid))
+ oldrelid = get_relname_relid(into->rel->relname, nspid);
+ if (OidIsValid(oldrelid))
{
+ /*
+ * The relation exists and IF NOT EXISTS has been specified.
+ *
+ * If we are in an extension script, insist that the pre-existing
+ * object be a member of the extension, to avoid security risks.
+ */
+ ObjectAddressSet(address, RelationRelationId, oldrelid);
+ checkMembershipInCurrentExtension(&address);
+
+ /* OK to skip */
ereport(NOTICE,
(errcode(ERRCODE_DUPLICATE_TABLE),
errmsg("relation \"%s\" already exists, skipping",
- stmt->into->rel->relname)));
+ into->rel->relname)));
return InvalidObjectAddress;
}
}
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index d7bc6e3..bc583c6 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -887,13 +887,22 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
ownerId = GetUserId();
/*
- * Check that there is no other foreign server by this name. Do nothing if
- * IF NOT EXISTS was enforced.
+ * Check that there is no other foreign server by this name. If there is
+ * one, do nothing if IF NOT EXISTS was specified.
*/
- if (GetForeignServerByName(stmt->servername, true) != NULL)
+ srvId = get_foreign_server_oid(stmt->servername, true);
+ if (OidIsValid(srvId))
{
if (stmt->if_not_exists)
{
+ /*
+ * If we are in an extension script, insist that the pre-existing
+ * object be a member of the extension, to avoid security risks.
+ */
+ ObjectAddressSet(myself, ForeignServerRelationId, srvId);
+ checkMembershipInCurrentExtension(&myself);
+
+ /* OK to skip */
ereport(NOTICE,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("server \"%s\" already exists, skipping",
@@ -1182,6 +1191,10 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
{
if (stmt->if_not_exists)
{
+ /*
+ * Since user mappings aren't members of extensions (see comments
+ * below), no need for checkMembershipInCurrentExtension here.
+ */
ereport(NOTICE,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("user mapping for \"%s\" already exists for server \"%s\", skipping",
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index 6cf94a3..6bc4edc 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -113,14 +113,25 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
* the permissions checks, but since CREATE TABLE IF NOT EXISTS makes its
* creation-permission check first, we do likewise.
*/
- if (stmt->if_not_exists &&
- SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(schemaName)))
+ if (stmt->if_not_exists)
{
- ereport(NOTICE,
- (errcode(ERRCODE_DUPLICATE_SCHEMA),
- errmsg("schema \"%s\" already exists, skipping",
- schemaName)));
- return InvalidOid;
+ namespaceId = get_namespace_oid(schemaName, true);
+ if (OidIsValid(namespaceId))
+ {
+ /*
+ * If we are in an extension script, insist that the pre-existing
+ * object be a member of the extension, to avoid security risks.
+ */
+ ObjectAddressSet(address, NamespaceRelationId, namespaceId);
+ checkMembershipInCurrentExtension(&address);
+
+ /* OK to skip */
+ ereport(NOTICE,
+ (errcode(ERRCODE_DUPLICATE_SCHEMA),
+ errmsg("schema \"%s\" already exists, skipping",
+ schemaName)));
+ return InvalidOid;
+ }
}
/*
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 0960b33..0577184 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -149,6 +149,14 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
RangeVarGetAndCheckCreationNamespace(seq->sequence, NoLock, &seqoid);
if (OidIsValid(seqoid))
{
+ /*
+ * If we are in an extension script, insist that the pre-existing
+ * object be a member of the extension, to avoid security risks.
+ */
+ ObjectAddressSet(address, RelationRelationId, seqoid);
+ checkMembershipInCurrentExtension(&address);
+
+ /* OK to skip */
ereport(NOTICE,
(errcode(ERRCODE_DUPLICATE_TABLE),
errmsg("relation \"%s\" already exists, skipping",
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index 5678d31..409cf28 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -173,6 +173,10 @@ CreateStatistics(CreateStatsStmt *stmt)
{
if (stmt->if_not_exists)
{
+ /*
+ * Since stats objects aren't members of extensions (see comments
+ * below), no need for checkMembershipInCurrentExtension here.
+ */
ereport(NOTICE,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("statistics object \"%s\" already exists, skipping",
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 87ed453..dd7cc97 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -205,7 +205,7 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
CommandCounterIncrement();
/*
- * Finally update the view options.
+ * Update the view's options.
*
* The new options list replaces the existing options list, even if
* it's empty.
@@ -218,8 +218,22 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
/* EventTriggerAlterTableStart called by ProcessUtilitySlow */
AlterTableInternal(viewOid, atcmds, true);
+ /*
+ * There is very little to do here to update the view's dependencies.
+ * Most view-level dependency relationships, such as those on the
+ * owner, schema, and associated composite type, aren't changing.
+ * Because we don't allow changing type or collation of an existing
+ * view column, those dependencies of the existing columns don't
+ * change either, while the AT_AddColumnToView machinery took care of
+ * adding such dependencies for new view columns. The dependencies of
+ * the view's query could have changed arbitrarily, but that was dealt
+ * with inside StoreViewQuery. What remains is only to check that
+ * view replacement is allowed when we're creating an extension.
+ */
ObjectAddressSet(address, RelationRelationId, viewOid);
+ recordDependencyOnCurrentExtension(&address, true);
+
/*
* Seems okay, so return the OID of the pre-existing view.
*/
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 44aa38a..8f4d940 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -206,6 +206,16 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
*/
if (stmt->if_not_exists && OidIsValid(existing_relid))
{
+ /*
+ * If we are in an extension script, insist that the pre-existing
+ * object be a member of the extension, to avoid security risks.
+ */
+ ObjectAddress address;
+
+ ObjectAddressSet(address, RelationRelationId, existing_relid);
+ checkMembershipInCurrentExtension(&address);
+
+ /* OK to skip */
ereport(NOTICE,
(errcode(ERRCODE_DUPLICATE_TABLE),
errmsg("relation \"%s\" already exists, skipping",
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 8b1e3aa..27c7509 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -201,6 +201,8 @@ extern void recordMultipleDependencies(const ObjectAddress *depender,
extern void recordDependencyOnCurrentExtension(const ObjectAddress *object,
bool isReplace);
+extern void checkMembershipInCurrentExtension(const ObjectAddress *object);
+
extern long deleteDependencyRecordsFor(Oid classId, Oid objectId,
bool skipExtensionDeps);
diff --git a/src/test/modules/test_extensions/Makefile b/src/test/modules/test_extensions/Makefile
index d18108e..7428f15 100644
--- a/src/test/modules/test_extensions/Makefile
+++ b/src/test/modules/test_extensions/Makefile
@@ -4,10 +4,13 @@ MODULE = test_extensions
PGFILEDESC = "test_extensions - regression testing for EXTENSION support"
EXTENSION = test_ext1 test_ext2 test_ext3 test_ext4 test_ext5 test_ext6 \
- test_ext7 test_ext8 test_ext_cyclic1 test_ext_cyclic2
+ test_ext7 test_ext8 test_ext_cine test_ext_cor \
+ test_ext_cyclic1 test_ext_cyclic2
DATA = test_ext1--1.0.sql test_ext2--1.0.sql test_ext3--1.0.sql \
test_ext4--1.0.sql test_ext5--1.0.sql test_ext6--1.0.sql \
test_ext7--1.0.sql test_ext7--1.0--2.0.sql test_ext8--1.0.sql \
+ test_ext_cine--1.0.sql test_ext_cine--1.0--1.1.sql \
+ test_ext_cor--1.0.sql \
test_ext_cyclic1--1.0.sql test_ext_cyclic2--1.0.sql
REGRESS = test_extensions test_extdepend
diff --git a/src/test/modules/test_extensions/expected/test_extensions.out b/src/test/modules/test_extensions/expected/test_extensions.out
index b5cbdfc..1e91640 100644
--- a/src/test/modules/test_extensions/expected/test_extensions.out
+++ b/src/test/modules/test_extensions/expected/test_extensions.out
@@ -154,3 +154,156 @@ DROP TABLE test_ext4_tab;
DROP FUNCTION create_extension_with_temp_schema();
RESET client_min_messages;
\unset SHOW_CONTEXT
+-- It's generally bad style to use CREATE OR REPLACE unnecessarily.
+-- Test what happens if an extension does it anyway.
+-- Replacing a shell type or operator is sort of like CREATE OR REPLACE;
+-- check that too.
+CREATE FUNCTION ext_cor_func() RETURNS text
+ AS $$ SELECT 'ext_cor_func: original'::text $$ LANGUAGE sql;
+CREATE EXTENSION test_ext_cor; -- fail
+ERROR: function ext_cor_func() is not a member of extension "test_ext_cor"
+DETAIL: An extension is not allowed to replace an object that it does not own.
+SELECT ext_cor_func();
+ ext_cor_func
+------------------------
+ ext_cor_func: original
+(1 row)
+
+DROP FUNCTION ext_cor_func();
+CREATE VIEW ext_cor_view AS
+ SELECT 'ext_cor_view: original'::text AS col;
+CREATE EXTENSION test_ext_cor; -- fail
+ERROR: view ext_cor_view is not a member of extension "test_ext_cor"
+DETAIL: An extension is not allowed to replace an object that it does not own.
+SELECT ext_cor_func();
+ERROR: function ext_cor_func() does not exist
+LINE 1: SELECT ext_cor_func();
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+SELECT * FROM ext_cor_view;
+ col
+------------------------
+ ext_cor_view: original
+(1 row)
+
+DROP VIEW ext_cor_view;
+CREATE TYPE test_ext_type;
+CREATE EXTENSION test_ext_cor; -- fail
+ERROR: type test_ext_type is not a member of extension "test_ext_cor"
+DETAIL: An extension is not allowed to replace an object that it does not own.
+DROP TYPE test_ext_type;
+-- this makes a shell "point <<@@ polygon" operator too
+CREATE OPERATOR @@>> ( PROCEDURE = poly_contain_pt,
+ LEFTARG = polygon, RIGHTARG = point,
+ COMMUTATOR = <<@@ );
+CREATE EXTENSION test_ext_cor; -- fail
+ERROR: operator <<@@(point,polygon) is not a member of extension "test_ext_cor"
+DETAIL: An extension is not allowed to replace an object that it does not own.
+DROP OPERATOR <<@@ (point, polygon);
+CREATE EXTENSION test_ext_cor; -- now it should work
+SELECT ext_cor_func();
+ ext_cor_func
+------------------------------
+ ext_cor_func: from extension
+(1 row)
+
+SELECT * FROM ext_cor_view;
+ col
+------------------------------
+ ext_cor_view: from extension
+(1 row)
+
+SELECT 'x'::test_ext_type;
+ test_ext_type
+---------------
+ x
+(1 row)
+
+SELECT point(0,0) <<@@ polygon(circle(point(0,0),1));
+ ?column?
+----------
+ t
+(1 row)
+
+\dx+ test_ext_cor
+Objects in extension "test_ext_cor"
+ Object description
+------------------------------
+ function ext_cor_func()
+ operator <<@@(point,polygon)
+ type test_ext_type
+ view ext_cor_view
+(4 rows)
+
+--
+-- CREATE IF NOT EXISTS is an entirely unsound thing for an extension
+-- to be doing, but let's at least plug the major security hole in it.
+--
+CREATE COLLATION ext_cine_coll
+ ( LC_COLLATE = "C", LC_CTYPE = "C" );
+CREATE EXTENSION test_ext_cine; -- fail
+ERROR: collation ext_cine_coll is not a member of extension "test_ext_cine"
+DETAIL: An extension may only use CREATE ... IF NOT EXISTS to skip object creation if the conflicting object is one that it already owns.
+DROP COLLATION ext_cine_coll;
+CREATE MATERIALIZED VIEW ext_cine_mv AS SELECT 11 AS f1;
+CREATE EXTENSION test_ext_cine; -- fail
+ERROR: materialized view ext_cine_mv is not a member of extension "test_ext_cine"
+DETAIL: An extension may only use CREATE ... IF NOT EXISTS to skip object creation if the conflicting object is one that it already owns.
+DROP MATERIALIZED VIEW ext_cine_mv;
+CREATE FOREIGN DATA WRAPPER dummy;
+CREATE SERVER ext_cine_srv FOREIGN DATA WRAPPER dummy;
+CREATE EXTENSION test_ext_cine; -- fail
+ERROR: server ext_cine_srv is not a member of extension "test_ext_cine"
+DETAIL: An extension may only use CREATE ... IF NOT EXISTS to skip object creation if the conflicting object is one that it already owns.
+DROP SERVER ext_cine_srv;
+CREATE SCHEMA ext_cine_schema;
+CREATE EXTENSION test_ext_cine; -- fail
+ERROR: schema ext_cine_schema is not a member of extension "test_ext_cine"
+DETAIL: An extension may only use CREATE ... IF NOT EXISTS to skip object creation if the conflicting object is one that it already owns.
+DROP SCHEMA ext_cine_schema;
+CREATE SEQUENCE ext_cine_seq;
+CREATE EXTENSION test_ext_cine; -- fail
+ERROR: sequence ext_cine_seq is not a member of extension "test_ext_cine"
+DETAIL: An extension may only use CREATE ... IF NOT EXISTS to skip object creation if the conflicting object is one that it already owns.
+DROP SEQUENCE ext_cine_seq;
+CREATE TABLE ext_cine_tab1 (x int);
+CREATE EXTENSION test_ext_cine; -- fail
+ERROR: table ext_cine_tab1 is not a member of extension "test_ext_cine"
+DETAIL: An extension may only use CREATE ... IF NOT EXISTS to skip object creation if the conflicting object is one that it already owns.
+DROP TABLE ext_cine_tab1;
+CREATE TABLE ext_cine_tab2 AS SELECT 42 AS y;
+CREATE EXTENSION test_ext_cine; -- fail
+ERROR: table ext_cine_tab2 is not a member of extension "test_ext_cine"
+DETAIL: An extension may only use CREATE ... IF NOT EXISTS to skip object creation if the conflicting object is one that it already owns.
+DROP TABLE ext_cine_tab2;
+CREATE EXTENSION test_ext_cine;
+\dx+ test_ext_cine
+Objects in extension "test_ext_cine"
+ Object description
+-----------------------------------
+ collation ext_cine_coll
+ foreign-data wrapper ext_cine_fdw
+ materialized view ext_cine_mv
+ schema ext_cine_schema
+ sequence ext_cine_seq
+ server ext_cine_srv
+ table ext_cine_tab1
+ table ext_cine_tab2
+(8 rows)
+
+ALTER EXTENSION test_ext_cine UPDATE TO '1.1';
+\dx+ test_ext_cine
+Objects in extension "test_ext_cine"
+ Object description
+-----------------------------------
+ collation ext_cine_coll
+ foreign-data wrapper ext_cine_fdw
+ materialized view ext_cine_mv
+ schema ext_cine_schema
+ sequence ext_cine_seq
+ server ext_cine_srv
+ table ext_cine_tab1
+ table ext_cine_tab2
+ table ext_cine_tab3
+(9 rows)
+
diff --git a/src/test/modules/test_extensions/sql/test_extensions.sql b/src/test/modules/test_extensions/sql/test_extensions.sql
index f505466..b3d4579 100644
--- a/src/test/modules/test_extensions/sql/test_extensions.sql
+++ b/src/test/modules/test_extensions/sql/test_extensions.sql
@@ -93,3 +93,113 @@ DROP TABLE test_ext4_tab;
DROP FUNCTION create_extension_with_temp_schema();
RESET client_min_messages;
\unset SHOW_CONTEXT
+
+-- It's generally bad style to use CREATE OR REPLACE unnecessarily.
+-- Test what happens if an extension does it anyway.
+-- Replacing a shell type or operator is sort of like CREATE OR REPLACE;
+-- check that too.
+
+CREATE FUNCTION ext_cor_func() RETURNS text
+ AS $$ SELECT 'ext_cor_func: original'::text $$ LANGUAGE sql;
+
+CREATE EXTENSION test_ext_cor; -- fail
+
+SELECT ext_cor_func();
+
+DROP FUNCTION ext_cor_func();
+
+CREATE VIEW ext_cor_view AS
+ SELECT 'ext_cor_view: original'::text AS col;
+
+CREATE EXTENSION test_ext_cor; -- fail
+
+SELECT ext_cor_func();
+
+SELECT * FROM ext_cor_view;
+
+DROP VIEW ext_cor_view;
+
+CREATE TYPE test_ext_type;
+
+CREATE EXTENSION test_ext_cor; -- fail
+
+DROP TYPE test_ext_type;
+
+-- this makes a shell "point <<@@ polygon" operator too
+CREATE OPERATOR @@>> ( PROCEDURE = poly_contain_pt,
+ LEFTARG = polygon, RIGHTARG = point,
+ COMMUTATOR = <<@@ );
+
+CREATE EXTENSION test_ext_cor; -- fail
+
+DROP OPERATOR <<@@ (point, polygon);
+
+CREATE EXTENSION test_ext_cor; -- now it should work
+
+SELECT ext_cor_func();
+
+SELECT * FROM ext_cor_view;
+
+SELECT 'x'::test_ext_type;
+
+SELECT point(0,0) <<@@ polygon(circle(point(0,0),1));
+
+\dx+ test_ext_cor
+
+--
+-- CREATE IF NOT EXISTS is an entirely unsound thing for an extension
+-- to be doing, but let's at least plug the major security hole in it.
+--
+
+CREATE COLLATION ext_cine_coll
+ ( LC_COLLATE = "C", LC_CTYPE = "C" );
+
+CREATE EXTENSION test_ext_cine; -- fail
+
+DROP COLLATION ext_cine_coll;
+
+CREATE MATERIALIZED VIEW ext_cine_mv AS SELECT 11 AS f1;
+
+CREATE EXTENSION test_ext_cine; -- fail
+
+DROP MATERIALIZED VIEW ext_cine_mv;
+
+CREATE FOREIGN DATA WRAPPER dummy;
+
+CREATE SERVER ext_cine_srv FOREIGN DATA WRAPPER dummy;
+
+CREATE EXTENSION test_ext_cine; -- fail
+
+DROP SERVER ext_cine_srv;
+
+CREATE SCHEMA ext_cine_schema;
+
+CREATE EXTENSION test_ext_cine; -- fail
+
+DROP SCHEMA ext_cine_schema;
+
+CREATE SEQUENCE ext_cine_seq;
+
+CREATE EXTENSION test_ext_cine; -- fail
+
+DROP SEQUENCE ext_cine_seq;
+
+CREATE TABLE ext_cine_tab1 (x int);
+
+CREATE EXTENSION test_ext_cine; -- fail
+
+DROP TABLE ext_cine_tab1;
+
+CREATE TABLE ext_cine_tab2 AS SELECT 42 AS y;
+
+CREATE EXTENSION test_ext_cine; -- fail
+
+DROP TABLE ext_cine_tab2;
+
+CREATE EXTENSION test_ext_cine;
+
+\dx+ test_ext_cine
+
+ALTER EXTENSION test_ext_cine UPDATE TO '1.1';
+
+\dx+ test_ext_cine
diff --git a/src/test/modules/test_extensions/test_ext_cine--1.0--1.1.sql b/src/test/modules/test_extensions/test_ext_cine--1.0--1.1.sql
new file mode 100644
index 0000000..6dadfd2
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext_cine--1.0--1.1.sql
@@ -0,0 +1,26 @@
+/* src/test/modules/test_extensions/test_ext_cine--1.0--1.1.sql */
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION test_ext_cine UPDATE TO '1.1'" to load this file. \quit
+
+--
+-- These are the same commands as in the 1.0 script; we expect them
+-- to do nothing.
+--
+
+CREATE COLLATION IF NOT EXISTS ext_cine_coll
+ ( LC_COLLATE = "POSIX", LC_CTYPE = "POSIX" );
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS ext_cine_mv AS SELECT 42 AS f1;
+
+CREATE SERVER IF NOT EXISTS ext_cine_srv FOREIGN DATA WRAPPER ext_cine_fdw;
+
+CREATE SCHEMA IF NOT EXISTS ext_cine_schema;
+
+CREATE SEQUENCE IF NOT EXISTS ext_cine_seq;
+
+CREATE TABLE IF NOT EXISTS ext_cine_tab1 (x int);
+
+CREATE TABLE IF NOT EXISTS ext_cine_tab2 AS SELECT 42 AS y;
+
+-- just to verify the script ran
+CREATE TABLE ext_cine_tab3 (z int);
diff --git a/src/test/modules/test_extensions/test_ext_cine--1.0.sql b/src/test/modules/test_extensions/test_ext_cine--1.0.sql
new file mode 100644
index 0000000..01408ff
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext_cine--1.0.sql
@@ -0,0 +1,25 @@
+/* src/test/modules/test_extensions/test_ext_cine--1.0.sql */
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_ext_cine" to load this file. \quit
+
+--
+-- CREATE IF NOT EXISTS is an entirely unsound thing for an extension
+-- to be doing, but let's at least plug the major security hole in it.
+--
+
+CREATE COLLATION IF NOT EXISTS ext_cine_coll
+ ( LC_COLLATE = "POSIX", LC_CTYPE = "POSIX" );
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS ext_cine_mv AS SELECT 42 AS f1;
+
+CREATE FOREIGN DATA WRAPPER ext_cine_fdw;
+
+CREATE SERVER IF NOT EXISTS ext_cine_srv FOREIGN DATA WRAPPER ext_cine_fdw;
+
+CREATE SCHEMA IF NOT EXISTS ext_cine_schema;
+
+CREATE SEQUENCE IF NOT EXISTS ext_cine_seq;
+
+CREATE TABLE IF NOT EXISTS ext_cine_tab1 (x int);
+
+CREATE TABLE IF NOT EXISTS ext_cine_tab2 AS SELECT 42 AS y;
diff --git a/src/test/modules/test_extensions/test_ext_cine.control b/src/test/modules/test_extensions/test_ext_cine.control
new file mode 100644
index 0000000..ced713b
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext_cine.control
@@ -0,0 +1,3 @@
+comment = 'Test extension using CREATE IF NOT EXISTS'
+default_version = '1.0'
+relocatable = true
diff --git a/src/test/modules/test_extensions/test_ext_cor--1.0.sql b/src/test/modules/test_extensions/test_ext_cor--1.0.sql
new file mode 100644
index 0000000..2e8d89c
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext_cor--1.0.sql
@@ -0,0 +1,20 @@
+/* src/test/modules/test_extensions/test_ext_cor--1.0.sql */
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_ext_cor" to load this file. \quit
+
+-- It's generally bad style to use CREATE OR REPLACE unnecessarily.
+-- Test what happens if an extension does it anyway.
+
+CREATE OR REPLACE FUNCTION ext_cor_func() RETURNS text
+ AS $$ SELECT 'ext_cor_func: from extension'::text $$ LANGUAGE sql;
+
+CREATE OR REPLACE VIEW ext_cor_view AS
+ SELECT 'ext_cor_view: from extension'::text AS col;
+
+-- These are for testing replacement of a shell type/operator, which works
+-- enough like an implicit OR REPLACE to be important to check.
+
+CREATE TYPE test_ext_type AS ENUM('x', 'y');
+
+CREATE OPERATOR <<@@ ( PROCEDURE = pt_contained_poly,
+ LEFTARG = point, RIGHTARG = polygon );
diff --git a/src/test/modules/test_extensions/test_ext_cor.control b/src/test/modules/test_extensions/test_ext_cor.control
new file mode 100644
index 0000000..0e972e5
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext_cor.control
@@ -0,0 +1,3 @@
+comment = 'Test extension using CREATE OR REPLACE'
+default_version = '1.0'
+relocatable = true
--
2.25.1

View File

@ -1,48 +0,0 @@
From 3f7342671341a7a137f2d8b06ab3461cdb0e1d88 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Mon, 6 Feb 2023 11:20:31 +0900
Subject: [PATCH] Properly NULL-terminate GSS receive buffer on error packet
reception
pqsecure_open_gss() includes a code path handling error messages with
v2-style protocol messages coming from the server. The client-side
buffer holding the error message does not force a NULL-termination, with
the data of the server getting copied to the errorMessage of the
connection. Hence, it would be possible for a server to send an
unterminated string and copy arbitrary bytes in the buffer receiving the
error message in the client, opening the door to a crash or even data
exposure.
As at this stage of the authentication process the exchange has not been
completed yet, this could be abused by an attacker without Kerberos
credentials. Clients that have a valid kerberos cache are vulnerable as
libpq opportunistically requests for it except if gssencmode is
disabled.
Author: Jacob Champion
Backpatch-through: 12
Security: CVE-2022-41862
CVE: CVE-2022-41862
Upstream-Status: Backport [https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=3f7342671341a7a137f2d8b06ab3461cdb0e1d88]
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
---
src/interfaces/libpq/fe-secure-gssapi.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c
index 7b5e383..aef201b 100644
--- a/src/interfaces/libpq/fe-secure-gssapi.c
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -578,6 +578,8 @@ pqsecure_open_gss(PGconn *conn)
PqGSSRecvLength += ret;
+ Assert(PqGSSRecvLength < PQ_GSS_RECV_BUFFER_SIZE);
+ PqGSSRecvBuffer[PqGSSRecvLength] = '\0';
printfPQExpBuffer(&conn->errorMessage, "%s\n", PqGSSRecvBuffer + 1);
return PGRES_POLLING_FAILED;
--
2.25.1

View File

@ -1,235 +0,0 @@
From 23cb8eaeb97df350273cb8902e55842a955339c8 Mon Sep 17 00:00:00 2001
From: Noah Misch <noah@leadboat.com>
Date: Mon, 8 May 2023 06:14:07 -0700
Subject: [PATCH] Replace last PushOverrideSearchPath() call with
set_config_option().
The two methods don't cooperate, so set_config_option("search_path",
...) has been ineffective under non-empty overrideStack. This defect
enabled an attacker having database-level CREATE privilege to execute
arbitrary code as the bootstrap superuser. While that particular attack
requires v13+ for the trusted extension attribute, other attacks are
feasible in all supported versions.
Standardize on the combination of NewGUCNestLevel() and
set_config_option("search_path", ...). It is newer than
PushOverrideSearchPath(), more-prevalent, and has no known
disadvantages. The "override" mechanism remains for now, for
compatibility with out-of-tree code. Users should update such code,
which likely suffers from the same sort of vulnerability closed here.
Back-patch to v11 (all supported versions).
Alexander Lakhin. Reported by Alexander Lakhin.
Security: CVE-2023-2454
Upstream-Status: Backport [https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=23cb8eaeb97df350273cb8902e55842a955339c8]
CVE: CVE-2023-2454
Signed-off-by: Vivek Kumbhar <vkumbhar@mvista.com>
---
src/backend/catalog/namespace.c | 4 +++
src/backend/commands/schemacmds.c | 37 ++++++++++++++------
src/test/regress/expected/namespace.out | 45 +++++++++++++++++++++++++
src/test/regress/sql/namespace.sql | 24 +++++++++++++
4 files changed, 100 insertions(+), 10 deletions(-)
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 48a7058..74a0536 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -3456,6 +3456,10 @@ OverrideSearchPathMatchesCurrent(OverrideSearchPath *path)
/*
* PushOverrideSearchPath - temporarily override the search path
*
+ * Do not use this function; almost any usage introduces a security
+ * vulnerability. It exists for the benefit of legacy code running in
+ * non-security-sensitive environments.
+ *
* We allow nested overrides, hence the push/pop terminology. The GUC
* search_path variable is ignored while an override is active.
*
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index 6bc4edc..27b1241 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -29,6 +29,7 @@
#include "commands/schemacmds.h"
#include "miscadmin.h"
#include "parser/parse_utilcmd.h"
+#include "parser/scansup.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
@@ -53,14 +54,16 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
{
const char *schemaName = stmt->schemaname;
Oid namespaceId;
- OverrideSearchPath *overridePath;
List *parsetree_list;
ListCell *parsetree_item;
Oid owner_uid;
Oid saved_uid;
int save_sec_context;
+ int save_nestlevel;
+ char *nsp = namespace_search_path;
AclResult aclresult;
ObjectAddress address;
+ StringInfoData pathbuf;
GetUserIdAndSecContext(&saved_uid, &save_sec_context);
@@ -153,14 +156,26 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
CommandCounterIncrement();
/*
- * Temporarily make the new namespace be the front of the search path, as
- * well as the default creation target namespace. This will be undone at
- * the end of this routine, or upon error.
+ * Prepend the new schema to the current search path.
+ *
+ * We use the equivalent of a function SET option to allow the setting to
+ * persist for exactly the duration of the schema creation. guc.c also
+ * takes care of undoing the setting on error.
*/
- overridePath = GetOverrideSearchPath(CurrentMemoryContext);
- overridePath->schemas = lcons_oid(namespaceId, overridePath->schemas);
- /* XXX should we clear overridePath->useTemp? */
- PushOverrideSearchPath(overridePath);
+ save_nestlevel = NewGUCNestLevel();
+
+ initStringInfo(&pathbuf);
+ appendStringInfoString(&pathbuf, quote_identifier(schemaName));
+
+ while (scanner_isspace(*nsp))
+ nsp++;
+
+ if (*nsp != '\0')
+ appendStringInfo(&pathbuf, ", %s", nsp);
+
+ (void) set_config_option("search_path", pathbuf.data,
+ PGC_USERSET, PGC_S_SESSION,
+ GUC_ACTION_SAVE, true, 0, false);
/*
* Report the new schema to possibly interested event triggers. Note we
@@ -213,8 +228,10 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
CommandCounterIncrement();
}
- /* Reset search path to normal state */
- PopOverrideSearchPath();
+ /*
+ * Restore the GUC variable search_path we set above.
+ */
+ AtEOXact_GUC(true, save_nestlevel);
/* Reset current user and security context */
SetUserIdAndSecContext(saved_uid, save_sec_context);
diff --git a/src/test/regress/expected/namespace.out b/src/test/regress/expected/namespace.out
index 2564d1b..a62fd8d 100644
--- a/src/test/regress/expected/namespace.out
+++ b/src/test/regress/expected/namespace.out
@@ -1,6 +1,14 @@
--
-- Regression tests for schemas (namespaces)
--
+-- set the whitespace-only search_path to test that the
+-- GUC list syntax is preserved during a schema creation
+SELECT pg_catalog.set_config('search_path', ' ', false);
+ set_config
+------------
+
+(1 row)
+
CREATE SCHEMA test_ns_schema_1
CREATE UNIQUE INDEX abc_a_idx ON abc (a)
CREATE VIEW abc_view AS
@@ -9,6 +17,43 @@ CREATE SCHEMA test_ns_schema_1
a serial,
b int UNIQUE
);
+-- verify that the correct search_path restored on abort
+SET search_path to public;
+BEGIN;
+SET search_path to public, test_ns_schema_1;
+CREATE SCHEMA test_ns_schema_2
+ CREATE VIEW abc_view AS SELECT c FROM abc;
+ERROR: column "c" does not exist
+LINE 2: CREATE VIEW abc_view AS SELECT c FROM abc;
+ ^
+COMMIT;
+SHOW search_path;
+ search_path
+-------------
+ public
+(1 row)
+
+-- verify that the correct search_path preserved
+-- after creating the schema and on commit
+BEGIN;
+SET search_path to public, test_ns_schema_1;
+CREATE SCHEMA test_ns_schema_2
+ CREATE VIEW abc_view AS SELECT a FROM abc;
+SHOW search_path;
+ search_path
+--------------------------
+ public, test_ns_schema_1
+(1 row)
+
+COMMIT;
+SHOW search_path;
+ search_path
+--------------------------
+ public, test_ns_schema_1
+(1 row)
+
+DROP SCHEMA test_ns_schema_2 CASCADE;
+NOTICE: drop cascades to view test_ns_schema_2.abc_view
-- verify that the objects were created
SELECT COUNT(*) FROM pg_class WHERE relnamespace =
(SELECT oid FROM pg_namespace WHERE nspname = 'test_ns_schema_1');
diff --git a/src/test/regress/sql/namespace.sql b/src/test/regress/sql/namespace.sql
index 6b12c96..3474f5e 100644
--- a/src/test/regress/sql/namespace.sql
+++ b/src/test/regress/sql/namespace.sql
@@ -2,6 +2,10 @@
-- Regression tests for schemas (namespaces)
--
+-- set the whitespace-only search_path to test that the
+-- GUC list syntax is preserved during a schema creation
+SELECT pg_catalog.set_config('search_path', ' ', false);
+
CREATE SCHEMA test_ns_schema_1
CREATE UNIQUE INDEX abc_a_idx ON abc (a)
@@ -13,6 +17,26 @@ CREATE SCHEMA test_ns_schema_1
b int UNIQUE
);
+-- verify that the correct search_path restored on abort
+SET search_path to public;
+BEGIN;
+SET search_path to public, test_ns_schema_1;
+CREATE SCHEMA test_ns_schema_2
+ CREATE VIEW abc_view AS SELECT c FROM abc;
+COMMIT;
+SHOW search_path;
+
+-- verify that the correct search_path preserved
+-- after creating the schema and on commit
+BEGIN;
+SET search_path to public, test_ns_schema_1;
+CREATE SCHEMA test_ns_schema_2
+ CREATE VIEW abc_view AS SELECT a FROM abc;
+SHOW search_path;
+COMMIT;
+SHOW search_path;
+DROP SCHEMA test_ns_schema_2 CASCADE;
+
-- verify that the objects were created
SELECT COUNT(*) FROM pg_class WHERE relnamespace =
(SELECT oid FROM pg_namespace WHERE nspname = 'test_ns_schema_1');
--
2.25.1

View File

@ -1,118 +0,0 @@
From 473626cf00babd829eb15c36b51dfb358d32bc95 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 8 May 2023 10:12:45 -0400
Subject: [PATCH] Handle RLS dependencies in inlined set-returning functions
properly.
If an SRF in the FROM clause references a table having row-level
security policies, and we inline that SRF into the calling query,
we neglected to mark the plan as potentially dependent on which
role is executing it. This could lead to later executions in the
same session returning or hiding rows that should have been hidden
or returned instead.
Our thanks to Wolfgang Walther for reporting this problem.
Stephen Frost and Tom Lane
Security: CVE-2023-2455
Upstream-Status: Backport [https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=473626cf00babd829eb15c36b51dfb358d32bc95]
CVE: CVE-2023-2455
Signed-off-by: Vivek Kumbhar <vkumbhar@mvista.com>
---
src/backend/optimizer/util/clauses.c | 7 ++++++
src/test/regress/expected/rowsecurity.out | 27 +++++++++++++++++++++++
src/test/regress/sql/rowsecurity.sql | 20 +++++++++++++++++
3 files changed, 54 insertions(+)
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 946e232..b8e469f 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -5142,6 +5142,13 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
*/
record_plan_function_dependency(root, func_oid);
+ /*
+ * We must also notice if the inserted query adds a dependency on the
+ * calling role due to RLS quals.
+ */
+ if (querytree->hasRowSecurity)
+ root->glob->dependsOnRole = true;
+
return querytree;
/* Here if func is not inlinable: release temp memory and return NULL */
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index 5116e23..26eecd0 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -4001,6 +4001,33 @@ SELECT * FROM rls_tbl;
DROP TABLE rls_tbl;
RESET SESSION AUTHORIZATION;
+-- CVE-2023-2455: inlining an SRF may introduce an RLS dependency
+create table rls_t (c text);
+insert into rls_t values ('invisible to bob');
+alter table rls_t enable row level security;
+grant select on rls_t to regress_rls_alice, regress_rls_bob;
+create policy p1 on rls_t for select to regress_rls_alice using (true);
+create policy p2 on rls_t for select to regress_rls_bob using (false);
+create function rls_f () returns setof rls_t
+ stable language sql
+ as $$ select * from rls_t $$;
+prepare q as select current_user, * from rls_f();
+set role regress_rls_alice;
+execute q;
+ current_user | c
+-------------------+------------------
+ regress_rls_alice | invisible to bob
+(1 row)
+
+set role regress_rls_bob;
+execute q;
+ current_user | c
+--------------+---
+(0 rows)
+
+RESET ROLE;
+DROP FUNCTION rls_f();
+DROP TABLE rls_t;
--
-- Clean up objects
--
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index 178eeb0..83d99e3 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -1839,6 +1839,26 @@ SELECT * FROM rls_tbl;
DROP TABLE rls_tbl;
RESET SESSION AUTHORIZATION;
+-- CVE-2023-2455: inlining an SRF may introduce an RLS dependency
+create table rls_t (c text);
+insert into rls_t values ('invisible to bob');
+alter table rls_t enable row level security;
+grant select on rls_t to regress_rls_alice, regress_rls_bob;
+create policy p1 on rls_t for select to regress_rls_alice using (true);
+create policy p2 on rls_t for select to regress_rls_bob using (false);
+create function rls_f () returns setof rls_t
+ stable language sql
+ as $$ select * from rls_t $$;
+prepare q as select current_user, * from rls_f();
+set role regress_rls_alice;
+execute q;
+set role regress_rls_bob;
+execute q;
+
+RESET ROLE;
+DROP FUNCTION rls_f();
+DROP TABLE rls_t;
+
--
-- Clean up objects
--
--
2.25.1

View File

@ -1,38 +0,0 @@
Remove duplicate code for riscv
Upstream-Status: Pending
Signed-off-by: Khem Raj <raj.khem@gmail.com>
--- a/src/include/storage/s_lock.h
+++ b/src/include/storage/s_lock.h
@@ -341,30 +341,6 @@ tas(volatile slock_t *lock)
#endif /* HAVE_GCC__SYNC_INT32_TAS */
#endif /* __arm__ || __arm || __aarch64__ || __aarch64 || __riscv */
-
-/*
- * RISC-V likewise uses __sync_lock_test_and_set(int *, int) if available.
- */
-#if defined(__riscv)
-#ifdef HAVE_GCC__SYNC_INT32_TAS
-#define HAS_TEST_AND_SET
-
-#define TAS(lock) tas(lock)
-
-typedef int slock_t;
-
-static __inline__ int
-tas(volatile slock_t *lock)
-{
- return __sync_lock_test_and_set(lock, 1);
-}
-
-#define S_UNLOCK(lock) __sync_lock_release(lock)
-
-#endif /* HAVE_GCC__SYNC_INT32_TAS */
-#endif /* __riscv */
-
-
/* S/390 and S/390x Linux (32- and 64-bit zSeries) */
#if defined(__s390__) || defined(__s390x__)
#define HAS_TEST_AND_SET

View File

@ -0,0 +1,11 @@
require postgresql.inc
LIC_FILES_CHKSUM = "file://COPYRIGHT;md5=c31f662bb2bfb3b4187fe9a53e0ffe7c"
SRC_URI += "\
file://not-check-libperl.patch \
file://0001-Add-support-for-RISC-V.patch \
file://0001-Improve-reproducibility.patch \
"
SRC_URI[sha256sum] = "c5f1fff7a0f93e1ec3746417b0594290ece617b4995ed95b8d527af0ba0e38f3"

View File

@ -1,17 +0,0 @@
require postgresql.inc
LIC_FILES_CHKSUM = "file://COPYRIGHT;md5=255f15687738db8068fbe9b938c90217"
SRC_URI += "\
file://not-check-libperl.patch \
file://0001-Add-support-for-RISC-V.patch \
file://0001-Improve-reproducibility.patch \
file://remove_duplicate.patch \
file://CVE-2022-1552.patch \
file://CVE-2022-2625.patch \
file://CVE-2022-41862.patch \
file://CVE-2023-2454.patch \
file://CVE-2023-2455.patch \
"
SRC_URI[sha256sum] = "89fda2de33ed04a98548e43f3ee5f15b882be17505d631fe0dd1a540a2b56dce"