Thread

Commits

  1. Fix privilege checks for pg_prewarm() on indexes.

  2. Fix lookup code for REINDEX INDEX.

  3. Fix redefinition of typedef RangeVar.

  4. Fix lookups in pg_{clear,restore}_{attribute,relation}_stats().

  5. dblink: Avoid locking relation before privilege check.

  1. Clarification on Role Access Rights to Table Indexes

    Ayush Vatsa <ayushvatsa1810@gmail.com> — 2025-02-17T18:01:46Z

    Hi PostgreSQL Community,
    I am currently exploring the behavior of pg_prewarm and encountered an
    issue related to role
    access rights that I was hoping you could help clarify.
    
    Here is the scenario I observed:
    
    postgres=# CREATE ROLE alpha;
    CREATE ROLE
    postgres=# GRANT SELECT ON pg_class TO alpha;
    GRANT
    postgres=# SET ROLE alpha;
    SET
    postgres=> SELECT pg_prewarm('pg_class');
     pg_prewarm
    ------------
             14
    (1 row)
    
    postgres=> SELECT pg_prewarm('pg_class_oid_index');
    ERROR:  permission denied for index pg_class_oid_index
    postgres=> RESET ROLE;
    RESET
    
    postgres=# GRANT SELECT ON pg_class_oid_index TO alpha;
    ERROR:  "pg_class_oid_index" is an index
    
    Based on this, I have few questions:
    1. Can a role have access rights to a table without having access to its
    index?
    2. If yes, how can we explicitly grant access to the index?
    3. If no, and the role inherently gets access to the index when granted
    access to the table, why
    does the pg_prewarm call fail [1] in the above scenario?
    
    [1]
    https://github.com/postgres/postgres/blob/master/contrib/pg_prewarm/pg_prewarm.c#L108-L110
    
    
    Regards,
    Ayush Vatsa
    SDE AWS
    
  2. Clarification on Role Access Rights to Table Indexes

    David G. Johnston <david.g.johnston@gmail.com> — 2025-02-17T18:17:16Z

    On Monday, February 17, 2025, Ayush Vatsa <ayushvatsa1810@gmail.com> wrote:
    
    > postgres=# CREATE ROLE alpha;
    >
    > CREATE ROLE
    > postgres=# GRANT SELECT ON pg_class TO alpha;
    >
    This is pointless, everyone (i.e. the PUBLIC pseudo-role) can already read
    pg_class.
    
    1. Can a role have access rights to a table without having access to its
    > index?
    >
    Roles don’t directly interact with indexes in PostgreSQL so this doesn’t
    even make sense.  But if you need a yes/no answer, then yes.
    
    >
    > 3. If no, and the role inherently gets access to the index when granted
    > access to the table, why
    > does the pg_prewarm call fail [1] in the above scenario?
    >
    > [1] https://github.com/postgres/postgres/blob/master/contrib
    > /pg_prewarm/pg_prewarm.c#L108-L110
    >
    It fails because AFAICS there is no way for it to work on an index, only
    tables.
    
    David J.
    
  3. Re: Clarification on Role Access Rights to Table Indexes

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-02-17T18:27:30Z

    Ayush Vatsa <ayushvatsa1810@gmail.com> writes:
    > postgres=> SELECT pg_prewarm('pg_class_oid_index');
    > ERROR:  permission denied for index pg_class_oid_index
    
    You'd really have to take that up with the author of pg_prewarm.
    It's not apparent to me why checking SQL access permissions is
    the right mechanism for limiting use of pg_prewarm.  It seems
    like ownership of the table would be more appropriate, or maybe
    access to one of the built-in roles like pg_maintain.
    
    > 1. Can a role have access rights to a table without having access to its
    > index?
    
    Indexes do not have access rights of their own, which is why
    access rights are a poor gating mechanism for something that
    needs to be applicable to indexes.  Ownership could work,
    because we make indexes inherit their table's ownership.
    
    			regards, tom lane
    
    
    
    
  4. Re: Clarification on Role Access Rights to Table Indexes

    Ayush Vatsa <ayushvatsa1810@gmail.com> — 2025-02-17T18:39:33Z

    > This is pointless, everyone (i.e. the PUBLIC pseudo-role) can already
    read pg_class.
    True, Just checked that.
    
    > It fails because AFAICS there is no way for it to work on an index, only
    tables.
    pg_prewarm extension works on index if we have right (SELECT) privileges
    postgres=# CREATE TABLE x(id INT);
    CREATE TABLE
    postgres=# CREATE INDEX idx ON x(id);
    CREATE INDEX
    
    postgres=# INSERT INTO x SELECT * FROM generate_series(1,10000);
    INSERT 0 10000
    postgres=# SELECT pg_prewarm('x');
     pg_prewarm
    ------------
             45
    (1 row)
    
    postgres=# SELECT pg_prewarm('idx');
     pg_prewarm
    ------------
             30
    (1 row)
    
    > It seems like ownership of the table would be more appropriate, or maybe
    > access to one of the built-in roles like pg_maintain.
    True, adding Robert Haas (author) to this thread for his opinion.
    
    Regards,
    Ayush Vatsa
    SDE AWS
    
  5. Re: Clarification on Role Access Rights to Table Indexes

    David G. Johnston <david.g.johnston@gmail.com> — 2025-02-17T18:43:04Z

    On Monday, February 17, 2025, Tom Lane <tgl@sss.pgh.pa.us> wrote:
    
    > Ayush Vatsa <ayushvatsa1810@gmail.com> writes:
    > > postgres=> SELECT pg_prewarm('pg_class_oid_index');
    > > ERROR:  permission denied for index pg_class_oid_index
    >
    > You'd really have to take that up with the author of pg_prewarm.
    
    
    This is our contrib module so this seems like the expected place to ask
    such a question.  It’s neither a bug nor a topic for -hackers.  FTR, Robert
    Haas is the author from 2013.  Not sure he monitors -general though.
    
    David J.
    
  6. Re: Clarification on Role Access Rights to Table Indexes

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-02-17T19:13:09Z

    "David G. Johnston" <david.g.johnston@gmail.com> writes:
    > On Monday, February 17, 2025, Tom Lane <tgl@sss.pgh.pa.us> wrote:
    >> You'd really have to take that up with the author of pg_prewarm.
    
    > This is our contrib module so this seems like the expected place to ask
    > such a question.  It’s neither a bug nor a topic for -hackers.  FTR, Robert
    > Haas is the author from 2013.  Not sure he monitors -general though.
    
    Ah, you are right, I was thinking it was a third-party extension.
    
    If we're talking about changing the behavior of a contrib module,
    I think -hackers would be the appropriate location for that.
    And it does seem like this deserves a fresh look.  As it stands,
    a superuser can prewarm an index (because she bypasses all
    privilege checks including this one), but nobody else can.
    Seems weird.
    
    			regards, tom lane
    
    
    
    
  7. Re: Clarification on Role Access Rights to Table Indexes

    Ayush Vatsa <ayushvatsa1810@gmail.com> — 2025-02-17T19:42:44Z

    > As it stands, a superuser can prewarm an index (because she bypasses all
    > privilege checks including this one), but nobody else can.
    That's not fully true. Any role can prewarm an index if the role has the
    correct privileges.
    postgres=# GRANT CREATE ON SCHEMA PUBLIC TO alpha;
    GRANT
    postgres=# SET ROLE alpha;
    SET
    postgres=> CREATE TABLE tab(id INT);
    CREATE TABLE
    postgres=> CREATE INDEX tab_idx ON tab(id);
    CREATE INDEX
    postgres=> SELECT pg_prewarm('tab_idx');
     pg_prewarm
    ------------
              1
    (1 row)
    
    Don't know what stopped it from prewarming the catalog table index.
    Maybe it's checking who is the owner of the table. Although in code
    it's just checking the ACL_SELECT [1]. I will be debugging more here
    and maybe create a patch for the same.
    
    postgres=# RESET ROLE;
    RESET
    postgres=# CREATE TABLE superuser_tab(id INT);
    CREATE TABLE
    postgres=# CREATE INDEX idx_superuser_tab ON superuser_tab(id);
    CREATE INDEX
    postgres=# GRANT SELECT ON superuser_tab TO alpha;
    GRANT
    postgres=# SET ROLE alpha;
    SET
    postgres=> SELECT pg_prewarm('superuser_tab');
     pg_prewarm
    ------------
              0
    (1 row)
    postgres=> SELECT pg_prewarm('idx_superuser_tab');
    ERROR:  permission denied for index idx_superuser_tab
    
    postgres=> RESET ROLE;
    RESET
    postgres=# ALTER TABLE superuser_tab OWNER TO alpha;
    ALTER TABLE
    postgres=# SET ROLE alpha;
    SET
    postgres=> SELECT pg_prewarm('idx_superuser_tab');
     pg_prewarm
    ------------
              1
    (1 row)
    
    But I agree we should just check the table privileges even in the case of
    indexes to decide whether to prewarm or not, as
    *indexes don't have any privilegesof their own.*
    
    > I think -hackers would be the appropriate location for that.
    I am shifting this to -hackers mailing list instead of general.
    
    [1]
    https://github.com/postgres/postgres/blob/master/contrib/pg_prewarm/pg_prewarm.c#L108-L110
    
    Regards,
    Ayush Vatsa
    SDE AWS
    
  8. Re: Clarification on Role Access Rights to Table Indexes

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-02-17T19:57:43Z

    Ayush Vatsa <ayushvatsa1810@gmail.com> writes:
    >> As it stands, a superuser can prewarm an index (because she bypasses all
    >> privilege checks including this one), but nobody else can.
    
    > That's not fully true. Any role can prewarm an index if the role has the
    > correct privileges.
    
    Ah, right.  An index will have null pg_class.relacl, which'll be
    interpreted as "owner has all rights", so it will work for the
    table owner too.  Likely this explains the lack of prior complaints.
    It's still a poor design IMO.
    
    			regards, tom lane
    
    
    
    
  9. Re: Clarification on Role Access Rights to Table Indexes

    Laurenz Albe <laurenz.albe@cybertec.at> — 2025-02-17T19:58:49Z

    On Mon, 2025-02-17 at 23:31 +0530, Ayush Vatsa wrote:
    > postgres=> SELECT pg_prewarm('pg_class_oid_index');
    > ERROR:  permission denied for index pg_class_oid_index
    > postgres=> RESET ROLE;
    > RESET
    > 
    > postgres=# GRANT SELECT ON pg_class_oid_index TO alpha;
    > ERROR:  "pg_class_oid_index" is an index
    > Based on this, I have few questions:
    > 1. Can a role have access rights to a table without having access to its index?
    > 2. If yes, how can we explicitly grant access to the index?
    > 3. If no, and the role inherently gets access to the index when granted access to the table, why
    > does the pg_prewarm call fail [1] in the above scenario?
    
    I have seen a complaint about this bug before:
    https://dba.stackexchange.com/a/344603/176905
    
    Yours,
    Laurenz Albe
    
    -- 
    
    *E-Mail Disclaimer*
    Der Inhalt dieser E-Mail ist ausschliesslich fuer den 
    bezeichneten Adressaten bestimmt. Wenn Sie nicht der vorgesehene Adressat 
    dieser E-Mail oder dessen Vertreter sein sollten, so beachten Sie bitte, 
    dass jede Form der Kenntnisnahme, Veroeffentlichung, Vervielfaeltigung oder 
    Weitergabe des Inhalts dieser E-Mail unzulaessig ist. Wir bitten Sie, sich 
    in diesem Fall mit dem Absender der E-Mail in Verbindung zu setzen.
    
    *CONFIDENTIALITY NOTICE & DISCLAIMER
    *This message and any attachment are 
    confidential and may be privileged or otherwise protected from disclosure 
    and solely for the use of the person(s) or entity to whom it is intended. 
    If you have received this message in error and are not the intended 
    recipient, please notify the sender immediately and delete this message and 
    any attachment from your system. If you are not the intended recipient, be 
    advised that any use of this message is prohibited and may be unlawful, and 
    you must not copy this message or attachment or disclose the contents to 
    any other person.
    
    
    
    
  10. Re: Clarification on Role Access Rights to Table Indexes

    Robert Haas <robertmhaas@gmail.com> — 2025-02-17T21:34:45Z

    On Mon, Feb 17, 2025 at 2:57 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > Ayush Vatsa <ayushvatsa1810@gmail.com> writes:
    > >> As it stands, a superuser can prewarm an index (because she bypasses all
    > >> privilege checks including this one), but nobody else can.
    >
    > > That's not fully true. Any role can prewarm an index if the role has the
    > > correct privileges.
    >
    > Ah, right.  An index will have null pg_class.relacl, which'll be
    > interpreted as "owner has all rights", so it will work for the
    > table owner too.  Likely this explains the lack of prior complaints.
    > It's still a poor design IMO.
    
    I'm not sure if I'd call that a "design". Sounds like I just made a
    mistake here.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  11. Re: Clarification on Role Access Rights to Table Indexes

    Ayush Vatsa <ayushvatsa1810@gmail.com> — 2025-02-17T21:45:30Z

    > I'm not sure if I'd call that a "design". Sounds like I just made a
    > mistake here.
    Thanks Robert for confirming, let me submit a patch to fix the same.
    
    Regards
    Ayush Vatsa
    ADE AWS
    
  12. Re: Clarification on Role Access Rights to Table Indexes

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-02-17T22:02:03Z

    Ayush Vatsa <ayushvatsa1810@gmail.com> writes:
    > Thanks Robert for confirming, let me submit a patch to fix the same.
    
    Well, the first thing you need is consensus on what the behavior
    should be instead.
    
    I have a very vague recollection that we concluded that SELECT
    privilege was a reasonable check because if you have that you
    could manually prewarm by reading the table.  That would lead
    to the conclusion that the minimal fix is to look at the owning
    table's privileges instead of the index's own privileges.
    
    Or we could switch to using ownership, which'd keep the code
    simple but some users might complain it's too restrictive.
    
    While I mentioned built-in roles earlier, I now think those mostly
    carry more privilege than should be required here, given the analogy
    to SELECT.
    
    			regards, tom lane
    
    
    
    
  13. Re: Clarification on Role Access Rights to Table Indexes

    David G. Johnston <david.g.johnston@gmail.com> — 2025-02-17T22:17:26Z

    On Mon, Feb 17, 2025 at 3:02 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    
    > Ayush Vatsa <ayushvatsa1810@gmail.com> writes:
    > > Thanks Robert for confirming, let me submit a patch to fix the same.
    >
    > Well, the first thing you need is consensus on what the behavior
    > should be instead.
    >
    > I have a very vague recollection that we concluded that SELECT
    > privilege was a reasonable check because if you have that you
    > could manually prewarm by reading the table.  That would lead
    > to the conclusion that the minimal fix is to look at the owning
    > table's privileges instead of the index's own privileges.
    >
    
    I feel like if you can blow up the cache by loading an entire table into
    memory with just select privilege on the table we should be ok with
    allowing the same person to name an index on the same table and load it
    into the cache too.
    
    David J.
    
  14. Re: Clarification on Role Access Rights to Table Indexes

    Robert Haas <robertmhaas@gmail.com> — 2025-02-18T15:13:03Z

    On Mon, Feb 17, 2025 at 5:18 PM David G. Johnston
    <david.g.johnston@gmail.com> wrote:
    >> I have a very vague recollection that we concluded that SELECT
    >> privilege was a reasonable check because if you have that you
    >> could manually prewarm by reading the table.  That would lead
    >> to the conclusion that the minimal fix is to look at the owning
    >> table's privileges instead of the index's own privileges.
    >
    > I feel like if you can blow up the cache by loading an entire table into memory with just select privilege on the table we should be ok with allowing the same person to name an index on the same table and load it into the cache too.
    
    +1.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  15. Re: Clarification on Role Access Rights to Table Indexes

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-02-18T16:02:38Z

    Robert Haas <robertmhaas@gmail.com> writes:
    > On Mon, Feb 17, 2025 at 5:18 PM David G. Johnston
    > <david.g.johnston@gmail.com> wrote:
    >>> I have a very vague recollection that we concluded that SELECT
    >>> privilege was a reasonable check because if you have that you
    >>> could manually prewarm by reading the table.  That would lead
    >>> to the conclusion that the minimal fix is to look at the owning
    >>> table's privileges instead of the index's own privileges.
    
    >> I feel like if you can blow up the cache by loading an entire table into memory with just select privilege on the table we should be ok with allowing the same person to name an index on the same table and load it into the cache too.
    
    > +1.
    
    Is that a +1 for the specific design of "check SELECT on the index's
    table", or just a +1 for changing something here?
    
    			regards, tom lane
    
    
    
    
  16. Re: Clarification on Role Access Rights to Table Indexes

    Robert Haas <robertmhaas@gmail.com> — 2025-02-18T16:21:33Z

    On Tue, Feb 18, 2025 at 11:02 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > Is that a +1 for the specific design of "check SELECT on the index's
    > table", or just a +1 for changing something here?
    
    That is a +1 for the specific design of "check SELECT on the index's
    table". I don't want to be closed-minded: if you have some strong
    reason for believing that's the wrong thing to do, I'm all ears.
    However, I'm presently of the view that it is exactly the right thing
    to do, to the point where I don't currently understand why there's
    anything to think about here.
    
    --
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  17. Re: Clarification on Role Access Rights to Table Indexes

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-02-18T16:30:02Z

    Robert Haas <robertmhaas@gmail.com> writes:
    > That is a +1 for the specific design of "check SELECT on the index's
    > table". I don't want to be closed-minded: if you have some strong
    > reason for believing that's the wrong thing to do, I'm all ears.
    > However, I'm presently of the view that it is exactly the right thing
    > to do, to the point where I don't currently understand why there's
    > anything to think about here.
    
    I have no objection to it, but I wasn't as entirely convinced
    as you are that it's the only plausible answer.
    
    One specific thing I'm slightly worried about is that a naive
    implementation would probably cause this function to lock the
    table after the index, risking deadlock against queries that
    take the locks in the more conventional order.  I don't recall
    what if anything we've done about that in other places
    (-ENOCAFFEINE).
    
    			regards, tom lane
    
    
    
    
  18. Re: Clarification on Role Access Rights to Table Indexes

    Robert Haas <robertmhaas@gmail.com> — 2025-02-18T18:16:24Z

    On Tue, Feb 18, 2025 at 11:30 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > I have no objection to it, but I wasn't as entirely convinced
    > as you are that it's the only plausible answer.
    
    Hmm, OK.
    
    > One specific thing I'm slightly worried about is that a naive
    > implementation would probably cause this function to lock the
    > table after the index, risking deadlock against queries that
    > take the locks in the more conventional order.  I don't recall
    > what if anything we've done about that in other places
    > (-ENOCAFFEINE).
    
    Yeah, that seems like a good thing to worry about from an
    implementation point of view but it doesn't seem like a reason to
    question the basic design choice. In general, if you can use a table,
    you also get to use its indexes, so that interpretation seems natural
    to me here, also. Now, if somebody finds a problem with requiring only
    SELECT permission, I could see changing the requirements for both
    tables and indexes, but I find it harder to imagine that we'd want
    those things to work differently from each other. Of course I'm
    willing to be convinced that there's a good reason for them to be
    different; I just can't currently imagine what it might be.
    
    -- 
    Robert Haas
    EDB: http://www.enterprisedb.com
    
    
    
    
  19. Re: Clarification on Role Access Rights to Table Indexes

    Ayush Vatsa <ayushvatsa1810@gmail.com> — 2025-02-19T10:23:48Z

    Hello Everyone,
    It seems there's a general consensus that we should maintain a
    original design to support pg_prewarm, with a minor adjustment:
    when querying indexes, we should verify the privileges of the parent table.
    
    I’ve attached a patch for this, which includes some test cases as well.
    Let me know if it needs any changes.
    
    Regards,
    Ayush Vatsa
    SDE AWS
    
  20. Re: Clarification on Role Access Rights to Table Indexes

    Ayush Vatsa <ayushvatsa1810@gmail.com> — 2025-02-19T10:31:32Z

    Added the CF entry for the same -
    https://commitfest.postgresql.org/patch/5583/
    
    Regards,
    Ayush Vatsa
    SDE AWS
    
    >
    
  21. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-03-04T20:01:28Z

    On Wed, Feb 19, 2025 at 03:53:48PM +0530, Ayush Vatsa wrote:
    > It seems there's a general consensus that we should maintain a
    > original design to support pg_prewarm, with a minor adjustment:
    > when querying indexes, we should verify the privileges of the parent table.
    > 
    > I´ve attached a patch for this, which includes some test cases as well.
    > Let me know if it needs any changes.
    
    +        tableOid = IndexGetRelation(relOid, false);
    +        aclresult = pg_class_aclcheck(tableOid, GetUserId(), ACL_SELECT);
    
    I'm wondering whether setting missing_ok to true is correct here.  IIUC we
    should have an AccessShareLock on the index, but I don't know if that's
    enough protection.  The only other similar coding pattern I'm aware of is
    RangeVarCallbackForReindexIndex(), which sets missing_ok to false and
    attempts to gracefully handle a missing table.  Of course, maybe that's
    wrong, too.
    
    Perhaps it's all close enough in practice.  If we get it wrong, you might
    get a slightly less helpful error message when the table is concurrently
    dropped, which isn't so bad.
    
    -- 
    nathan
    
    
    
    
  22. Re: Clarification on Role Access Rights to Table Indexes

    Ayush Vatsa <ayushvatsa1810@gmail.com> — 2025-03-08T15:04:40Z

    > I'm wondering whether setting missing_ok to true is correct here.  IIUC we
    > should have an AccessShareLock on the index, but I don't know if that's
    > enough protection.
    
    Since we are already opening the relation with rel = relation_open(relOid,
    AccessShareLock);,
    if relOid does not exist, it will throw an error. If it does exist, we
    acquire an AccessShareLock,
    preventing it from being dropped.
    
    By the time we reach IndexGetRelation(), we can be confident that relOid
    exists and is
    protected by the lock. Given this, it makes sense to keep missing_ok = false
    here.
    
    Let me know if you agree or if you see any scenario where
    missing_ok = true would be preferable—I can update the condition
    accordingly.
    
    Thanks!
    Ayush Vatsa
    SDE AWS
    
  23. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-03-08T21:08:02Z

    On Sat, Mar 08, 2025 at 08:34:40PM +0530, Ayush Vatsa wrote:
    >> I'm wondering whether setting missing_ok to true is correct here.  IIUC we
    >> should have an AccessShareLock on the index, but I don't know if that's
    >> enough protection.
    > 
    > Since we are already opening the relation with rel = relation_open(relOid,
    > AccessShareLock);,
    > if relOid does not exist, it will throw an error. If it does exist, we
    > acquire an AccessShareLock,
    > preventing it from being dropped.
    > 
    > By the time we reach IndexGetRelation(), we can be confident that relOid
    > exists and is
    > protected by the lock. Given this, it makes sense to keep missing_ok = false
    > here.
    > 
    > Let me know if you agree or if you see any scenario where
    > missing_ok = true would be preferable-I can update the condition
    > accordingly.
    
    Right, we will have a lock on the index, but my concern is that we won't
    have a lock on its table.  I was specifically concerned that a concurrent
    DROP TABLE could cause IndexGetRelation() to fail, i.e., emit a gross
    "cache lookup failed" error.  From a quick test and skim of the relevant
    code, I think your patch is fine, though.  IndexGetRelation() retrieves the
    table OID from pg_index, so the OID should definitely be valid.  And IIUC
    DROP TABLE first acquires a lock on the table and its dependent objects
    (e.g., indexes) before any actual deletions, so AFAICT there's no problem
    with using it in pg_class_aclcheck() and get_rel_name(), either.
    
    -- 
    nathan
    
    
    
    
  24. Re: Clarification on Role Access Rights to Table Indexes

    Ayush Vatsa <ayushvatsa1810@gmail.com> — 2025-03-08T21:31:41Z

    > From a quick test and skim of the relevant
    > code, I think your patch is fine, though
    Thanks for reviewing.
    
    > And IIUC
    > DROP TABLE first acquires a lock on the table and its dependent objects
    > (e.g., indexes) before any actual deletions, so AFAICT there's no problem
    > with using it in pg_class_aclcheck() and get_rel_name(), either.
    True, I have also verified that from [1], hence I think we are safe here.
    Maybe we can move ahead with the patch if we can see no other concerns.
    
    [1]
    https://github.com/postgres/postgres/blob/master/src/backend/catalog/dependency.c#L398-L430
    
    Thanks,
    Ayush Vatsa
    SDE AWS
    
  25. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-03-08T21:57:25Z

    On Sun, Mar 09, 2025 at 03:01:41AM +0530, Ayush Vatsa wrote:
    > Maybe we can move ahead with the patch if we can see no other concerns.
    
    I think we should allow some time in case others want to review the patch.
    I do see a concern upthread about increased deadlock risk [0], but your
    patch doesn't lock the table, but unless I'm wrong [1] (which is always
    possible), it doesn't need to lock it.
    
    Anyway, here is a tidied up patch.
    
    [0] https://postgr.es/m/1246906.1739896202%40sss.pgh.pa.us
    [1] https://postgr.es/m/Z8yxsm9ZWVkHlPbV%40nathan
    
    -- 
    nathan
    
  26. Re: Clarification on Role Access Rights to Table Indexes

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-03-08T22:17:40Z

    Nathan Bossart <nathandbossart@gmail.com> writes:
    > I do see a concern upthread about increased deadlock risk [0], but your
    > patch doesn't lock the table, but unless I'm wrong [1] (which is always
    > possible), it doesn't need to lock it.
    
    It bothers me a bit that this proposes to do something as complicated
    as pg_class_aclcheck on a table we have no lock on.  As you say, the
    lock we hold on the index would prevent DROP TABLE, but that doesn't
    mean we won't have any issues with other DDL on the table.  Still,
    taking a lock would be bad because of the deadlock hazard, and I
    think the potential for conflicts with concurrent DDL is nonzero in
    a lot of other places.  So I don't have any concrete reason to object.
    
    ReindexIndex() faces this same problem and solves it with some
    very complex code that manages to get the table's lock first.
    But I see that it's also doing pg_class_aclcheck on a table
    it hasn't locked yet, so I don't think that adopting its approach
    would do anything useful for us here.
    
    			regards, tom lane
    
    
    
    
  27. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-03-09T01:35:18Z

    On Sat, Mar 08, 2025 at 05:17:40PM -0500, Tom Lane wrote:
    > It bothers me a bit that this proposes to do something as complicated
    > as pg_class_aclcheck on a table we have no lock on.  As you say, the
    > lock we hold on the index would prevent DROP TABLE, but that doesn't
    > mean we won't have any issues with other DDL on the table.  Still,
    > taking a lock would be bad because of the deadlock hazard, and I
    > think the potential for conflicts with concurrent DDL is nonzero in
    > a lot of other places.  So I don't have any concrete reason to object.
    > 
    > ReindexIndex() faces this same problem and solves it with some
    > very complex code that manages to get the table's lock first.
    > But I see that it's also doing pg_class_aclcheck on a table
    > it hasn't locked yet, so I don't think that adopting its approach
    > would do anything useful for us here.
    
    I noticed that amcheck's bt_index_check_internal() handles this problem,
    and I think that approach could be adapted here:
    
    	relkind = get_rel_relkind(relOid);
    	if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
    	{
    		permOid = IndexGetRelation(relOid, true);
    		if (OidIsValid(permOid))
    			LockRelationOid(permOid, AccessShareLock);
    		else
    			fail = true;
    	}
    	else
    		permOid = relOid;
    
    	rel = relation_open(relOid, AccessShareLock);
    	if (fail ||
    		(permOid != relOid && permOid != IndexGetRelation(relOid, false)))
    		ereport(ERROR,
    				(errcode(ERRCODE_UNDEFINED_TABLE),
    				 errmsg("could not find parent table of index \"%s\"",
    						RelationGetRelationName(rel))));
    
    	aclresult = pg_class_aclcheck(permOid, GetUserId(), ACL_SELECT);
    	if (aclresult != ACLCHECK_OK)
    		aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind), get_rel_name(relOid));
    
    	if (permOid != relOid)
    		UnlockRelationOid(permOid, AccessShareLock);
    
    stats_lock_check_privileges() does something similar, but it's not as
    cautious about the "heapid != IndexGetRelation(indrelid, false)" race
    condition.  Maybe RangeVarCallbackForReindexIndex() should be smarter about
    this, too.  That being said, this is a fair amount of complexity to handle
    something that is in theory extremely rare...
    
    -- 
    nathan
    
    
    
    
  28. Re: Clarification on Role Access Rights to Table Indexes

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-03-09T15:48:05Z

    Nathan Bossart <nathandbossart@gmail.com> writes:
    > On Sat, Mar 08, 2025 at 05:17:40PM -0500, Tom Lane wrote:
    >> ReindexIndex() faces this same problem and solves it with some
    >> very complex code that manages to get the table's lock first.
    
    > I noticed that amcheck's bt_index_check_internal() handles this problem,
    > ...
    > stats_lock_check_privileges() does something similar, but it's not as
    > cautious about the "heapid != IndexGetRelation(indrelid, false)" race
    > condition.
    
    Egad, we've already got three inconsistent implementations of this
    functionality?  I think the first step must be to unify them into
    a common implementation, if at all possible.
    
    			regards, tom lane
    
    
    
    
  29. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-03-10T15:15:19Z

    On Sun, Mar 09, 2025 at 11:48:05AM -0400, Tom Lane wrote:
    > Nathan Bossart <nathandbossart@gmail.com> writes:
    >> On Sat, Mar 08, 2025 at 05:17:40PM -0500, Tom Lane wrote:
    >>> ReindexIndex() faces this same problem and solves it with some
    >>> very complex code that manages to get the table's lock first.
    > 
    >> I noticed that amcheck's bt_index_check_internal() handles this problem,
    >> ...
    >> stats_lock_check_privileges() does something similar, but it's not as
    >> cautious about the "heapid != IndexGetRelation(indrelid, false)" race
    >> condition.
    > 
    > Egad, we've already got three inconsistent implementations of this
    > functionality?  I think the first step must be to unify them into
    > a common implementation, if at all possible.
    
    Agreed.  I worry that trying to unify each bespoke implementation into a
    single function might result in an unwieldy mess, but I'll give it a
    shot...
    
    -- 
    nathan
    
    
    
    
  30. Re: Clarification on Role Access Rights to Table Indexes

    vignesh C <vignesh21@gmail.com> — 2025-03-16T13:00:23Z

    On Sun, 9 Mar 2025 at 03:27, Nathan Bossart <nathandbossart@gmail.com> wrote:
    >
    > On Sun, Mar 09, 2025 at 03:01:41AM +0530, Ayush Vatsa wrote:
    > > Maybe we can move ahead with the patch if we can see no other concerns.
    >
    > I think we should allow some time in case others want to review the patch.
    > I do see a concern upthread about increased deadlock risk [0], but your
    > patch doesn't lock the table, but unless I'm wrong [1] (which is always
    > possible), it doesn't need to lock it.
    >
    > Anyway, here is a tidied up patch.
    
    I noticed that Tom Lane's comment from [1] is not addressed. I'm
    changing the commitfest entry status to Waiting on Author, Please
    address them and update the status to Needs Review.
    [1] - https://www.postgresql.org/message-id/279947.1741535285%40sss.pgh.pa.us
    
    Regards,
    Vignesh
    
    
    
    
  31. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-09-24T15:58:25Z

    On Mon, Mar 10, 2025 at 10:15:19AM -0500, Nathan Bossart wrote:
    > On Sun, Mar 09, 2025 at 11:48:05AM -0400, Tom Lane wrote:
    >> Nathan Bossart <nathandbossart@gmail.com> writes:
    >>> On Sat, Mar 08, 2025 at 05:17:40PM -0500, Tom Lane wrote:
    >>>> ReindexIndex() faces this same problem and solves it with some
    >>>> very complex code that manages to get the table's lock first.
    >> 
    >>> I noticed that amcheck's bt_index_check_internal() handles this problem,
    >>> ...
    >>> stats_lock_check_privileges() does something similar, but it's not as
    >>> cautious about the "heapid != IndexGetRelation(indrelid, false)" race
    >>> condition.
    >> 
    >> Egad, we've already got three inconsistent implementations of this
    >> functionality?  I think the first step must be to unify them into
    >> a common implementation, if at all possible.
    > 
    > Agreed.  I worry that trying to unify each bespoke implementation into a
    > single function might result in an unwieldy mess, but I'll give it a
    > shot...
    
    I tried to unify these, but each one seems to be just different enough to
    make it not worth the trouble.  Instead, I took a look at each
    implementation:
    
    * amcheck's amcheck_lock_relation_and_check() seems correct to me.
    
    * stats_lock_check_privileges() appears to be missing the second
    IndexGetRelation() check after locking the table and index, so I added
    that in 0001.  Since this code is new to v18, I proposed to back-patch 0001
    there.
    
    * RangeVarCallbackForReindexIndex() was checking privileges on the table
    before locking it, so I reversed it in 0002.  Interestingly, this caused
    test errors because LockRelationOid() checks for invalidation messages, so
    the pg_class_aclcheck() call started failing with unhelpful errors due to
    concurrently dropped relations.  To deal with that, I switched to
    pg_class_aclcheck_ext() so that we can handle missing relations.
    Furthermore, I noticed that this callback seems to assume that as long as
    the index does not change between calls, its table won't, either.  That's
    probably always true in practice, but even if it's completely true, I see
    no reason to rely on it.  So, I simplified the code to unconditionally
    unlock any previously-locked table and to lock whatever IndexGetRelation()
    returns.  This could probably be back-patched, but in the absence of any
    reports or any reproducible bugs, I don't think we should.
    
    * 0003 fixes pg_prewarm's privilege checks by following a similar pattern.
    This probably ought to get back-patched to all supported versions.
    
    -- 
    nathan
    
  32. Re: Clarification on Role Access Rights to Table Indexes

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-09-24T16:13:34Z

    Nathan Bossart <nathandbossart@gmail.com> writes:
    > * RangeVarCallbackForReindexIndex() was checking privileges on the table
    > before locking it, so I reversed it in 0002.
    
    Don't we do that intentionally, to make sure someone can't cause DOS
    on a table they have no privileges on?
    
    			regards, tom lane
    
    
    
    
  33. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-09-24T16:52:09Z

    On Wed, Sep 24, 2025 at 12:13:34PM -0400, Tom Lane wrote:
    > Nathan Bossart <nathandbossart@gmail.com> writes:
    >> * RangeVarCallbackForReindexIndex() was checking privileges on the table
    >> before locking it, so I reversed it in 0002.
    > 
    > Don't we do that intentionally, to make sure someone can't cause DOS
    > on a table they have no privileges on?
    
    Ah, right.  I switched it back in v4.
    
    -- 
    nathan
    
  34. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-10-02T17:37:44Z

    On Wed, Sep 24, 2025 at 11:52:09AM -0500, Nathan Bossart wrote:
    > Ah, right.  I switched it back in v4.
    
    Unless more feedback materializes, I'm planning to commit these sometime
    next week.
    
    -- 
    nathan
    
    
    
    
  35. Re: Clarification on Role Access Rights to Table Indexes

    Jeff Davis <pgsql@j-davis.com> — 2025-10-09T03:06:04Z

    On Wed, 2025-09-24 at 11:52 -0500, Nathan Bossart wrote:
    > On Wed, Sep 24, 2025 at 12:13:34PM -0400, Tom Lane wrote:
    > > Nathan Bossart <nathandbossart@gmail.com> writes:
    > > > * RangeVarCallbackForReindexIndex() was checking privileges on
    > > > the table
    > > > before locking it, so I reversed it in 0002.
    > > 
    > > Don't we do that intentionally, to make sure someone can't cause
    > > DOS
    > > on a table they have no privileges on?
    > 
    > Ah, right.  I switched it back in v4.
    
    v4-0001 looks good to me.
    
    Just to make sure I understand: the actual problem would only happen
    with OID wraparound, right?
    
    Regards,
    	Jeff Davis
    
    
    
    
    
  36. Re: Clarification on Role Access Rights to Table Indexes

    Jeff Davis <pgsql@j-davis.com> — 2025-10-09T03:28:01Z

    On Wed, 2025-10-08 at 20:06 -0700, Jeff Davis wrote:
    > On Wed, 2025-09-24 at 11:52 -0500, Nathan Bossart wrote:
    > > On Wed, Sep 24, 2025 at 12:13:34PM -0400, Tom Lane wrote:
    > > > Nathan Bossart <nathandbossart@gmail.com> writes:
    > > > > * RangeVarCallbackForReindexIndex() was checking privileges on
    > > > > the table
    > > > > before locking it, so I reversed it in 0002.
    > > > 
    > > > Don't we do that intentionally, to make sure someone can't cause
    > > > DOS
    > > > on a table they have no privileges on?
    > > 
    > > Ah, right.  I switched it back in v4.
    > 
    > v4-0001 looks good to me.
    
    Actually, now I'm unsure. v4-0001 is taking a lock on the table before
    checking privileges, whereas v4-0002 is going to some effort to avoid
    that. Is that because the latter is taking a ShareLock?
    
    Regards,
    	Jeff Davis
    
    
    
    
    
  37. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-10-09T15:39:32Z

    On Wed, Oct 08, 2025 at 08:28:01PM -0700, Jeff Davis wrote:
    > Actually, now I'm unsure. v4-0001 is taking a lock on the table before
    > checking privileges, whereas v4-0002 is going to some effort to avoid
    > that. Is that because the latter is taking a ShareLock?
    
    I was confused by this, too.  We seem to go to great lengths to avoid
    taking a lock before checking permissions in RangeVarGetRelidExtended(),
    but in pg_prewarm() and this stats code, we are taking the lock first.
    pg_prewarm() can't use RangeVarGetRelid because you give it the OID, but
    I'm not seeing why stat_utils.c can't use it.  We should probably fix this.
    I wouldn't be surprised if there are other examples.
    
    -- 
    nathan
    
    
    
    
  38. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-10-09T21:18:03Z

    On Thu, Oct 09, 2025 at 10:39:32AM -0500, Nathan Bossart wrote:
    > On Wed, Oct 08, 2025 at 08:28:01PM -0700, Jeff Davis wrote:
    >> Actually, now I'm unsure. v4-0001 is taking a lock on the table before
    >> checking privileges, whereas v4-0002 is going to some effort to avoid
    >> that. Is that because the latter is taking a ShareLock?
    > 
    > I was confused by this, too.  We seem to go to great lengths to avoid
    > taking a lock before checking permissions in RangeVarGetRelidExtended(),
    > but in pg_prewarm() and this stats code, we are taking the lock first.
    > pg_prewarm() can't use RangeVarGetRelid because you give it the OID, but
    > I'm not seeing why stat_utils.c can't use it.  We should probably fix this.
    > I wouldn't be surprised if there are other examples.
    
    I spent some time trying to change pg_prewarm() to check permissions before
    locking and came up with the attached.  There are certainly issues with the
    patch, but this at least demonstrates the complexity required.  I'm tempted
    to say that this is more trouble than it's worth, but it does feel a little
    weird to leave it as-is.
    
    There's a similar pattern in get_rel_from_relname() in dblink.c, which also
    seems to only be used with an AccessShareLock (like pg_prewarm).  My best
    guess from reading lots of code, commit messages, and old e-mails in the
    archives is that the original check-privileges-before-locking work was
    never completed.
    
    I'm currently leaning towards continuing with v4 of the patch set.  0001
    and 0003 are a little weird in that a concurrent change could lead to a
    "could not find parent table" ERROR, but IIUC that is an extremely remote
    possibility.
    
    -- 
    nathan
    
  39. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-10-10T16:26:08Z

    On Thu, Oct 09, 2025 at 04:18:03PM -0500, Nathan Bossart wrote:
    > There's a similar pattern in get_rel_from_relname() in dblink.c, which also
    > seems to only be used with an AccessShareLock (like pg_prewarm).  My best
    > guess from reading lots of code, commit messages, and old e-mails in the
    > archives is that the original check-privileges-before-locking work was
    > never completed.
    
    I added an 0004 that changes dblink to use RangeVarGetRelidExtended().
    
    > I'm currently leaning towards continuing with v4 of the patch set.  0001
    > and 0003 are a little weird in that a concurrent change could lead to a
    > "could not find parent table" ERROR, but IIUC that is an extremely remote
    > possibility.
    
    After sleeping on it, I still think this is the right call.  In any case,
    I've spent way too much time on this stuff, so I plan to commit the
    attached soon.
    
    -- 
    nathan
    
  40. Re: Clarification on Role Access Rights to Table Indexes

    Jeff Davis <pgsql@j-davis.com> — 2025-10-10T18:31:03Z

    On Fri, 2025-10-10 at 11:26 -0500, Nathan Bossart wrote:
    > On Thu, Oct 09, 2025 at 04:18:03PM -0500, Nathan Bossart wrote:
    > > There's a similar pattern in get_rel_from_relname() in dblink.c,
    > > which also
    > > seems to only be used with an AccessShareLock (like pg_prewarm). 
    > > My best
    > > guess from reading lots of code, commit messages, and old e-mails
    > > in the
    > > archives is that the original check-privileges-before-locking work
    > > was
    > > never completed.
    
    Interesting, thank you for the analysis.
    
    > > I'm currently leaning towards continuing with v4 of the patch set. 
    > > 0001
    > > and 0003 are a little weird in that a concurrent change could lead
    > > to a
    > > "could not find parent table" ERROR, but IIUC that is an extremely
    > > remote
    > > possibility.
    > 
    > After sleeping on it, I still think this is the right call.  In any
    > case,
    > I've spent way too much time on this stuff, so I plan to commit the
    > attached soon.
    
    I'm OK with that. v5-0001 is an improvement over the current situation.
    
    Regards,
    	Jeff Davis
    
    
    
    
    
  41. Re: Clarification on Role Access Rights to Table Indexes

    Jeff Davis <pgsql@j-davis.com> — 2025-10-13T18:16:36Z

    On Wed, 2025-09-24 at 12:13 -0400, Tom Lane wrote:
    > Don't we do that intentionally, to make sure someone can't cause DOS
    > on a table they have no privileges on?
    
    Is this only a problem for strong locks (ShareLock or greater)?
    
    Strong locks are a problem when you have a pattern like a long running
    query that holds an AccessShareLock, and then an unprivileged user
    requests an AccessExclusiveLock, forcing other queries to queue up
    behind it, and the queue doesn't clear until the long running query
    finishes.
    
    But weaker locks don't seem to have that problem, right?
    
    Regards,
    	Jeff Davis
    
    
    
    
    
  42. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-10-13T19:30:48Z

    On Fri, Oct 10, 2025 at 11:31:03AM -0700, Jeff Davis wrote:
    > On Fri, 2025-10-10 at 11:26 -0500, Nathan Bossart wrote:
    >> After sleeping on it, I still think this is the right call.  In any
    >> case, I've spent way too much time on this stuff, so I plan to commit
    >> the attached soon.
    > 
    > I'm OK with that. v5-0001 is an improvement over the current situation.
    
    Okay, I lied.  I spent even more time on these patches and came up with the
    attached.  Here's a summary of what's going on:
    
    * 0001 moves the code for stats clearing/setting to use
    RangeVarGetRelidExtended().  The existing code looks up the relation, locks
    it, and then checks permissions.  There's no guarantee that the relation
    you looked up didn't concurrently change before locking, and locking before
    privilege checks is troublesome from a DOS perspective.  One downside of
    using RangeVarGetRelidExtended() is that we can't use AccessShareLock for
    regular indexes, but I'm not sure that's really a problem since we're
    already using ShareUpdateExclusiveLock for everything else.  The
    RangeVarGetRelidExtended() callback is similar to the one modified by 0002.
    This should be back-patched to v18.
    
    * 0002 fixes the RangeVarGetRelidExtended() callback for REINDEX INDEX to
    handle unlikely scenarios involving OID reuse (e.g., lookup returns the
    same index OID for a different table).  I did confirm there was a bug here
    by concurrently re-creating an index with the same OID for a heap with a
    different OID (via the pg_upgrade support functions).  In previous versions
    of this patch, I tried to fix this by unconditionally unlocking the heap at
    the beginning of the callback, but upon further inspection, I noticed that
    creates deadlock hazards because we might've already locked the index.  (We
    need to lock the heap first.)  In v6, I've just added an ERROR for these
    extremely unlikely scenarios.  I've also replaced all early returns in this
    function with ERRORs (except for the invalid relId case).  AFAICT the extra
    checks are unecessary, and even if they were necessary, I think they break
    some of the code related to heap locking in subtle ways.  Some callbacks do
    these extra checks, and others do not, and AFAIK there haven't been any
    reported problems either way.  0002 should be back-patched to v13, but it
    will look a little different on v16 and newer, i.e., before MAINTAIN was
    added.
    
    * 0003 fixes the privilege checks in pg_prewarm by using a similar approach
    to amcheck_lock_relation_and_check().  This seems correct to me, but it
    does add more locking.  This should be back-patched to v13.
    
    * 0004 is a small patch to teach dblink to use RangeVarGetRelidExtended().
    I believe this code predates that function.  I don't intend to back-patch
    this one.
    
    -- 
    nathan
    
  43. Re: Clarification on Role Access Rights to Table Indexes

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-10-13T21:21:07Z

    Jeff Davis <pgsql@j-davis.com> writes:
    > On Wed, 2025-09-24 at 12:13 -0400, Tom Lane wrote:
    >> Don't we do that intentionally, to make sure someone can't cause DOS
    >> on a table they have no privileges on?
    
    > Is this only a problem for strong locks (ShareLock or greater)?
    
    > Strong locks are a problem when you have a pattern like a long running
    > query that holds an AccessShareLock, and then an unprivileged user
    > requests an AccessExclusiveLock, forcing other queries to queue up
    > behind it, and the queue doesn't clear until the long running query
    > finishes.
    
    > But weaker locks don't seem to have that problem, right?
    
    I don't think so.  Even AccessShareLock is enough to block another
    session trying to acquire AccessExclusiveLock, and then not only
    have you DoS'd that session, but everything else trying to access
    the table will queue up behind the AccessExclusiveLock request.
    So it's only not-a-problem if nothing anywhere in the system wants
    non-sharable locks.
    
    			regards, tom lane
    
    
    
    
  44. Re: Clarification on Role Access Rights to Table Indexes

    Jeff Davis <pgsql@j-davis.com> — 2025-10-13T22:11:38Z

    On Mon, 2025-10-13 at 17:21 -0400, Tom Lane wrote:
    > I don't think so.  Even AccessShareLock is enough to block another
    > session trying to acquire AccessExclusiveLock, and then not only
    > have you DoS'd that session, but everything else trying to access
    > the table will queue up behind the AccessExclusiveLock request.
    > So it's only not-a-problem if nothing anywhere in the system wants
    > non-sharable locks.
    
    I tried imagining how that could be a problem, but couldn't come up
    with anything. If the privilege check is right after the lock, then
    either:
    
    (a) The malicious AccessShareLock is granted, then is quickly released
    when the privilege check fails and the transaction aborts; or
    
    (b) The malicious AccessShareLock is queued behind a legitimate
    AccessExclusiveLock, in which case every other lock would be queued up
    as well. As soon as the AccessExclusiveLock is released, the
    AccessShareLock would be granted, but quickly released when the
    privilege check fails.
    
    For it to be a problem, the malicious lock needs to be strong enough to
    conflict with a lock level weaker than itself, i.e. ShareLock or
    stronger.
    
    I'm not sure we save anything by being lazier for weaker lock levels,
    so perhaps the point is irrelevant. But if I'm missing something,
    please let me know.
    
    Regards,
    	Jeff Davis
    
    
    
    
    
  45. Re: Clarification on Role Access Rights to Table Indexes

    Jeff Davis <pgsql@j-davis.com> — 2025-10-14T02:23:36Z

    On Mon, 2025-10-13 at 14:30 -0500, Nathan Bossart wrote:
    > * 0001 moves the code for stats clearing/setting to use
    > RangeVarGetRelidExtended().  The existing code looks up the relation,
    > locks
    > it, and then checks permissions.  There's no guarantee that the
    > relation
    > you looked up didn't concurrently change before locking, and locking
    > before
    > privilege checks is troublesome from a DOS perspective.  One downside
    > of
    > using RangeVarGetRelidExtended() is that we can't use AccessShareLock
    > for
    > regular indexes, but I'm not sure that's really a problem since we're
    > already using ShareUpdateExclusiveLock for everything else.  The
    > RangeVarGetRelidExtended() callback is similar to the one modified by
    > 0002.
    > This should be back-patched to v18.
    
    We tried to match the locking behavior for analyze. Originally, that's
    because we were using in-place updates, which required those specific
    kinds of locks. Now that the in-place code is gone, then I think it's
    OK to use ShareUpdateExclusiveLock for indexes, too, but it is a
    notable difference in behavior.
    
    Including Corey in case he has comments.
    
    As for the patch itself, it looks good to me. Stylistically I might
    have kept the "index_oid" variable, which makes some of the tests a bit
    clearer, but I don't have a strong opinion.
    
    The unlikely scenarios are a bit confusing. I'd probably error for
    either case. Also, the error message on the second scenario is wrong if
    the previous lookup was a table, I think.
    
    > * 0002 fixes the RangeVarGetRelidExtended() callback for REINDEX
    > INDEX to
    > handle unlikely scenarios involving OID reuse (e.g., lookup returns
    > the
    > same index OID for a different table).  I did confirm there was a bug
    > here
    > by concurrently re-creating an index with the same OID for a heap
    > with a
    > different OID (via the pg_upgrade support functions).  In previous
    > versions
    > of this patch, I tried to fix this by unconditionally unlocking the
    > heap at
    > the beginning of the callback, but upon further inspection, I noticed
    > that
    > creates deadlock hazards because we might've already locked the
    > index.  (We
    > need to lock the heap first.)  In v6, I've just added an ERROR for
    > these
    > extremely unlikely scenarios.  I've also replaced all early returns
    > in this
    > function with ERRORs (except for the invalid relId case).
    
    +1 for throwing errors when we have race conditions combined with name
    reuse. Looks fine to me.
    
    > 
    > * 0003 fixes the privilege checks in pg_prewarm by using a similar
    > approach
    > to amcheck_lock_relation_and_check().  This seems correct to me, but
    > it
    > does add more locking.  This should be back-patched to v13.
    
    IIUC this is locking before the privilege check. Is there a reason why
    we think this is OK here (and in amcheck_lock_relation_and_check()) but
    not for the stats?
    
    > * 0004 is a small patch to teach dblink to use
    > RangeVarGetRelidExtended().
    > I believe this code predates that function.  I don't intend to back-
    > patch
    > this one.
    
    Looks good.
    
    Regards,
    	Jeff Davis
    
    
    
    
    
    
  46. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-10-14T16:05:32Z

    Thanks for reviewing.
    
    On Mon, Oct 13, 2025 at 07:23:36PM -0700, Jeff Davis wrote:
    > The unlikely scenarios are a bit confusing. I'd probably error for
    > either case. Also, the error message on the second scenario is wrong if
    > the previous lookup was a table, I think.
    
    Yeah, I think that's a better idea.
    
    > IIUC this is locking before the privilege check. Is there a reason why
    > we think this is OK here (and in amcheck_lock_relation_and_check()) but
    > not for the stats?
    
    For amcheck, AFAICT there aren't actually any ACL checks within the code
    because the function is restricted to superuser by default.  For
    pg_prewarm, I don't know.  You do have to install the extension before
    using it, but once installed, it's available to everyone by default.  My
    guess is that it just hasn't been a problem in the field.
    
    Regardless, fixing the lock-before-privilege-checks behavior doesn't strike
    me as a bug, so I think we ought to proceed with something like 0003 for
    back-patching purposes and then to rework it further for v19.  Does that
    sound okay to you?
    
    >> * 0004 is a small patch to teach dblink to use
    >> RangeVarGetRelidExtended().  I believe this code predates that
    >> function.  I don't intend to back-patch this one.
    > 
    > Looks good.
    
    I'm going to go commit this one now to get it out of the way.
    
    -- 
    nathan
    
    
    
    
  47. Re: Clarification on Role Access Rights to Table Indexes

    Tom Lane <tgl@sss.pgh.pa.us> — 2025-10-14T16:30:26Z

    Jeff Davis <pgsql@j-davis.com> writes:
    > On Mon, 2025-10-13 at 17:21 -0400, Tom Lane wrote:
    >> I don't think so.  Even AccessShareLock is enough to block another
    >> session trying to acquire AccessExclusiveLock, and then not only
    >> have you DoS'd that session, but everything else trying to access
    >> the table will queue up behind the AccessExclusiveLock request.
    >> So it's only not-a-problem if nothing anywhere in the system wants
    >> non-sharable locks.
    
    > I tried imagining how that could be a problem, but couldn't come up
    > with anything. If the privilege check is right after the lock, then
    > either:
    > (a) The malicious AccessShareLock is granted, then is quickly released
    > when the privilege check fails and the transaction aborts; or
    > (b) The malicious AccessShareLock is queued behind a legitimate
    > AccessExclusiveLock, in which case every other lock would be queued up
    > as well. As soon as the AccessExclusiveLock is released, the
    > AccessShareLock would be granted, but quickly released when the
    > privilege check fails.
    > For it to be a problem, the malicious lock needs to be strong enough to
    > conflict with a lock level weaker than itself, i.e. ShareLock or
    > stronger.
    
    Robert might remember better, but I think the assumption behind
    the current design of RangeVarGetRelidExtended is that it's not
    okay to take a lock in the first place if you don't have the
    privilege to do so.  Your analysis here supposes that it's okay
    to take a lock without privileges so long as you can't block someone
    else for very long, where "very long" is not tightly defined but
    hopefully isn't controllable by the malicious user.  So that's
    moving the goalposts somewhat, but you might get people to sign
    onto it with more careful analysis of the worst-case delay.
    (The thing I'd worry about is whether it's possible to block
    execution of the privilege check, or even just make it slow.)
    
    Given that definition, I think you're right that it's possible to
    identify cases where lock-then-check can't cause meaningful DoS.
    RangeVarGetRelidExtended has to cope with the general case, so
    that's not an argument for simplifying it, but we might not need
    equivalent complexity everywhere.
    
    			regards, tom lane
    
    
    
    
  48. Re: Clarification on Role Access Rights to Table Indexes

    Jeff Davis <pgsql@j-davis.com> — 2025-10-14T17:01:37Z

    On Tue, 2025-10-14 at 11:05 -0500, Nathan Bossart wrote:
    > For
    > pg_prewarm, I don't know.  You do have to install the extension
    > before
    > using it, but once installed, it's available to everyone by default. 
    > My
    > guess is that it just hasn't been a problem in the field.
    
    If we start with an OID, what's the right way to do these kinds of
    checks? Could we do an ACL check, then lock it, then do an ACL check
    again to catch OID wraparound?
    
    Last-minute suggestions on 0003:
    
      * Add a comment around the privOid check to explain that, if the
    object is an index, we must check the privileges on the table instead.
    
      * Clarify in the comment that the race against index drop/recreation
    involves OID wraparound.
    
    +1 to the patch and backpatch.
    
    As a separate thought, I'm wondering if we should do more to enforce
    the idea that we check the privileges and owner of an index's table,
    and never the index itself. That's for another discussion, though.
    
    > Regardless, fixing the lock-before-privilege-checks behavior doesn't
    > strike
    > me as a bug, so I think we ought to proceed with something like 0003
    > for
    > back-patching purposes and then to rework it further for v19.  Does
    > that
    > sound okay to you?
    
    According to the current rules[1], it does seem to technically be a
    bug, but as far as I can tell, not one of much consequence.
    
    Regards,
    	Jeff Davis
    
    [1]
    https://www.postgresql.org/message-id/976405.1760459426@sss.pgh.pa.us
    
    
    
    
  49. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-10-14T21:32:55Z

    I've committed 0004.  Here is an updated patch set.
    
    -- 
    nathan
    
  50. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-10-15T15:39:17Z

    On Tue, Oct 14, 2025 at 10:01:37AM -0700, Jeff Davis wrote:
    > If we start with an OID, what's the right way to do these kinds of
    > checks? Could we do an ACL check, then lock it, then do an ACL check
    > again to catch OID wraparound?
    
    I tried something like this upthread [0].  My feeling was that this was a
    lot of complexity for not a lot of gain.  Perhaps it's still worth doing,
    though.
    
    [0] https://postgr.es/m/aOgmi6avE6qMw_6t%40nathan
    
    -- 
    nathan
    
    
    
    
  51. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-10-15T17:54:21Z

    I just pushed 0001, and longfin and sifaka have very quickly reminded me
    that we don't require C11 on v18.  Will fix shortly...
    
    -- 
    nathan
    
    
    
    
  52. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-10-15T21:38:05Z

    I've committed everything except for 0003, which I probably won't get to
    until Friday.  Note that I decided against back-patching 0002 because of
    the presumed rarity of and lack of reports for the bug.
    
    -- 
    nathan
    
    
    
    
  53. Re: Clarification on Role Access Rights to Table Indexes

    Nathan Bossart <nathandbossart@gmail.com> — 2025-10-17T16:56:04Z

    On Wed, Oct 15, 2025 at 04:38:05PM -0500, Nathan Bossart wrote:
    > I've committed everything except for 0003, which I probably won't get to
    > until Friday.  Note that I decided against back-patching 0002 because of
    > the presumed rarity of and lack of reports for the bug.
    
    Everything tracked in this thread is now committed.
    
    -- 
    nathan