Re: Clarification on Role Access Rights to Table Indexes

From: Nathan Bossart <nathandbossart(at)gmail(dot)com>
To: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
Cc: Ayush Vatsa <ayushvatsa1810(at)gmail(dot)com>, Robert Haas <robertmhaas(at)gmail(dot)com>, "David G(dot) Johnston" <david(dot)g(dot)johnston(at)gmail(dot)com>, PostgreSQL Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: Clarification on Role Access Rights to Table Indexes
Date: 2025-03-09 01:35:18
Message-ID: Z8zwVmGzXyDdkAXj@nathan
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-general pgsql-hackers

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

In response to

Responses

Browse pgsql-general by date

  From Date Subject
Next Message Tom Lane 2025-03-09 15:48:05 Re: Clarification on Role Access Rights to Table Indexes
Previous Message Tom Lane 2025-03-08 22:17:40 Re: Clarification on Role Access Rights to Table Indexes

Browse pgsql-hackers by date

  From Date Subject
Next Message Nathan Bossart 2025-03-09 02:16:49 Re: strange valgrind reports about wrapper_handler on 64-bit arm
Previous Message David Rowley 2025-03-09 01:15:25 Re: Printing window function OVER clauses in EXPLAIN