public inbox for [email protected]  
help / color / mirror / Atom feed
From: Andreas Lind <[email protected]>
To: [email protected]
Subject: equalPolicy() doesn't compare the permissive flag
Date: Thu, 9 Jul 2026 16:28:25 +0200
Message-ID: <CAMxA3rv1CS6R7JR5ojz-3CmCEnZEFrqu+XXTnGbLRWrjJRH7sA@mail.gmail.com> (raw)

Hi,

While working on an unrelated RLS patch, I noticed that equalPolicy() in
relcache.c doesn't compare the permissive field of RowSecurityPolicy. It
compares polcmd, hassublinks, policy_name, roles, qual, and
with_check_qual, but not permissive, so two policies that are identical
in every other respect except their PERMISSIVE/RESTRICTIVE designation
are reported as equal.

equalPolicy() is used (via equalRSDesc()) by RelationRebuildRelation()
to decide whether an open relation's existing row-security descriptor
can be kept as-is across a relcache rebuild, or must be treated as
changed. ALTER POLICY has no way to flip a policy's
PERMISSIVE/RESTRICTIVE designation, but DROP POLICY followed by CREATE
POLICY of the same name, roles, command, and quals, differing only in AS
PERMISSIVE/RESTRICTIVE, hits this exactly: the stale descriptor would be
kept, leaving the relcache out of sync with how the policy should now
combine with others (PERMISSIVE policies are ORed together; RESTRICTIVE
policies are ANDed with the rest).

I wasn't able to construct a simple SQL reproduction:
RelationRebuildRelation() (and thus this comparison) is only reached
while the relation is still open (refcount > 0) at the moment its
invalidation is processed. Closing and reopening the relation between
statements -- the usual behavior -- sidesteps the bug, and a second
session can't hold the relation open across the DROP/CREATE either,
since both need AccessExclusiveLock. This looks analogous to
ab6d1cd26eb, which fixed the same kind of omission in this function for
the USING qual and likewise shipped without a test.

Patch attached.

Regards,
Andreas Lind


Attachments:

  [application/octet-stream] 0001-Fix-relcache-s-equalPolicy-to-compare-the-permissive.patch (2.9K, ../CAMxA3rv1CS6R7JR5ojz-3CmCEnZEFrqu+XXTnGbLRWrjJRH7sA@mail.gmail.com/2-0001-Fix-relcache-s-equalPolicy-to-compare-the-permissive.patch)
  download | inline diff:
From b6a9184c9202a84c5fa925cb87924b47bbe8aeca Mon Sep 17 00:00:00 2001
From: Andreas Lind <[email protected]>
Date: Thu, 9 Jul 2026 15:06:39 +0200
Subject: [PATCH] Fix relcache's equalPolicy() to compare the
 permissive/restrictive flag

equalPolicy() is used by equalRSDesc(), which RelationRebuildRelation()
consults when rebuilding an open relation's relcache entry in place
(refcount > 0) to decide whether the entry's existing row-security
descriptor can be kept as-is or must be treated as changed. It
compared polcmd, hassublinks, policy_name, roles, qual, and
with_check_qual, but never permissive, so two policies that are
identical in every other respect but differ in their
PERMISSIVE/RESTRICTIVE designation were reported as equal, and the
stale descriptor (with the old designation) would be kept.

This is reachable without any unsupported catalog surgery: ALTER
POLICY has no way to change a policy's PERMISSIVE/RESTRICTIVE
designation, but DROP POLICY followed by CREATE POLICY of the same
name, roles, command, and quals, differing only in AS
PERMISSIVE/RESTRICTIVE, hits exactly this case, and would leave the
relcache out of sync with how the policy should now combine with
others (PERMISSIVE policies are ORed together; RESTRICTIVE policies
are ANDed with the rest).

Note that RelationRebuildRelation(), and thus this comparison, is only
reached while the relation is still open (refcount > 0) at the moment
its invalidation is locally processed; if nothing holds it open at
that point, RelationClearRelation() just deletes the entry outright
and it gets rebuilt from scratch on next access, sidestepping the
comparison entirely. Consequently this isn't reachable through a
straightforward sequence of ordinary SQL statements: closing and
reopening the relation between statements (the usual behavior) avoids
the bug, and a second session cannot hold the relation open across the
DROP/CREATE either, since both require AccessExclusiveLock. As with
ab6d1cd26eb, which fixed the same kind of omission in this function
for the USING qual, this is a fix for an internal cache-consistency
invariant rather than something with a simple externally observable
trigger, and is submitted without a reproduction script for that
reason.
---
 src/backend/utils/cache/relcache.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index fb4e042be8a..19c4ff6e75e 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -980,6 +980,8 @@ equalPolicy(RowSecurityPolicy *policy1, RowSecurityPolicy *policy2)
 
 		if (policy1->polcmd != policy2->polcmd)
 			return false;
+		if (policy1->permissive != policy2->permissive)
+			return false;
 		if (policy1->hassublinks != policy2->hassublinks)
 			return false;
 		if (strcmp(policy1->policy_name, policy2->policy_name) != 0)
-- 
2.50.1 (Apple Git-155)



view thread (3+ messages)  latest in thread

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected]
  Subject: Re: equalPolicy() doesn't compare the permissive flag
  In-Reply-To: <CAMxA3rv1CS6R7JR5ojz-3CmCEnZEFrqu+XXTnGbLRWrjJRH7sA@mail.gmail.com>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox