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 1wvzhN-001fpU-0h for pgsql-hackers@arkaria.postgresql.org; Mon, 17 Aug 2026 15:56:29 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wvzhL-00BbVn-0G for pgsql-hackers@arkaria.postgresql.org; Mon, 17 Aug 2026 15:56:28 +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 1wvzhK-00BbVe-1c for pgsql-hackers@lists.postgresql.org; Mon, 17 Aug 2026 15:56:27 +0000 Received: from fout-b6-smtp.messagingengine.com ([202.12.124.149]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1wvzhI-00000001ErP-20Vs for pgsql-hackers@lists.postgresql.org; Mon, 17 Aug 2026 15:56:27 +0000 Received: from phl-compute-06.internal (phl-compute-06.internal [10.202.2.46]) by mailfout.stl.internal (Postfix) with ESMTP id 45D3D1D0017B; Mon, 17 Aug 2026 11:56:22 -0400 (EDT) Received: from phl-frontend-04 ([10.202.2.163]) by phl-compute-06.internal (MEProxy); Mon, 17 Aug 2026 11:56:22 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kurilemu.de; h= cc:cc:content-transfer-encoding:content-type:content-type:date :date:from:from:in-reply-to:in-reply-to:message-id:mime-version :reply-to:subject:subject:to:to; s=fm2; t=1786982182; x= 1787068582; bh=5ori0COuI4SIh3qd+9TiG2PL5nqP6Sn2cvSmcgmURYY=; b=H NOvMGG6pUEnj7l1hhPlGrJaEDDDZng6pDljrQMwg+kdpCMvGzAMhypq48Y9Go8YN YoOSfdN/LVRFezzABUreJD/3UFSGPaymerVSWjiPA+8iUlueDNg4vVYHKSwBPhzB tNNLBPXPdHvy29BmWtj9mt69r8r+OFXihCg1clScSJQHL6x/vo/CQaW9d+JvfFIU 79D9uVk1qqH9vBiMbn/6wshsERFWVg86yzbzedUAJDLZU80FZH/KBt89TYTMkvhD tg1JVDKpDm/tEL1HgtsX1W5ljHlTGqxNgAKPsGylMNpH3Ftds134BbRGBy4h6wtd U8YCfWbhSGOYebiBoVTaQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:content-type:date:date:feedback-id:feedback-id :from:from:in-reply-to:in-reply-to:message-id:mime-version :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1786982182; x=1787068582; bh=5 ori0COuI4SIh3qd+9TiG2PL5nqP6Sn2cvSmcgmURYY=; b=mU/1YhlxTk0UWcrmQ sj8vMz59xuzkajBhltYJEe/s7XprXpd56UtgXgKJ1YPPsyBzv4wUmCGIvssZKNYh lD8ZBaPgsVohhB4QP1Ro54waI8lAYOOWIvrSabI5ukb6CgSdBNjnpR7xtOK2v2N+ jyV/etdeNZ2m/0b6HWnTITObRg+4lj0wLE70zEnch1Fcztf54nM9ibg1SuCB5ZAM keU8UsVM9AgHCBQ2M/gvgmHaIZpJQanr7mNZ35wBBSFL7HLqHh6opq2T07SFYAhr bj6MkFtat9aFNfi6gGbWBGsoCD35vMpobwYt04jrrJJL9UYw0C0CTAOXhhzzIcS6 zSDYQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: dmFkZTFQ2+sKWYehUhXNLu9L1GxoQThum6tMzq6d+TGEimlEMk6Lp+jKMs6mIKwVP0CJnE lseuZ94sPjNTZUv9/S6LqGOYw+6SYvyzejR/nP8BjqTFEzALhHLVlAQ8HWiRw+oTpht+0d FxZYp7SRarb7PglrC1wNXOToQSyxnFc5erP5440DGynxYjr1KsU673Sy72hPH+MYWJF7hk R+lBn1cjVlZIr+4YAlMnxhhm/IIIzeoCLbiBY0pdyzPeaNkbpZF6cQ26QHIzxYdFF/mGM9 5Xm5K5wsum2T90M6b+Vhw+v+KU45ZyrqvU+pvPqg/rfOoSfqYAL9YDZQN7sbNOwNaqmyJf PqzHJUDOzuEAXbsdUj7tjQi7ksxSyScziPo8dc9MbX++Cvcg3oVXNwuDVVPptTr1XMjjU9 0iHeTO2sLF2rIEoDzx1wgAx8D2NnB/aJOuu+qPOxLVSW/O+sujM4y88sKNQknDMt2mHglk efqagVgXHsMmF8InTNk0ao8AzzCTpsV3UkAJy/7PH4psEMyYrHbxPti/oKXam5h3TZuFyS 97vpfFrAgXk1QvVF5eZxJuh9ndRSZBsRdyMYtQ/M+HZDvl9YRoGvK+zdu6VUVEKCmyanVY b5KktUtvmXJ3Po9pmfMpb+SzL3nfmQXTZjkVYm4xOXdy+xsTThWRzCZGh1nA X-ME-Proxy: Feedback-ID: ie3de48e3:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Mon, 17 Aug 2026 11:56:21 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kurilemu.de; s=schmee; t=1786982179; bh=DW8qSowl2jRIAx1AwNp456Iiwm7w4sLkN27KpuWM8vs=; h=Date:From:To:Cc:Subject:In-Reply-To:From; b=JMplJ+cehLhrloZ5OUct0SvA6ndBSwdzWzU1RAnsMWXHgqF0i5pKvIlc7yZqLlpfO EJ0j344waI+IRvwg5gdYIEtE8xADffCVUzhuz2QlqIE24ljBF3r12OxGw8kUGZa83d 8pywb8TKgmzN7XNLY//zHkLRRpwhTe3YXx6uOhEFec/QuPGDzeds3InmWK8Sm667Ik 3pG6TZAsF8iE6/+gJ1sqLNVB149Cu9jHB1EZBdBfz42GG02Q5t1kPmvZrsywyiTICr 875mK1q1/cgA8iHNZwlzgYb2vXTfT6lK4oZVJdzjpZD29KUJnfIelVHwrdq2Nn5V5D SLNNPUPIE4beg== Received: by ida.kurilemu.internal (Postfix, from userid 1000) id D916CB00048; Mon, 17 Aug 2026 17:56:19 +0200 (CEST) Date: Mon, 17 Aug 2026 17:56:19 +0200 From: =?utf-8?Q?=C3=81lvaro?= Herrera To: Bharath Rupireddy Cc: PostgreSQL Hackers Subject: Re: Tighten ACL check in repack_is_permitted_for_relation() Message-ID: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="lagmlhwoe2cxax2o" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --lagmlhwoe2cxax2o Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit Hello On 2026-Aug-04, Bharath Rupireddy wrote: > repack_is_permitted_for_relation() uses pg_class_aclcheck_ext() to > silently skip a concurrently-dropped relation. That's wrong for a > caller that may already hold a lock on the relation whose ACL is > checked, where missing a relation is not fine, and it makes the > single-relation REPACK cases more brittle > (https://www.postgresql.org/message-id/akPhEffRipH4isWF@nathan). So > only detect a missing relation where that's expected, following the > fix for vacuum_is_permitted_for_relation() in commit 824d5f6. That makes sense. I think "missing OK" is a bit weird as an argument here though; I prefer it as "already locked", inverting the boolean. What do you think of this formulation? -- Álvaro Herrera Breisgau, Deutschland — https://www.EnterpriseDB.com/ "Learn about compilers. Then everything looks like either a compiler or a database, and now you have two problems but one of them is fun." https://twitter.com/thingskatedid/status/1456027786158776329 --lagmlhwoe2cxax2o Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename=v3-0001-Tighten-ACL-check-in-repack_is_permitted_for_rela.patch From 7efe46210cd9e5c943f02c2a55757f23b2bfec9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Herrera?= Date: Mon, 17 Aug 2026 17:53:34 +0200 Subject: [PATCH v3] Tighten ACL check in repack_is_permitted_for_relation() repack_is_permitted_for_relation() uses pg_class_aclcheck_ext() to silently skip a concurrently-dropped relation. That's wrong for a caller that may already hold a lock on the relation whose ACL is checked, where missing a relation is not fine, and it makes the single-relation REPACK and CLUSTER cases more brittle. So only detect a missing relation where that's expected, following the fix for vacuum_is_permitted_for_relation() in commit 824d5f6241ea. The new already_locked behavior is limited to get_tables_to_repack() and get_tables_to_repack_partitioned(). All other callers of repack_is_permitted_for_relation() hold a lock on the relation that prevents it from being concurrently dropped, so this commit also adds an assertion to that effect. Author: Bharath Rupireddy Discussion: https://www.postgresql.org/message-id/CALj2ACX3pyuRS8%2B%2B6L20cJUMRTf_qbbVp69J1btJ3y6%3D77e5gw%40mail.gmail.com --- src/backend/commands/repack.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index edff54e734e..66b88e28c2e 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -173,7 +173,8 @@ static List *get_tables_to_repack_partitioned(RepackStmt *stmt, Relation rel, MemoryContext permcxt); static bool repack_is_permitted_for_relation(RepackCommand cmd, - Oid relid, Oid userid); + Oid relid, Oid userid, + bool already_locked); static void apply_concurrent_changes(BufFile *file, ChangeContext *chgcxt); static void apply_concurrent_insert(Relation rel, TupleTableSlot *slot, @@ -681,7 +682,7 @@ cluster_rel_recheck(RepackCommand cmd, Relation OldHeap, Oid indexOid, Assert(CheckRelationLockedByMe(OldHeap, lmode, false)); /* Check that the user still has privileges for the relation */ - if (!repack_is_permitted_for_relation(cmd, tableOid, userid)) + if (!repack_is_permitted_for_relation(cmd, tableOid, userid, true)) { relation_close(OldHeap, lmode); return false; @@ -2155,7 +2156,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt) /* noisily skip rels which the user can't process */ if (!repack_is_permitted_for_relation(cmd, index->indrelid, - GetUserId())) + GetUserId(), false)) continue; /* Use a permanent memory context for the result list */ @@ -2192,7 +2193,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt) /* noisily skip rels which the user can't process */ if (!repack_is_permitted_for_relation(cmd, class->oid, - GetUserId())) + GetUserId(), false)) continue; /* Use a permanent memory context for the result list */ @@ -2321,7 +2322,7 @@ get_tables_to_repack_partitioned(RepackStmt *stmt, Relation rel, * if so. */ if (!repack_is_permitted_for_relation(stmt->command, table_oid, - GetUserId())) + GetUserId(), false)) continue; /* Use a permanent memory context for the result list */ @@ -2341,26 +2342,38 @@ get_tables_to_repack_partitioned(RepackStmt *stmt, Relation rel, /* - * Return whether userid has privileges to execute REPACK on relid. + * Return whether userid has privileges to execute REPACK/CLUSTER on relid. * - * Caller may not have a lock on the relation, so it could have been - * dropped concurrently. In that case, silently return false. + * The relation may already be locked by caller, in which case it cannot + * possibly go missing; otherwise it can have been removed recently. If + * it's been removed, silently return false. If the relation does exist but + * the user doesn't have the required privs, emit a WARNING and return false. * - * If the relation does exist but the user doesn't have the required - * privs, emit a WARNING and return false. Otherwise, return true. + * Otherwise, return true. */ static bool -repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid) +repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid, + bool already_locked) { bool is_missing = false; AclResult result; char *relname; Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK); + Assert(!already_locked || + CheckRelationOidLockedByMe(relid, AccessShareLock, true)); result = pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing); + + /* + * If the relation was concurrently dropped, nothing to do. This is only + * reachable when the caller doesn't already have a lock on the relation. + */ if (is_missing) + { + Assert(!already_locked); return false; + } if (result == ACLCHECK_OK) return true; -- 2.47.3 --lagmlhwoe2cxax2o--