Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wgh6e-006L2e-0M for pgsql-hackers@arkaria.postgresql.org; Mon, 06 Jul 2026 11:03:20 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wgh6c-00H61c-3A for pgsql-hackers@arkaria.postgresql.org; Mon, 06 Jul 2026 11:03:18 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wgh6c-00H61R-22 for pgsql-hackers@lists.postgresql.org; Mon, 06 Jul 2026 11:03:18 +0000 Received: from mail-yw1-x1132.google.com ([2607:f8b0:4864:20::1132]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wgh6a-000000022ir-1Yt1 for pgsql-hackers@lists.postgresql.org; Mon, 06 Jul 2026 11:03:18 +0000 Received: by mail-yw1-x1132.google.com with SMTP id 00721157ae682-8143daf89c7so24915997b3.1 for ; Mon, 06 Jul 2026 04:03:15 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1783335794; cv=none; d=google.com; s=arc-20260327; b=qh6uuNHBqPW3rRgtIAoCUDbGTtLgttozn9qLQqTf8GaQLkYLJ2pnjfhZSFym96a9mh ntlQ6gaOPoCBdefaWcxbEayaqMO2lwUaWMtJZGAoLrNf/7cAIL0EbR37fabY0fHUkm2Q OP3BbiTa6IyERrKbStpCAgXrQhb+Dg2u3tSeAQub/IfOcCWY6TOcRPSlGdRZIJt4YQ2+ ChRC5B21mt8QgaXbKd4r9AKWDhQMJGVqJ9v2cRCHgEjeQviBE+9Bh8tp5wx8VIOJjYzT 6j4H5WFsFbDU6aoAoiZcFx6lbM8sAn0MSsIvwvV4Aog6Ev868ikuc5qKgEOukOEb5qFm Qa2Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=jAoLGF186GsuapBkElccWbn2xJiKA23R/H8Xf62J4Ug=; fh=KJqbGoqY250Tjp1IoJ4/nMPGgzk4wqj0kX2kDwZjgck=; b=r3dytptvGb71klbxJxXpUYaUZzwmHxeNOOUT+3GM6Gc1vhpzGcQc9imjaTq9rrQ5k2 3hzhsKKGRQwkNBISFtWPwaDuC5j6XRAGhon+rHK44QJ3W71hpQBBTiy7Yl4f6YEG7CJr 9xevBa8iif7L9n1VBISVfmEBsStu4wExRt5YT9NoRo1T8MOYqd7fYTx606LGtS0wc10f ywIKFA50H8VWSzdfhuIR+eYi1STB6apCHioKVJ4a0ZzSyOV91uzbYX5fJtSvl1eUgEfO 4WS+25MB/7p7m9kSNhxPrpJCWUDJ5eHRjdArmPG0+6XxIwVoLPEMxUbd/tGyf7mc0xOQ BG9Q==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783335794; x=1783940594; darn=lists.postgresql.org; h=content-type:cc:to:subject:message-id:date:from:in-reply-to :references:mime-version:from:to:cc:subject:date:message-id:reply-to :content-type; bh=jAoLGF186GsuapBkElccWbn2xJiKA23R/H8Xf62J4Ug=; b=YazHU73KEBAb8x6S7r0emTpmCRhw4tC6Of14yuwCMYWmxxP4XX8iDXRwA1KQ3dvJPA MDahS3l8ZWxjpQgytMmRO3OCafycCtQ9f9adDxk/gNXGE4H/8pCpSsH6Mq1ymtEOuNdA ghVJetcVgrvHwKQN8GMS77NOtKNqZKYCUAK019mOfN4/S8WQ1p+Qe96Vr4mHNJswViiC dkafEbE9S9MmfCfVpHmm6IysFx5P7cEDWWL6Ym1Bs46ZIfH2WPyU+O7vvyRVcqt3jQRz jGx5tURkEhf+adoZklSDm4GJKf3rc5rGdxTGg6taHWoZDkMl1zipgts4+lxwPkt8MPsk nxiw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783335794; x=1783940594; h=content-type:cc:to:subject:message-id:date:from:in-reply-to :references:mime-version:x-gm-gg:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to:content-type; bh=jAoLGF186GsuapBkElccWbn2xJiKA23R/H8Xf62J4Ug=; b=ZomEkr2rmXWzel8kIIbYSaE9uceu1GM2eVmkp8UmXB+o4x6vUSr/V9S7ULXcPTeMWh z9OCoBDrLvtwK9oD7d4E+LOjMkA3yIQEYHmNjFy8cx+URZdzyNqWoy6QaxSsyd3Aolp1 Ps1i/01jkgdnq/f++a+W9kGSxT8pXPgd71qm2r2dlyV6QD4+vhVqMGZpHnt3xc50gBhC SBOX6qA1lhofLQOgwzlFqagCstjLxSv2ifhRqliOs2Voma12UqhT2eZMdu0ZVMiGemOM um/MKXxecbqcZhpPIzDTYdWLhphujL/bHOkghJ4ar5kOcZ4RuwB9gXB+tLw2Z6yIYway cNyA== X-Gm-Message-State: AOJu0Yz+RtT3BUvJOWq/wQYI1oSAYxi8jwKNyQzBOa7FHClbuF8Sia0c zCZ71vyJ7ibU+6PVla4UcrkICZrEm17d0PhbdRNt9SS+kz8YkrdgBrzWlTZfTP0wQLwaQyTKEP3 7XWQaDh9iTm0J4p5KRFfm8P+R77LaVSU= X-Gm-Gg: AfdE7cnEUfOx1uvp0z0SLFHnK5rBmttSiK7yUvHJ4SSd42OXPFf082NnDcXMrPGSGDW C0/R1si4mBexitw2frXEIB5BjcTM9Tbxv9iukl73xAanoXkxQz3OGi8Q2MFHnYBDg5x3CZvs69W mODuA2EbzzDIzzM0nK/9xF1VD+Vr+YhywEtK/DVQxyhq2EPIoC7vQZsUct1bIoPUlYZ66tqr0w+ T4RDef68est/UvHfgZHF23ACHnr4zQLCKr1HeitiEDPzlLmr8PqOiMbIUItDBr8s0w0ri9jTg== X-Received: by 2002:a05:690c:9c0f:b0:80c:4d94:26ca with SMTP id 00721157ae682-817393fc7c6mr106627027b3.38.1783335794198; Mon, 06 Jul 2026 04:03:14 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Ayush Tiwari Date: Mon, 6 Jul 2026 16:33:03 +0530 X-Gm-Features: AVVi8Cc_350jjYAzQ043DZIkqxzZW_xhfc3rP9uOuLoZT_31Ygz_lzxSGeXbNK4 Message-ID: Subject: Re: REVOKE's CASCADE protection doesn't work with INHERITed table owners To: Jacob Champion Cc: PostgreSQL Hackers Content-Type: multipart/alternative; boundary="000000000000874c2b0655ef383d" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000874c2b0655ef383d Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi, On Fri, 26 Jun 2026 at 05:43, Jacob Champion < jacob.champion@enterprisedb.com> wrote: > [moving to -hackers] > > On Wed, Jun 24, 2026 at 2:57=E2=80=AFPM Jacob Champion > wrote: > > TL;DR: The protection in recursive_revoke() against broken GRANT > > OPTION chains doesn't seem to work properly when the grantee also > > holds the privileges of the grantor. > > More accurately: "when an intermediate grantor in the chain only > indirectly holds the ability to grant." > > > I think the issue is in recursive_revoke()'s usage of aclmask(), which > > in turn uses has_privs_of_role(). It doesn't seem like that's what was > > wanted in this particular case... thoughts? > > I propose changing that to aclmask_direct(), as in the attached, and > backpatching all the way down. > > To try to prove to myself that this works, I added tests to pin each > of the three cases that are treated differently by aclmask_direct(): > 1. the grantor has indirect ownership privileges > 2. the grantor has indirect grant options via INHERIT > 3. the grantor has indirect grant options via PUBLIC (which is already > disallowed in practice) > Thanks, this looks right to me. I traced recursive_revoke() and the aclmask() -> aclmask_direct() switch makes sense to me, since a grant is only ever attributed to a role that holds the option directly (or the owner), it seems right that recursive_revoke() should judge "still has it" the same way, rather than counting inherited/superuser access? The three new test cases line up with that too. I also tried to expand the existing comment, both to point out the > pitfall and to explain why the short-circuit works. But I've rewritten > it at least a dozen times, so if anyone can tell me whether I've made > sense and/or used the terminology appropriately, I'd appreciate it. One tiny comment question: the phrase "granted by any role on the chain" in the new comment reads a little oddly to me, would something like "still holds the option directly via another grantor" be closer to what the code checks? Could just be me misreading it. Apart from the above, patch LGTM. > I'm pretty sure the following is unintended behavior. It looks > > potentially related to [1] as well. > > (To fix [1] I suspect we need to make a similar tweak to > check_circularity(), but I haven't looked into that yet.) > > [1] > https://postgr.es/m/CAM6Zo8wD7RtQNhbQHODc9DobiW+GpT=3DtnqOSMz4+mnzA9m0zMg= @mail.gmail.com On check_circularity() for [1]: I tried the same aclmask_direct() swap, but since it runs on every GRANT ... WITH GRANT OPTION, which pg_dump/restore replays, erroring there could make restore/pg_upgrade of an existing cluster (one already holding the [1] self-grant) fail. Feels like it may need a companion dump/restore change first. Regards, Ayush --000000000000874c2b0655ef383d Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi,

On Fri, 26 Jun 2= 026 at 05:43, Jacob Champion <jacob.champion@enterprisedb.com> wrote:
[moving to -hackers]

On Wed, Jun 24, 2026 at 2:57=E2=80=AFPM Jacob Champion
<ja= cob.champion@enterprisedb.com> wrote:
> TL;DR: The protection in recursive_revoke() against broken GRANT
> OPTION chains doesn't seem to work properly when the grantee also<= br> > holds the privileges of the grantor.

More accurately: "when an intermediate grantor in the chain only
indirectly holds the ability to grant."

> I think the issue is in recursive_revoke()'s usage of aclmask(), w= hich
> in turn uses has_privs_of_role(). It doesn't seem like that's = what was
> wanted in this particular case... thoughts?

I propose changing that to aclmask_direct(), as in the attached, and
backpatching all the way down.

To try to prove to myself that this works, I added tests to pin each
of the three cases that are treated differently by aclmask_direct():
1. the grantor has indirect ownership privileges
2. the grantor has indirect grant options via INHERIT
3. the grantor has indirect grant options via PUBLIC (which is already
disallowed in practice)

Thanks, this looks right t= o me.=C2=A0 I traced recursive_revoke() and the
aclmask() -> aclmask_= direct() switch makes sense to me, since a
grant is only ever attribute= d to a role that holds the option directly
(or the owner), it seems rig= ht that recursive_revoke() should judge
"still has it" the sa= me way, rather than counting inherited/superuser
access?=C2=A0 The thre= e new test cases line up with that too.=C2=A0 =C2=A0

I also tried to expand the existing comment, both to point out the
pitfall and to explain why the short-circuit works. But I've rewritten<= br> it at least a dozen times, so if anyone can tell me whether I've made sense and/or used the terminology appropriately, I'd appreciate it.
=C2=A0
One tiny comment question: the phrase "granted= by any role on the chain"
in the new comment reads a little oddly = to me, would something like
"still holds the option directly via an= other grantor" be closer to what
the code checks?=C2=A0 Could just = be me misreading it.

Apart from the above, patch LGTM.

=
> I'm pretty sure the following is unintended behavior. It looks
> potentially related to [1] as well.

(To fix [1] I suspect we need to make a similar tweak to
check_circularity(), but I haven't looked into that yet.)

[1] https://= postgr.es/m/CAM6Zo8wD7RtQNhbQHODc9DobiW+GpT=3DtnqOSMz4+mnzA9m0zMg@mail.gmai= l.com

On check_circularity() for [1]: I tried the = same aclmask_direct() swap,
but since it runs on every GRANT ... WITH GR= ANT OPTION, which
pg_dump/restore replays, erroring there could make res= tore/pg_upgrade of
an existing cluster (one already holding the [1] self= -grant) fail. Feels
like it may need a companion dump/restore change fir= st.

Regards,
Ayush=C2=A0
--000000000000874c2b0655ef383d--