This is an automated email from the ASF dual-hosted git repository.

reshke pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudberry.git

commit 906a1cc4f5ea8f0b405f69f9720b61c968a41697
Author: Nathan Bossart <[email protected]>
AuthorDate: Mon Aug 10 06:38:25 2026 -0700

    Invalidate plan cache after role changes.
    
    Role membership, role attribute, and database ownership changes may
    impact the expected behavior of row-level security policies, but
    currently the plan cache doesn't take notice.  To fix, register
    syscache callbacks on pg_auth_members, pg_authid, and pg_database
    that invalidate the role-dependent plans.  Changes to other
    databases' pg_database rows are ignored.
    
    Reported-by: Ilya Staroverov <[email protected]>
    Reported-by: Shinya Kato <[email protected]>
    Author: Ilya Staroverov <[email protected]>
    Author: Shinya Kato <[email protected]>
    Co-authored-by: Nathan Bossart <[email protected]>
    Reviewed-by: Tom Lane <[email protected]>
    Security: CVE-2026-14666
    Backpatch-through: 14
---
 src/backend/utils/adt/acl.c         |  2 +-
 src/backend/utils/cache/plancache.c | 60 ++++++++++++++++++++++++++++++++++++-
 src/include/utils/acl.h             |  3 ++
 3 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 5b01b7753df..545f61b809c 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -80,7 +80,7 @@ enum RoleRecurseType
 };
 static Oid     cached_role[] = {InvalidOid, InvalidOid, InvalidOid};
 static List *cached_roles[] = {NIL, NIL, NIL};
-static uint32 cached_db_hash;
+uint32         cached_db_hash;
 
 
 static const char *getid(const char *s, char *n, Node *escontext);
diff --git a/src/backend/utils/cache/plancache.c 
b/src/backend/utils/cache/plancache.c
index 06c909b0aa8..e4c7f18313f 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -36,7 +36,10 @@
  * certain other system catalogs, such as pg_namespace; but for them, our
  * response is just to invalidate all plans.  We expect updates on those
  * catalogs to be infrequent enough that more-detailed tracking is not worth
- * the effort.
+ * the effort.  We likewise watch pg_authid, pg_auth_members, and
+ * pg_database, which can change which row-level security policies apply.
+ * Since those are shared catalogs whose inval events reach every backend
+ * in the cluster, we invalidate only the role-dependent plans.
  *
  * In addition to full-fledged query plans, we provide a facility for
  * detecting invalidations of simple scalar expressions.  This is fairly
@@ -67,6 +70,7 @@
 #include "storage/lmgr.h"
 #include "tcop/pquery.h"
 #include "tcop/utility.h"
+#include "utils/acl.h"
 #include "utils/inval.h"
 #include "utils/memutils.h"
 #include "utils/resowner_private.h"
@@ -122,6 +126,7 @@ static bool ScanQueryWalker(Node *node, bool *acquire);
 static TupleDesc PlanCacheComputeResultDesc(List *stmt_list);
 static void PlanCacheRelCallback(Datum arg, Oid relid);
 static void PlanCacheObjectCallback(Datum arg, int cacheid, uint32 hashvalue);
+static void PlanCacheRoleCallback(Datum arg, int cacheid, uint32 hashvalue);
 static void PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue);
 
 /* GUC parameter */
@@ -143,6 +148,9 @@ InitPlanCache(void)
        CacheRegisterSyscacheCallback(AMOPOPID, PlanCacheSysCallback, (Datum) 
0);
        CacheRegisterSyscacheCallback(FOREIGNSERVEROID, PlanCacheSysCallback, 
(Datum) 0);
        CacheRegisterSyscacheCallback(FOREIGNDATAWRAPPEROID, 
PlanCacheSysCallback, (Datum) 0);
+       CacheRegisterSyscacheCallback(AUTHMEMROLEMEM, PlanCacheRoleCallback, 
(Datum) 0);
+       CacheRegisterSyscacheCallback(AUTHOID, PlanCacheRoleCallback, (Datum) 
0);
+       CacheRegisterSyscacheCallback(DATABASEOID, PlanCacheRoleCallback, 
(Datum) 0);
 }
 
 /*
@@ -2254,6 +2262,56 @@ PlanCacheObjectCallback(Datum arg, int cacheid, uint32 
hashvalue)
        }
 }
 
+/*
+ * PlanCacheRoleCallback
+ *             Syscache inval callback function for AUTHMEMROLEMEM, AUTHOID, 
and
+ *             DATABASEOID caches
+ *
+ * Role membership, role attributes, and database ownership (which confers
+ * membership in pg_database_owner) affect planning by way of row-level
+ * security, so invalidate just the role-dependent plans.  For DATABASEOID, we
+ * can ignore changes to other databases' pg_database rows.
+ */
+static void
+PlanCacheRoleCallback(Datum arg, int cacheid, uint32 hashvalue)
+{
+       dlist_iter      iter;
+
+       if (cacheid == DATABASEOID &&
+               hashvalue != cached_db_hash &&
+               hashvalue != 0)
+               return;                                 /* ignore pg_database 
changes for other DBs */
+
+       dlist_foreach(iter, &saved_plan_list)
+       {
+               CachedPlanSource *plansource = dlist_container(CachedPlanSource,
+                                                                               
                           node, iter.cur);
+
+               Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
+
+               /* No work if it's already invalidated */
+               if (!plansource->is_valid)
+                       continue;
+
+               /* Never invalidate if parse/plan would be a no-op anyway */
+               if (!StmtPlanRequiresRevalidation(plansource))
+                       continue;
+
+               if (plansource->dependsOnRLS)
+               {
+                       /* Invalidate the querytree and generic plan */
+                       plansource->is_valid = false;
+                       if (plansource->gplan)
+                               plansource->gplan->is_valid = false;
+               }
+               else if (plansource->gplan && plansource->gplan->dependsOnRole)
+               {
+                       /* Invalidate the generic plan only */
+                       plansource->gplan->is_valid = false;
+               }
+       }
+}
+
 /*
  * PlanCacheSysCallback
  *             Syscache inval callback function for other caches
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index e3ea31d83f5..4ce5d40201f 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -235,6 +235,9 @@ extern void select_best_grantor(Oid roleId, AclMode 
privileges,
                                                                const Acl *acl, 
Oid ownerId,
                                                                Oid *grantorId, 
AclMode *grantOptions);
 
+/* DATABASEOID syscache hash value for our own database, set by initialize_acl 
*/
+extern uint32 cached_db_hash;
+
 extern void initialize_acl(void);
 
 extern bool revoked_something;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to