agora inbox for [email protected]
help / color / mirror / Atom feedRe: PATCH: warn about, and deprecate, clear text passwords
8+ messages / 3 participants
[nested] [flat]
* Re: PATCH: warn about, and deprecate, clear text passwords
@ 2026-01-09 14:57 Greg Sabino Mullane <[email protected]>
0 siblings, 1 reply; 8+ messages in thread
From: Greg Sabino Mullane @ 2026-01-09 14:57 UTC (permalink / raw)
To: Nathan Bossart <[email protected]>; +Cc: Robert Haas <[email protected]>; David G. Johnston <[email protected]>; Tom Lane <[email protected]>; Isaac Morland <[email protected]>; Aleksander Alekseev <[email protected]>; pgsql-hackers
On Wed, Mar 19, 2025 at 11:01 AM Nathan Bossart <[email protected]>
wrote:
> One of the main reasons I'm not totally sold on a clear-text password
> warning is because we don't have
> agreement on removing that ability anytime soon, not to mention
> Bruce's point about the debate extending into mid-March.
>
Okay, we are now safely past last March :). Any further thoughts on this?
My preference is still warn by default, but I could also be on board with
ignore by default.
Nathan, I'm not sure that having a concrete resolve to someday remove
cleartext passwords should be a blocker to warning now that they are a bad
idea, but open to discussion there.
Cheers,
Greg
^ permalink raw reply [nested|flat] 8+ messages in thread
* Re: PATCH: warn about, and deprecate, clear text passwords
@ 2026-01-09 16:28 Robert Treat <[email protected]>
parent: Greg Sabino Mullane <[email protected]>
0 siblings, 0 replies; 8+ messages in thread
From: Robert Treat @ 2026-01-09 16:28 UTC (permalink / raw)
To: Greg Sabino Mullane <[email protected]>; +Cc: Nathan Bossart <[email protected]>; Robert Haas <[email protected]>; David G. Johnston <[email protected]>; Tom Lane <[email protected]>; Isaac Morland <[email protected]>; Aleksander Alekseev <[email protected]>; pgsql-hackers
On Fri, Jan 9, 2026 at 9:58 AM Greg Sabino Mullane <[email protected]> wrote:
> On Wed, Mar 19, 2025 at 11:01 AM Nathan Bossart <[email protected]> wrote:
>>
>> One of the main reasons I'm not totally sold on a clear-text password warning is because we don't have
>> agreement on removing that ability anytime soon, not to mention Bruce's point about the debate extending into mid-March.
>
>
>
> Okay, we are now safely past last March :). Any further thoughts on this? My preference is still warn by default, but I could also be on board with ignore by default.
>
> Nathan, I'm not sure that having a concrete resolve to someday remove cleartext passwords should be a blocker to warning now that they are a bad idea, but open to discussion there.
>
But Is it a bad idea?In the case of md5, it was a bad idea, because we
were going to remove support for md5, so we knew that people who
clearly care about security were going to need to make a change to
*something*, and giving them a heads up was certainly a good idea
about that.
In the cleartext password case, if we aren't going to remove cleartext
passwords (and TBH, I really can't fathom that we would remove them
entirely), then the warning is just us having opinions about other
people's setup that we can't possibly be more informed about.
So I'm generally -1 on the idea of needing a warning and more so on
the idea it should be the default. And while I do think an option that
allows administrators to disable cleartext passwords seems potentially
useful, for all the gnashing of teeth about it, I don't see a lot of
adoption of things like https://github.com/HexaCluster/credcheck.
Robert Treat
https://xzilla.net
^ permalink raw reply [nested|flat] 8+ messages in thread
* [PATCH v2 2/2] Protect role resolution in roleSpecsToIds() against concurrent DROP
@ 2026-07-06 08:28 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 8+ messages in thread
From: Bertrand Drouvot @ 2026-07-06 08:28 UTC (permalink / raw)
roleSpecsToIds() resolves role names to OIDs without acquiring any lock.
A concurrent DROP ROLE that commits between this resolution and the caller's use
of the OID leaves the caller operating on a stale OID, which can create orphaned
pg_auth_members entries.
Fix this by acquiring AccessShareLock on each resolved role within
roleSpecsToIds(), ensuring the role cannot be dropped while any caller is using
its OID.
Author: Bertrand Drouvot <[email protected]>
Reported-by: Virender Singla <[email protected]>
Reviewed-by:
Discussion: https://postgr.es/m/aki6fMNLUx6%2BBR8K%40bdtpg
Discussion: https://postgr.es/m/CAM6Zo8woa62ZFHtMKox6a4jb8qQ%3Dw87R2L0K8347iE-juQL2EA%40mail.gmail.com
---
src/backend/commands/user.c | 13 +++++-
.../expected/role-membership-drop-member.out | 36 ++++++++++++++++
src/test/isolation/isolation_schedule | 1 +
.../specs/role-membership-drop-member.spec | 43 +++++++++++++++++++
4 files changed, 92 insertions(+), 1 deletion(-)
13.8% src/backend/commands/
42.9% src/test/isolation/expected/
42.2% src/test/isolation/specs/
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 5b869e91c17..05ec753f4f0 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -1778,6 +1778,8 @@ ReassignOwnedObjects(ReassignOwnedStmt *stmt)
* roleSpecsToIds
*
* Given a list of RoleSpecs, generate a list of role OIDs in the same order.
+ * Each role is locked with AccessShareLock to prevent concurrent DROP ROLE
+ * from removing it between resolution and the caller's catalog update.
*
* ROLESPEC_PUBLIC is not allowed.
*/
@@ -1792,7 +1794,16 @@ roleSpecsToIds(List *memberNames)
RoleSpec *rolespec = lfirst_node(RoleSpec, l);
Oid roleid;
- roleid = get_rolespec_oid(rolespec, false);
+ if (rolespec->roletype == ROLESPEC_CSTRING)
+ roleid = RoleNameGetOid(rolespec->rolename,
+ AccessShareLock, false,
+ NULL, NULL);
+ else
+ {
+ roleid = get_rolespec_oid(rolespec, false);
+ LockSharedObject(AuthIdRelationId, roleid, 0,
+ AccessShareLock);
+ }
result = lappend_oid(result, roleid);
}
return result;
diff --git a/src/test/isolation/expected/role-membership-drop-member.out b/src/test/isolation/expected/role-membership-drop-member.out
new file mode 100644
index 00000000000..e6cae59d2c9
--- /dev/null
+++ b/src/test/isolation/expected/role-membership-drop-member.out
@@ -0,0 +1,36 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_grant s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_grant: GRANT regress_role_group TO regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_alter_add s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_alter_add: ALTER GROUP regress_role_group ADD USER regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_create_role s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_create_role: CREATE ROLE regress_role_new ROLE regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_drop_owned s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_drop_owned: DROP OWNED BY regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_reassign_owned s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_reassign_owned: REASSIGN OWNED BY regress_role_member TO regress_role_group;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index b8ebe92553c..8fb8b52b77f 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -128,3 +128,4 @@ test: matview-write-skew
test: lock-nowait
test: for-portion-of
test: ddl-dependency-locking
+test: role-membership-drop-member
diff --git a/src/test/isolation/specs/role-membership-drop-member.spec b/src/test/isolation/specs/role-membership-drop-member.spec
new file mode 100644
index 00000000000..e0140826e52
--- /dev/null
+++ b/src/test/isolation/specs/role-membership-drop-member.spec
@@ -0,0 +1,43 @@
+# Test that role membership commands properly lock the grantee/member
+# role to prevent concurrent DROP ROLE from creating orphaned # pg_auth_members
+# entries.
+
+setup
+{
+ CREATE ROLE regress_role_group;
+ CREATE ROLE regress_role_member;
+}
+
+teardown
+{
+ DROP ROLE IF EXISTS regress_role_group;
+ DROP ROLE IF EXISTS regress_role_member;
+ DROP ROLE IF EXISTS regress_role_new;
+}
+
+session s1
+step s1_begin { BEGIN; }
+step s1_grant { GRANT regress_role_group TO regress_role_member; }
+step s1_alter_add { ALTER GROUP regress_role_group ADD USER regress_role_member; }
+step s1_create_role { CREATE ROLE regress_role_new ROLE regress_role_member; }
+step s1_drop_owned { DROP OWNED BY regress_role_member; }
+step s1_reassign_owned { REASSIGN OWNED BY regress_role_member TO regress_role_group; }
+step s1_commit { COMMIT; }
+
+session s2
+step s2_drop_member { DROP ROLE regress_role_member; }
+
+# GRANT role TO member - concurrent DROP of the member
+permutation s1_begin s1_grant s2_drop_member s1_commit
+
+# ALTER ROLE ADD USER - concurrent DROP of the member
+permutation s1_begin s1_alter_add s2_drop_member s1_commit
+
+# CREATE ROLE ... ROLE member - concurrent DROP of the member
+permutation s1_begin s1_create_role s2_drop_member s1_commit
+
+# DROP OWNED BY role - concurrent DROP of the role
+permutation s1_begin s1_drop_owned s2_drop_member s1_commit
+
+# REASSIGN OWNED BY role - concurrent DROP of the role
+permutation s1_begin s1_reassign_owned s2_drop_member s1_commit
--
2.34.1
--cOHldBwZEf1OqVbN--
^ permalink raw reply [nested|flat] 8+ messages in thread
* [PATCH v3 2/2] Protect role resolution in roleSpecsToIds() against concurrent DROP
@ 2026-07-06 08:28 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 8+ messages in thread
From: Bertrand Drouvot @ 2026-07-06 08:28 UTC (permalink / raw)
roleSpecsToIds() resolves role names to OIDs without acquiring any lock.
A concurrent DROP ROLE that commits between this resolution and the caller's use
of the OID leaves the caller operating on a stale OID, which can create orphaned
pg_auth_members entries.
Fix this by acquiring AccessShareLock on each resolved role within
roleSpecsToIds(), ensuring the role cannot be dropped while any caller is using
its OID.
The AccessShareLock is held until end of transaction, so an open transaction
that performed GRANT, CREATE ROLE ... ROLE, or REASSIGN OWNED BY will block a
concurrent DROP ROLE on the same role until it commits.
Note that this introduces a potential deadlock between GRANT and DROP ROLE
when both target overlapping roles. The deadlock is detected and one session is
aborted with an error, which is preferable to the pre-patch behavior of silently
creating orphaned catalog entries.
Author: Bertrand Drouvot <[email protected]>
Reported-by: Virender Singla <[email protected]>
Reviewed-by: Surya Poondla <[email protected]>
Discussion: https://postgr.es/m/aki6fMNLUx6%2BBR8K%40bdtpg
Discussion: https://postgr.es/m/CAM6Zo8woa62ZFHtMKox6a4jb8qQ%3Dw87R2L0K8347iE-juQL2EA%40mail.gmail.com
---
src/backend/commands/user.c | 21 +++++-
.../expected/role-membership-drop-member.out | 75 +++++++++++++++++++
src/test/isolation/isolation_schedule | 1 +
.../specs/role-membership-drop-member.spec | 51 +++++++++++++
4 files changed, 147 insertions(+), 1 deletion(-)
14.9% src/backend/commands/
48.1% src/test/isolation/expected/
36.1% src/test/isolation/specs/
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 5b869e91c17..375fb527af2 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -1778,6 +1778,8 @@ ReassignOwnedObjects(ReassignOwnedStmt *stmt)
* roleSpecsToIds
*
* Given a list of RoleSpecs, generate a list of role OIDs in the same order.
+ * Each role is locked with AccessShareLock to prevent concurrent DROP ROLE
+ * from removing it between resolution and the caller's catalog update.
*
* ROLESPEC_PUBLIC is not allowed.
*/
@@ -1792,7 +1794,24 @@ roleSpecsToIds(List *memberNames)
RoleSpec *rolespec = lfirst_node(RoleSpec, l);
Oid roleid;
- roleid = get_rolespec_oid(rolespec, false);
+ if (rolespec->roletype == ROLESPEC_CSTRING)
+ roleid = RoleNameGetOid(rolespec->rolename,
+ AccessShareLock, false,
+ NULL, NULL);
+ else
+ {
+ roleid = get_rolespec_oid(rolespec, false);
+ LockSharedObject(AuthIdRelationId, roleid, 0,
+ AccessShareLock);
+
+ /* Recheck that the role still exists after locking. */
+ if (!SearchSysCacheExists1(AUTHOID, ObjectIdGetDatum(roleid)))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("role \"%s\" does not exist",
+ get_rolespec_name(rolespec))));
+ }
+
result = lappend_oid(result, roleid);
}
return result;
diff --git a/src/test/isolation/expected/role-membership-drop-member.out b/src/test/isolation/expected/role-membership-drop-member.out
new file mode 100644
index 00000000000..5b267ea32c2
--- /dev/null
+++ b/src/test/isolation/expected/role-membership-drop-member.out
@@ -0,0 +1,75 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_grant s2_drop_member s1_commit s2_check_orphans
+step s1_begin: BEGIN;
+step s1_grant: GRANT regress_role_group TO regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+step s2_check_orphans:
+ SELECT count(*)
+ FROM pg_auth_members m
+ LEFT JOIN pg_authid ra ON m.roleid = ra.oid
+ LEFT JOIN pg_authid me ON m.member = me.oid
+ LEFT JOIN pg_authid gr ON m.grantor = gr.oid
+ WHERE ra.oid IS NULL OR me.oid IS NULL OR gr.oid IS NULL;
+
+count
+-----
+ 0
+(1 row)
+
+
+starting permutation: s1_begin s1_alter_add s2_drop_member s1_commit s2_check_orphans
+step s1_begin: BEGIN;
+step s1_alter_add: ALTER GROUP regress_role_group ADD USER regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+step s2_check_orphans:
+ SELECT count(*)
+ FROM pg_auth_members m
+ LEFT JOIN pg_authid ra ON m.roleid = ra.oid
+ LEFT JOIN pg_authid me ON m.member = me.oid
+ LEFT JOIN pg_authid gr ON m.grantor = gr.oid
+ WHERE ra.oid IS NULL OR me.oid IS NULL OR gr.oid IS NULL;
+
+count
+-----
+ 0
+(1 row)
+
+
+starting permutation: s1_begin s1_create_role s2_drop_member s1_commit s2_check_orphans
+step s1_begin: BEGIN;
+step s1_create_role: CREATE ROLE regress_role_new ROLE regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+step s2_check_orphans:
+ SELECT count(*)
+ FROM pg_auth_members m
+ LEFT JOIN pg_authid ra ON m.roleid = ra.oid
+ LEFT JOIN pg_authid me ON m.member = me.oid
+ LEFT JOIN pg_authid gr ON m.grantor = gr.oid
+ WHERE ra.oid IS NULL OR me.oid IS NULL OR gr.oid IS NULL;
+
+count
+-----
+ 0
+(1 row)
+
+
+starting permutation: s1_begin s1_drop_owned s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_drop_owned: DROP OWNED BY regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_reassign_owned s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_reassign_owned: REASSIGN OWNED BY regress_role_member TO regress_role_group;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index b8ebe92553c..8fb8b52b77f 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -128,3 +128,4 @@ test: matview-write-skew
test: lock-nowait
test: for-portion-of
test: ddl-dependency-locking
+test: role-membership-drop-member
diff --git a/src/test/isolation/specs/role-membership-drop-member.spec b/src/test/isolation/specs/role-membership-drop-member.spec
new file mode 100644
index 00000000000..989fe996249
--- /dev/null
+++ b/src/test/isolation/specs/role-membership-drop-member.spec
@@ -0,0 +1,51 @@
+# Test that role membership commands properly lock the grantee/member
+# role to prevent concurrent DROP ROLE from creating orphaned pg_auth_members
+# entries, or from operating on a stale OID.
+
+setup
+{
+ CREATE ROLE regress_role_group;
+ CREATE ROLE regress_role_member;
+}
+
+teardown
+{
+ DROP ROLE IF EXISTS regress_role_group;
+ DROP ROLE IF EXISTS regress_role_member;
+ DROP ROLE IF EXISTS regress_role_new;
+}
+
+session s1
+step s1_begin { BEGIN; }
+step s1_grant { GRANT regress_role_group TO regress_role_member; }
+step s1_alter_add { ALTER GROUP regress_role_group ADD USER regress_role_member; }
+step s1_create_role { CREATE ROLE regress_role_new ROLE regress_role_member; }
+step s1_drop_owned { DROP OWNED BY regress_role_member; }
+step s1_reassign_owned { REASSIGN OWNED BY regress_role_member TO regress_role_group; }
+step s1_commit { COMMIT; }
+
+session s2
+step s2_drop_member { DROP ROLE regress_role_member; }
+step s2_check_orphans {
+ SELECT count(*)
+ FROM pg_auth_members m
+ LEFT JOIN pg_authid ra ON m.roleid = ra.oid
+ LEFT JOIN pg_authid me ON m.member = me.oid
+ LEFT JOIN pg_authid gr ON m.grantor = gr.oid
+ WHERE ra.oid IS NULL OR me.oid IS NULL OR gr.oid IS NULL;
+}
+
+# GRANT role TO member - concurrent DROP of the member
+permutation s1_begin s1_grant s2_drop_member s1_commit s2_check_orphans
+
+# ALTER GROUP ADD USER - concurrent DROP of the member
+permutation s1_begin s1_alter_add s2_drop_member s1_commit s2_check_orphans
+
+# CREATE ROLE ... ROLE member - concurrent DROP of the member
+permutation s1_begin s1_create_role s2_drop_member s1_commit s2_check_orphans
+
+# DROP OWNED BY role - concurrent DROP of the role
+permutation s1_begin s1_drop_owned s2_drop_member s1_commit
+
+# REASSIGN OWNED BY role - concurrent DROP of the role
+permutation s1_begin s1_reassign_owned s2_drop_member s1_commit
--
2.34.1
--Dvg/WemKV0I3T/Od--
^ permalink raw reply [nested|flat] 8+ messages in thread
* [PATCH v3 2/2] Protect role resolution in roleSpecsToIds() against concurrent DROP
@ 2026-07-06 08:28 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 8+ messages in thread
From: Bertrand Drouvot @ 2026-07-06 08:28 UTC (permalink / raw)
roleSpecsToIds() resolves role names to OIDs without acquiring any lock.
A concurrent DROP ROLE that commits between this resolution and the caller's use
of the OID leaves the caller operating on a stale OID, which can create orphaned
pg_auth_members entries.
Fix this by acquiring AccessShareLock on each resolved role within
roleSpecsToIds(), ensuring the role cannot be dropped while any caller is using
its OID.
The AccessShareLock is held until end of transaction, so an open transaction
that performed GRANT, CREATE ROLE ... ROLE, or REASSIGN OWNED BY will block a
concurrent DROP ROLE on the same role until it commits.
Note that this introduces a potential deadlock between GRANT and DROP ROLE
when both target overlapping roles. The deadlock is detected and one session is
aborted with an error, which is preferable to the pre-patch behavior of silently
creating orphaned catalog entries.
Author: Bertrand Drouvot <[email protected]>
Reported-by: Virender Singla <[email protected]>
Reviewed-by: Surya Poondla <[email protected]>
Discussion: https://postgr.es/m/aki6fMNLUx6%2BBR8K%40bdtpg
Discussion: https://postgr.es/m/CAM6Zo8woa62ZFHtMKox6a4jb8qQ%3Dw87R2L0K8347iE-juQL2EA%40mail.gmail.com
---
src/backend/commands/user.c | 21 +++++-
.../expected/role-membership-drop-member.out | 75 +++++++++++++++++++
src/test/isolation/isolation_schedule | 1 +
.../specs/role-membership-drop-member.spec | 51 +++++++++++++
4 files changed, 147 insertions(+), 1 deletion(-)
14.9% src/backend/commands/
48.1% src/test/isolation/expected/
36.1% src/test/isolation/specs/
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 5b869e91c17..375fb527af2 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -1778,6 +1778,8 @@ ReassignOwnedObjects(ReassignOwnedStmt *stmt)
* roleSpecsToIds
*
* Given a list of RoleSpecs, generate a list of role OIDs in the same order.
+ * Each role is locked with AccessShareLock to prevent concurrent DROP ROLE
+ * from removing it between resolution and the caller's catalog update.
*
* ROLESPEC_PUBLIC is not allowed.
*/
@@ -1792,7 +1794,24 @@ roleSpecsToIds(List *memberNames)
RoleSpec *rolespec = lfirst_node(RoleSpec, l);
Oid roleid;
- roleid = get_rolespec_oid(rolespec, false);
+ if (rolespec->roletype == ROLESPEC_CSTRING)
+ roleid = RoleNameGetOid(rolespec->rolename,
+ AccessShareLock, false,
+ NULL, NULL);
+ else
+ {
+ roleid = get_rolespec_oid(rolespec, false);
+ LockSharedObject(AuthIdRelationId, roleid, 0,
+ AccessShareLock);
+
+ /* Recheck that the role still exists after locking. */
+ if (!SearchSysCacheExists1(AUTHOID, ObjectIdGetDatum(roleid)))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("role \"%s\" does not exist",
+ get_rolespec_name(rolespec))));
+ }
+
result = lappend_oid(result, roleid);
}
return result;
diff --git a/src/test/isolation/expected/role-membership-drop-member.out b/src/test/isolation/expected/role-membership-drop-member.out
new file mode 100644
index 00000000000..5b267ea32c2
--- /dev/null
+++ b/src/test/isolation/expected/role-membership-drop-member.out
@@ -0,0 +1,75 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_grant s2_drop_member s1_commit s2_check_orphans
+step s1_begin: BEGIN;
+step s1_grant: GRANT regress_role_group TO regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+step s2_check_orphans:
+ SELECT count(*)
+ FROM pg_auth_members m
+ LEFT JOIN pg_authid ra ON m.roleid = ra.oid
+ LEFT JOIN pg_authid me ON m.member = me.oid
+ LEFT JOIN pg_authid gr ON m.grantor = gr.oid
+ WHERE ra.oid IS NULL OR me.oid IS NULL OR gr.oid IS NULL;
+
+count
+-----
+ 0
+(1 row)
+
+
+starting permutation: s1_begin s1_alter_add s2_drop_member s1_commit s2_check_orphans
+step s1_begin: BEGIN;
+step s1_alter_add: ALTER GROUP regress_role_group ADD USER regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+step s2_check_orphans:
+ SELECT count(*)
+ FROM pg_auth_members m
+ LEFT JOIN pg_authid ra ON m.roleid = ra.oid
+ LEFT JOIN pg_authid me ON m.member = me.oid
+ LEFT JOIN pg_authid gr ON m.grantor = gr.oid
+ WHERE ra.oid IS NULL OR me.oid IS NULL OR gr.oid IS NULL;
+
+count
+-----
+ 0
+(1 row)
+
+
+starting permutation: s1_begin s1_create_role s2_drop_member s1_commit s2_check_orphans
+step s1_begin: BEGIN;
+step s1_create_role: CREATE ROLE regress_role_new ROLE regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+step s2_check_orphans:
+ SELECT count(*)
+ FROM pg_auth_members m
+ LEFT JOIN pg_authid ra ON m.roleid = ra.oid
+ LEFT JOIN pg_authid me ON m.member = me.oid
+ LEFT JOIN pg_authid gr ON m.grantor = gr.oid
+ WHERE ra.oid IS NULL OR me.oid IS NULL OR gr.oid IS NULL;
+
+count
+-----
+ 0
+(1 row)
+
+
+starting permutation: s1_begin s1_drop_owned s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_drop_owned: DROP OWNED BY regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_reassign_owned s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_reassign_owned: REASSIGN OWNED BY regress_role_member TO regress_role_group;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index b8ebe92553c..8fb8b52b77f 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -128,3 +128,4 @@ test: matview-write-skew
test: lock-nowait
test: for-portion-of
test: ddl-dependency-locking
+test: role-membership-drop-member
diff --git a/src/test/isolation/specs/role-membership-drop-member.spec b/src/test/isolation/specs/role-membership-drop-member.spec
new file mode 100644
index 00000000000..989fe996249
--- /dev/null
+++ b/src/test/isolation/specs/role-membership-drop-member.spec
@@ -0,0 +1,51 @@
+# Test that role membership commands properly lock the grantee/member
+# role to prevent concurrent DROP ROLE from creating orphaned pg_auth_members
+# entries, or from operating on a stale OID.
+
+setup
+{
+ CREATE ROLE regress_role_group;
+ CREATE ROLE regress_role_member;
+}
+
+teardown
+{
+ DROP ROLE IF EXISTS regress_role_group;
+ DROP ROLE IF EXISTS regress_role_member;
+ DROP ROLE IF EXISTS regress_role_new;
+}
+
+session s1
+step s1_begin { BEGIN; }
+step s1_grant { GRANT regress_role_group TO regress_role_member; }
+step s1_alter_add { ALTER GROUP regress_role_group ADD USER regress_role_member; }
+step s1_create_role { CREATE ROLE regress_role_new ROLE regress_role_member; }
+step s1_drop_owned { DROP OWNED BY regress_role_member; }
+step s1_reassign_owned { REASSIGN OWNED BY regress_role_member TO regress_role_group; }
+step s1_commit { COMMIT; }
+
+session s2
+step s2_drop_member { DROP ROLE regress_role_member; }
+step s2_check_orphans {
+ SELECT count(*)
+ FROM pg_auth_members m
+ LEFT JOIN pg_authid ra ON m.roleid = ra.oid
+ LEFT JOIN pg_authid me ON m.member = me.oid
+ LEFT JOIN pg_authid gr ON m.grantor = gr.oid
+ WHERE ra.oid IS NULL OR me.oid IS NULL OR gr.oid IS NULL;
+}
+
+# GRANT role TO member - concurrent DROP of the member
+permutation s1_begin s1_grant s2_drop_member s1_commit s2_check_orphans
+
+# ALTER GROUP ADD USER - concurrent DROP of the member
+permutation s1_begin s1_alter_add s2_drop_member s1_commit s2_check_orphans
+
+# CREATE ROLE ... ROLE member - concurrent DROP of the member
+permutation s1_begin s1_create_role s2_drop_member s1_commit s2_check_orphans
+
+# DROP OWNED BY role - concurrent DROP of the role
+permutation s1_begin s1_drop_owned s2_drop_member s1_commit
+
+# REASSIGN OWNED BY role - concurrent DROP of the role
+permutation s1_begin s1_reassign_owned s2_drop_member s1_commit
--
2.34.1
--Dvg/WemKV0I3T/Od--
^ permalink raw reply [nested|flat] 8+ messages in thread
* [PATCH v2 2/2] Protect role resolution in roleSpecsToIds() against concurrent DROP
@ 2026-07-06 08:28 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 8+ messages in thread
From: Bertrand Drouvot @ 2026-07-06 08:28 UTC (permalink / raw)
roleSpecsToIds() resolves role names to OIDs without acquiring any lock.
A concurrent DROP ROLE that commits between this resolution and the caller's use
of the OID leaves the caller operating on a stale OID, which can create orphaned
pg_auth_members entries.
Fix this by acquiring AccessShareLock on each resolved role within
roleSpecsToIds(), ensuring the role cannot be dropped while any caller is using
its OID.
Author: Bertrand Drouvot <[email protected]>
Reported-by: Virender Singla <[email protected]>
Reviewed-by:
Discussion: https://postgr.es/m/aki6fMNLUx6%2BBR8K%40bdtpg
Discussion: https://postgr.es/m/CAM6Zo8woa62ZFHtMKox6a4jb8qQ%3Dw87R2L0K8347iE-juQL2EA%40mail.gmail.com
---
src/backend/commands/user.c | 13 +++++-
.../expected/role-membership-drop-member.out | 36 ++++++++++++++++
src/test/isolation/isolation_schedule | 1 +
.../specs/role-membership-drop-member.spec | 43 +++++++++++++++++++
4 files changed, 92 insertions(+), 1 deletion(-)
13.8% src/backend/commands/
42.9% src/test/isolation/expected/
42.2% src/test/isolation/specs/
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 5b869e91c17..05ec753f4f0 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -1778,6 +1778,8 @@ ReassignOwnedObjects(ReassignOwnedStmt *stmt)
* roleSpecsToIds
*
* Given a list of RoleSpecs, generate a list of role OIDs in the same order.
+ * Each role is locked with AccessShareLock to prevent concurrent DROP ROLE
+ * from removing it between resolution and the caller's catalog update.
*
* ROLESPEC_PUBLIC is not allowed.
*/
@@ -1792,7 +1794,16 @@ roleSpecsToIds(List *memberNames)
RoleSpec *rolespec = lfirst_node(RoleSpec, l);
Oid roleid;
- roleid = get_rolespec_oid(rolespec, false);
+ if (rolespec->roletype == ROLESPEC_CSTRING)
+ roleid = RoleNameGetOid(rolespec->rolename,
+ AccessShareLock, false,
+ NULL, NULL);
+ else
+ {
+ roleid = get_rolespec_oid(rolespec, false);
+ LockSharedObject(AuthIdRelationId, roleid, 0,
+ AccessShareLock);
+ }
result = lappend_oid(result, roleid);
}
return result;
diff --git a/src/test/isolation/expected/role-membership-drop-member.out b/src/test/isolation/expected/role-membership-drop-member.out
new file mode 100644
index 00000000000..e6cae59d2c9
--- /dev/null
+++ b/src/test/isolation/expected/role-membership-drop-member.out
@@ -0,0 +1,36 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_grant s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_grant: GRANT regress_role_group TO regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_alter_add s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_alter_add: ALTER GROUP regress_role_group ADD USER regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_create_role s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_create_role: CREATE ROLE regress_role_new ROLE regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_drop_owned s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_drop_owned: DROP OWNED BY regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_reassign_owned s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_reassign_owned: REASSIGN OWNED BY regress_role_member TO regress_role_group;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index b8ebe92553c..8fb8b52b77f 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -128,3 +128,4 @@ test: matview-write-skew
test: lock-nowait
test: for-portion-of
test: ddl-dependency-locking
+test: role-membership-drop-member
diff --git a/src/test/isolation/specs/role-membership-drop-member.spec b/src/test/isolation/specs/role-membership-drop-member.spec
new file mode 100644
index 00000000000..e0140826e52
--- /dev/null
+++ b/src/test/isolation/specs/role-membership-drop-member.spec
@@ -0,0 +1,43 @@
+# Test that role membership commands properly lock the grantee/member
+# role to prevent concurrent DROP ROLE from creating orphaned # pg_auth_members
+# entries.
+
+setup
+{
+ CREATE ROLE regress_role_group;
+ CREATE ROLE regress_role_member;
+}
+
+teardown
+{
+ DROP ROLE IF EXISTS regress_role_group;
+ DROP ROLE IF EXISTS regress_role_member;
+ DROP ROLE IF EXISTS regress_role_new;
+}
+
+session s1
+step s1_begin { BEGIN; }
+step s1_grant { GRANT regress_role_group TO regress_role_member; }
+step s1_alter_add { ALTER GROUP regress_role_group ADD USER regress_role_member; }
+step s1_create_role { CREATE ROLE regress_role_new ROLE regress_role_member; }
+step s1_drop_owned { DROP OWNED BY regress_role_member; }
+step s1_reassign_owned { REASSIGN OWNED BY regress_role_member TO regress_role_group; }
+step s1_commit { COMMIT; }
+
+session s2
+step s2_drop_member { DROP ROLE regress_role_member; }
+
+# GRANT role TO member - concurrent DROP of the member
+permutation s1_begin s1_grant s2_drop_member s1_commit
+
+# ALTER ROLE ADD USER - concurrent DROP of the member
+permutation s1_begin s1_alter_add s2_drop_member s1_commit
+
+# CREATE ROLE ... ROLE member - concurrent DROP of the member
+permutation s1_begin s1_create_role s2_drop_member s1_commit
+
+# DROP OWNED BY role - concurrent DROP of the role
+permutation s1_begin s1_drop_owned s2_drop_member s1_commit
+
+# REASSIGN OWNED BY role - concurrent DROP of the role
+permutation s1_begin s1_reassign_owned s2_drop_member s1_commit
--
2.34.1
--cOHldBwZEf1OqVbN--
^ permalink raw reply [nested|flat] 8+ messages in thread
* [PATCH v2 2/2] Protect role resolution in roleSpecsToIds() against concurrent DROP
@ 2026-07-06 08:28 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 8+ messages in thread
From: Bertrand Drouvot @ 2026-07-06 08:28 UTC (permalink / raw)
roleSpecsToIds() resolves role names to OIDs without acquiring any lock.
A concurrent DROP ROLE that commits between this resolution and the caller's use
of the OID leaves the caller operating on a stale OID, which can create orphaned
pg_auth_members entries.
Fix this by acquiring AccessShareLock on each resolved role within
roleSpecsToIds(), ensuring the role cannot be dropped while any caller is using
its OID.
Author: Bertrand Drouvot <[email protected]>
Reported-by: Virender Singla <[email protected]>
Reviewed-by:
Discussion: https://postgr.es/m/aki6fMNLUx6%2BBR8K%40bdtpg
Discussion: https://postgr.es/m/CAM6Zo8woa62ZFHtMKox6a4jb8qQ%3Dw87R2L0K8347iE-juQL2EA%40mail.gmail.com
---
src/backend/commands/user.c | 13 +++++-
.../expected/role-membership-drop-member.out | 36 ++++++++++++++++
src/test/isolation/isolation_schedule | 1 +
.../specs/role-membership-drop-member.spec | 43 +++++++++++++++++++
4 files changed, 92 insertions(+), 1 deletion(-)
13.8% src/backend/commands/
42.9% src/test/isolation/expected/
42.2% src/test/isolation/specs/
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 5b869e91c17..05ec753f4f0 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -1778,6 +1778,8 @@ ReassignOwnedObjects(ReassignOwnedStmt *stmt)
* roleSpecsToIds
*
* Given a list of RoleSpecs, generate a list of role OIDs in the same order.
+ * Each role is locked with AccessShareLock to prevent concurrent DROP ROLE
+ * from removing it between resolution and the caller's catalog update.
*
* ROLESPEC_PUBLIC is not allowed.
*/
@@ -1792,7 +1794,16 @@ roleSpecsToIds(List *memberNames)
RoleSpec *rolespec = lfirst_node(RoleSpec, l);
Oid roleid;
- roleid = get_rolespec_oid(rolespec, false);
+ if (rolespec->roletype == ROLESPEC_CSTRING)
+ roleid = RoleNameGetOid(rolespec->rolename,
+ AccessShareLock, false,
+ NULL, NULL);
+ else
+ {
+ roleid = get_rolespec_oid(rolespec, false);
+ LockSharedObject(AuthIdRelationId, roleid, 0,
+ AccessShareLock);
+ }
result = lappend_oid(result, roleid);
}
return result;
diff --git a/src/test/isolation/expected/role-membership-drop-member.out b/src/test/isolation/expected/role-membership-drop-member.out
new file mode 100644
index 00000000000..e6cae59d2c9
--- /dev/null
+++ b/src/test/isolation/expected/role-membership-drop-member.out
@@ -0,0 +1,36 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_grant s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_grant: GRANT regress_role_group TO regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_alter_add s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_alter_add: ALTER GROUP regress_role_group ADD USER regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_create_role s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_create_role: CREATE ROLE regress_role_new ROLE regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_drop_owned s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_drop_owned: DROP OWNED BY regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_reassign_owned s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_reassign_owned: REASSIGN OWNED BY regress_role_member TO regress_role_group;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index b8ebe92553c..8fb8b52b77f 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -128,3 +128,4 @@ test: matview-write-skew
test: lock-nowait
test: for-portion-of
test: ddl-dependency-locking
+test: role-membership-drop-member
diff --git a/src/test/isolation/specs/role-membership-drop-member.spec b/src/test/isolation/specs/role-membership-drop-member.spec
new file mode 100644
index 00000000000..e0140826e52
--- /dev/null
+++ b/src/test/isolation/specs/role-membership-drop-member.spec
@@ -0,0 +1,43 @@
+# Test that role membership commands properly lock the grantee/member
+# role to prevent concurrent DROP ROLE from creating orphaned # pg_auth_members
+# entries.
+
+setup
+{
+ CREATE ROLE regress_role_group;
+ CREATE ROLE regress_role_member;
+}
+
+teardown
+{
+ DROP ROLE IF EXISTS regress_role_group;
+ DROP ROLE IF EXISTS regress_role_member;
+ DROP ROLE IF EXISTS regress_role_new;
+}
+
+session s1
+step s1_begin { BEGIN; }
+step s1_grant { GRANT regress_role_group TO regress_role_member; }
+step s1_alter_add { ALTER GROUP regress_role_group ADD USER regress_role_member; }
+step s1_create_role { CREATE ROLE regress_role_new ROLE regress_role_member; }
+step s1_drop_owned { DROP OWNED BY regress_role_member; }
+step s1_reassign_owned { REASSIGN OWNED BY regress_role_member TO regress_role_group; }
+step s1_commit { COMMIT; }
+
+session s2
+step s2_drop_member { DROP ROLE regress_role_member; }
+
+# GRANT role TO member - concurrent DROP of the member
+permutation s1_begin s1_grant s2_drop_member s1_commit
+
+# ALTER ROLE ADD USER - concurrent DROP of the member
+permutation s1_begin s1_alter_add s2_drop_member s1_commit
+
+# CREATE ROLE ... ROLE member - concurrent DROP of the member
+permutation s1_begin s1_create_role s2_drop_member s1_commit
+
+# DROP OWNED BY role - concurrent DROP of the role
+permutation s1_begin s1_drop_owned s2_drop_member s1_commit
+
+# REASSIGN OWNED BY role - concurrent DROP of the role
+permutation s1_begin s1_reassign_owned s2_drop_member s1_commit
--
2.34.1
--cOHldBwZEf1OqVbN--
^ permalink raw reply [nested|flat] 8+ messages in thread
* [PATCH v2 2/2] Protect role resolution in roleSpecsToIds() against concurrent DROP
@ 2026-07-06 08:28 Bertrand Drouvot <[email protected]>
0 siblings, 0 replies; 8+ messages in thread
From: Bertrand Drouvot @ 2026-07-06 08:28 UTC (permalink / raw)
roleSpecsToIds() resolves role names to OIDs without acquiring any lock.
A concurrent DROP ROLE that commits between this resolution and the caller's use
of the OID leaves the caller operating on a stale OID, which can create orphaned
pg_auth_members entries.
Fix this by acquiring AccessShareLock on each resolved role within
roleSpecsToIds(), ensuring the role cannot be dropped while any caller is using
its OID.
Author: Bertrand Drouvot <[email protected]>
Reported-by: Virender Singla <[email protected]>
Reviewed-by:
Discussion: https://postgr.es/m/aki6fMNLUx6%2BBR8K%40bdtpg
Discussion: https://postgr.es/m/CAM6Zo8woa62ZFHtMKox6a4jb8qQ%3Dw87R2L0K8347iE-juQL2EA%40mail.gmail.com
---
src/backend/commands/user.c | 13 +++++-
.../expected/role-membership-drop-member.out | 36 ++++++++++++++++
src/test/isolation/isolation_schedule | 1 +
.../specs/role-membership-drop-member.spec | 43 +++++++++++++++++++
4 files changed, 92 insertions(+), 1 deletion(-)
13.8% src/backend/commands/
42.9% src/test/isolation/expected/
42.2% src/test/isolation/specs/
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 5b869e91c17..05ec753f4f0 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -1778,6 +1778,8 @@ ReassignOwnedObjects(ReassignOwnedStmt *stmt)
* roleSpecsToIds
*
* Given a list of RoleSpecs, generate a list of role OIDs in the same order.
+ * Each role is locked with AccessShareLock to prevent concurrent DROP ROLE
+ * from removing it between resolution and the caller's catalog update.
*
* ROLESPEC_PUBLIC is not allowed.
*/
@@ -1792,7 +1794,16 @@ roleSpecsToIds(List *memberNames)
RoleSpec *rolespec = lfirst_node(RoleSpec, l);
Oid roleid;
- roleid = get_rolespec_oid(rolespec, false);
+ if (rolespec->roletype == ROLESPEC_CSTRING)
+ roleid = RoleNameGetOid(rolespec->rolename,
+ AccessShareLock, false,
+ NULL, NULL);
+ else
+ {
+ roleid = get_rolespec_oid(rolespec, false);
+ LockSharedObject(AuthIdRelationId, roleid, 0,
+ AccessShareLock);
+ }
result = lappend_oid(result, roleid);
}
return result;
diff --git a/src/test/isolation/expected/role-membership-drop-member.out b/src/test/isolation/expected/role-membership-drop-member.out
new file mode 100644
index 00000000000..e6cae59d2c9
--- /dev/null
+++ b/src/test/isolation/expected/role-membership-drop-member.out
@@ -0,0 +1,36 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_grant s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_grant: GRANT regress_role_group TO regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_alter_add s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_alter_add: ALTER GROUP regress_role_group ADD USER regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_create_role s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_create_role: CREATE ROLE regress_role_new ROLE regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_drop_owned s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_drop_owned: DROP OWNED BY regress_role_member;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
+
+starting permutation: s1_begin s1_reassign_owned s2_drop_member s1_commit
+step s1_begin: BEGIN;
+step s1_reassign_owned: REASSIGN OWNED BY regress_role_member TO regress_role_group;
+step s2_drop_member: DROP ROLE regress_role_member; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_member: <... completed>
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index b8ebe92553c..8fb8b52b77f 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -128,3 +128,4 @@ test: matview-write-skew
test: lock-nowait
test: for-portion-of
test: ddl-dependency-locking
+test: role-membership-drop-member
diff --git a/src/test/isolation/specs/role-membership-drop-member.spec b/src/test/isolation/specs/role-membership-drop-member.spec
new file mode 100644
index 00000000000..e0140826e52
--- /dev/null
+++ b/src/test/isolation/specs/role-membership-drop-member.spec
@@ -0,0 +1,43 @@
+# Test that role membership commands properly lock the grantee/member
+# role to prevent concurrent DROP ROLE from creating orphaned # pg_auth_members
+# entries.
+
+setup
+{
+ CREATE ROLE regress_role_group;
+ CREATE ROLE regress_role_member;
+}
+
+teardown
+{
+ DROP ROLE IF EXISTS regress_role_group;
+ DROP ROLE IF EXISTS regress_role_member;
+ DROP ROLE IF EXISTS regress_role_new;
+}
+
+session s1
+step s1_begin { BEGIN; }
+step s1_grant { GRANT regress_role_group TO regress_role_member; }
+step s1_alter_add { ALTER GROUP regress_role_group ADD USER regress_role_member; }
+step s1_create_role { CREATE ROLE regress_role_new ROLE regress_role_member; }
+step s1_drop_owned { DROP OWNED BY regress_role_member; }
+step s1_reassign_owned { REASSIGN OWNED BY regress_role_member TO regress_role_group; }
+step s1_commit { COMMIT; }
+
+session s2
+step s2_drop_member { DROP ROLE regress_role_member; }
+
+# GRANT role TO member - concurrent DROP of the member
+permutation s1_begin s1_grant s2_drop_member s1_commit
+
+# ALTER ROLE ADD USER - concurrent DROP of the member
+permutation s1_begin s1_alter_add s2_drop_member s1_commit
+
+# CREATE ROLE ... ROLE member - concurrent DROP of the member
+permutation s1_begin s1_create_role s2_drop_member s1_commit
+
+# DROP OWNED BY role - concurrent DROP of the role
+permutation s1_begin s1_drop_owned s2_drop_member s1_commit
+
+# REASSIGN OWNED BY role - concurrent DROP of the role
+permutation s1_begin s1_reassign_owned s2_drop_member s1_commit
--
2.34.1
--cOHldBwZEf1OqVbN--
^ permalink raw reply [nested|flat] 8+ messages in thread
end of thread, other threads:[~2026-07-06 08:28 UTC | newest]
Thread overview: 8+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-01-09 14:57 Re: PATCH: warn about, and deprecate, clear text passwords Greg Sabino Mullane <[email protected]>
2026-01-09 16:28 ` Robert Treat <[email protected]>
2026-07-06 08:28 [PATCH v2 2/2] Protect role resolution in roleSpecsToIds() against concurrent DROP Bertrand Drouvot <[email protected]>
2026-07-06 08:28 [PATCH v2 2/2] Protect role resolution in roleSpecsToIds() against concurrent DROP Bertrand Drouvot <[email protected]>
2026-07-06 08:28 [PATCH v3 2/2] Protect role resolution in roleSpecsToIds() against concurrent DROP Bertrand Drouvot <[email protected]>
2026-07-06 08:28 [PATCH v2 2/2] Protect role resolution in roleSpecsToIds() against concurrent DROP Bertrand Drouvot <[email protected]>
2026-07-06 08:28 [PATCH v3 2/2] Protect role resolution in roleSpecsToIds() against concurrent DROP Bertrand Drouvot <[email protected]>
2026-07-06 08:28 [PATCH v2 2/2] Protect role resolution in roleSpecsToIds() against concurrent DROP Bertrand Drouvot <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox