aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/init/miscinit.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2024-12-28 16:08:50 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2024-12-28 16:08:50 -0500
commit3d1ecc92a0d15eb299ea37c9842b3367eddc0081 (patch)
tree97ede5352eef79a0fc3ce729caf6a18d0f1db5f6 /src/backend/utils/init/miscinit.c
parent83bb52375630ce93660bde938ffba93c6dc7fc63 (diff)
downloadpostgresql-3d1ecc92a0d15eb299ea37c9842b3367eddc0081.tar.gz
postgresql-3d1ecc92a0d15eb299ea37c9842b3367eddc0081.zip
Exclude parallel workers from connection privilege/limit checks.
Cause parallel workers to not check datallowconn, rolcanlogin, and ACL_CONNECT privileges. The leader already checked these things (except for rolcanlogin which might have been checked for a different role). Re-checking can accomplish little except to induce unexpected failures in applications that might not even be aware that their query has been parallelized. We already had the principle that parallel workers rely on their leader to pass a valid set of authorization information, so this change just extends that a bit further. Also, modify the ReservedConnections, datconnlimit and rolconnlimit logic so that these limits are only enforced against regular backends, and only regular backends are counted while checking if the limits were already reached. Previously, background processes that had an assigned database or role were subject to these limits (with rather random exclusions for autovac workers and walsenders), and the set of existing processes that counted against each limit was quite haphazard as well. The point of these limits, AFAICS, is to ensure the availability of PGPROC slots for regular backends. Since all other types of processes have their own separate pools of PGPROC slots, it makes no sense either to enforce these limits against them or to count them while enforcing the limit. While edge-case failures of these sorts have been possible for a long time, the problem got a good deal worse with commit 5a2fed911 (CVE-2024-10978), which caused parallel workers to make some of these checks using the leader's current role where before we had used its AuthenticatedUserId, thus allowing parallel queries to fail after SET ROLE. The previous behavior was fairly accidental and I have no desire to return to it. This patch includes reverting 73c9f91a1, which was an emergency hack to suppress these same checks in some cases. It wasn't complete, as shown by a recent bug report from Laurenz Albe. We can also revert fd4d93d26 and 492217301, which hacked around the same problems in one regression test. In passing, remove the special case for autovac workers in CheckMyDatabase; it seems cleaner to have AutoVacWorkerMain pass the INIT_PG_OVERRIDE_ALLOW_CONNS flag, now that that does what's needed. Like 5a2fed911, back-patch to supported branches (which sadly no longer includes v12). Discussion: https://postgr.es/m/1808397.1735156190@sss.pgh.pa.us
Diffstat (limited to 'src/backend/utils/init/miscinit.c')
-rw-r--r--src/backend/utils/init/miscinit.c75
1 files changed, 36 insertions, 39 deletions
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 609ba953c93..e8cbd800d67 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -736,6 +736,16 @@ InitializeSessionUserId(const char *rolename, Oid roleid)
bool is_superuser;
/*
+ * In a parallel worker, we don't have to do anything here.
+ * ParallelWorkerMain already set our output variables, and we aren't
+ * going to enforce either rolcanlogin or rolconnlimit. Furthermore, we
+ * don't really want to perform a catalog lookup for the role: we don't
+ * want to fail if it's been dropped.
+ */
+ if (InitializingParallelWorker)
+ return;
+
+ /*
* Don't do scans if we're bootstrapping, none of the system catalogs
* exist yet, and they should be owned by postgres anyway.
*/
@@ -750,34 +760,22 @@ InitializeSessionUserId(const char *rolename, Oid roleid)
/*
* Look up the role, either by name if that's given or by OID if not.
- * Normally we have to fail if we don't find it, but in parallel workers
- * just return without doing anything: all the critical work has been done
- * already. The upshot of that is that if the role has been deleted, we
- * will not enforce its rolconnlimit against parallel workers anymore.
*/
if (rolename != NULL)
{
roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(rolename));
if (!HeapTupleIsValid(roleTup))
- {
- if (InitializingParallelWorker)
- return;
ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("role \"%s\" does not exist", rolename)));
- }
}
else
{
roleTup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
if (!HeapTupleIsValid(roleTup))
- {
- if (InitializingParallelWorker)
- return;
ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("role with OID %u does not exist", roleid)));
- }
}
rform = (Form_pg_authid) GETSTRUCT(roleTup);
@@ -785,33 +783,29 @@ InitializeSessionUserId(const char *rolename, Oid roleid)
rname = NameStr(rform->rolname);
is_superuser = rform->rolsuper;
- /* In a parallel worker, ParallelWorkerMain already set these variables */
- if (!InitializingParallelWorker)
- {
- SetAuthenticatedUserId(roleid, is_superuser);
+ SetAuthenticatedUserId(roleid, is_superuser);
- /*
- * Set SessionUserId and related variables, including "role", via the
- * GUC mechanisms.
- *
- * Note: ideally we would use PGC_S_DYNAMIC_DEFAULT here, so that
- * session_authorization could subsequently be changed from
- * pg_db_role_setting entries. Instead, session_authorization in
- * pg_db_role_setting has no effect. Changing that would require
- * solving two problems:
- *
- * 1. If pg_db_role_setting has values for both session_authorization
- * and role, we could not be sure which order those would be applied
- * in, and it would matter.
- *
- * 2. Sites may have years-old session_authorization entries. There's
- * not been any particular reason to remove them. Ending the dormancy
- * of those entries could seriously change application behavior, so
- * only a major release should do that.
- */
- SetConfigOption("session_authorization", rname,
- PGC_BACKEND, PGC_S_OVERRIDE);
- }
+ /*
+ * Set SessionUserId and related variables, including "role", via the GUC
+ * mechanisms.
+ *
+ * Note: ideally we would use PGC_S_DYNAMIC_DEFAULT here, so that
+ * session_authorization could subsequently be changed from
+ * pg_db_role_setting entries. Instead, session_authorization in
+ * pg_db_role_setting has no effect. Changing that would require solving
+ * two problems:
+ *
+ * 1. If pg_db_role_setting has values for both session_authorization and
+ * role, we could not be sure which order those would be applied in, and
+ * it would matter.
+ *
+ * 2. Sites may have years-old session_authorization entries. There's not
+ * been any particular reason to remove them. Ending the dormancy of
+ * those entries could seriously change application behavior, so only a
+ * major release should do that.
+ */
+ SetConfigOption("session_authorization", rname,
+ PGC_BACKEND, PGC_S_OVERRIDE);
/*
* These next checks are not enforced when in standalone mode, so that
@@ -830,7 +824,9 @@ InitializeSessionUserId(const char *rolename, Oid roleid)
rname)));
/*
- * Check connection limit for this role.
+ * Check connection limit for this role. We enforce the limit only
+ * for regular backends, since other process types have their own
+ * PGPROC pools.
*
* There is a race condition here --- we create our PGPROC before
* checking for other PGPROCs. If two backends did this at about the
@@ -840,6 +836,7 @@ InitializeSessionUserId(const char *rolename, Oid roleid)
* just document that the connection limit is approximate.
*/
if (rform->rolconnlimit >= 0 &&
+ AmRegularBackendProcess() &&
!is_superuser &&
CountUserBackends(roleid) > rform->rolconnlimit)
ereport(FATAL,