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.94.2) (envelope-from ) id 1tI9NV-00CVNI-HK for pgsql-hackers@arkaria.postgresql.org; Mon, 02 Dec 2024 16:34:29 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1tI9NS-001BiV-Rm for pgsql-hackers@arkaria.postgresql.org; Mon, 02 Dec 2024 16:34:28 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1tI9NS-001BiN-Aw for pgsql-hackers@lists.postgresql.org; Mon, 02 Dec 2024 16:34:27 +0000 Received: from mail-qv1-xf2f.google.com ([2607:f8b0:4864:20::f2f]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1tI9NQ-000g8u-MP for pgsql-hackers@postgresql.org; Mon, 02 Dec 2024 16:34:26 +0000 Received: by mail-qv1-xf2f.google.com with SMTP id 6a1803df08f44-6d8878ec049so31883886d6.3 for ; Mon, 02 Dec 2024 08:34:24 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1733157264; x=1733762064; darn=postgresql.org; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=KQzoJvgiBJpPAoV0hgHKrOf1rIcxei4fb/suMYJ1bQ4=; b=ivx9hzEWcDRbo3eJOysKLtmx+jnVDPs7s/LtAtdyxbeOix3dOmbs49cjgvms6XfyVX mCVtlFO1dzdlCPX9cEFDYtRA1vRx0NbA/8WjyVaXfSi43cMzWFU1jsk3hQC0yYQv/I+k az9JWrwvMA7nJQTO62Tir8mG8Yz1V6YYOyKaFKrBAXIlocAa+k0V6ljSwXr5Ud9NiLM5 3zekBMA6JK58V4bqKa9nV+B/ktKhFA+a5zmWk4f+3t4onIRXDvNeuJc5x0xDQt1eNCbp Ao9Jd3p5cf1isoJyciqCIyBqYL+SMs0q5AwYHwWslC1Eb8JVSgC96a6O/2fNnVE3fb7n /omQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1733157264; x=1733762064; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=KQzoJvgiBJpPAoV0hgHKrOf1rIcxei4fb/suMYJ1bQ4=; b=vv2ZBzQB6aO7yCNFvF9ubrVY/f/Y0qOK3ydZU8KT1o+3LcOBAYeEUaBOXfXtLljv5q zVtTZYdhwkKWfeSCQIzZbuu99eyINSivYtKhoFADFrMnV4AnH5qoTHAQddkoBj3v2tFK 4canMXUt3fufXz0b9Po7ql4Zwh0wwB/eMQ+5FsthlHuvk08xbGXgRFNyAv9xpBnGBaoM TKfJAYQAxZFg1DgW7quYLsBAXYBFgFMy7DM4sPFes88Tj6ou9knJeZgh6U9CVSa9wCSi CZ6f2f0CI6AD2qvylni8sMRImYePhoJtiGqlWm4cbuUO6Z336Tpah84jMb0p2bZy30C8 Fhsg== X-Gm-Message-State: AOJu0Yzku6FfQpbY03uQolwTyh8MBmEILdHnEpjCHdZQ/0EtZscOloR/ zVbqXEVlqP8IxRKBLbBSRqdPjLORj0Z7S6AGLWXGsTCwY/uaFytg X-Gm-Gg: ASbGncuCjCgqnhOoBnhisBFrwfg/+t0yesZEjU6gsj124FbY+SQ/oeQKFtwMmVZq0g6 U1Q8B2rR0W2CWbP15McM6OmfFfTZ6LLe9EMXxiTXoTIOst+9rL/0gJtJ0wvUB695JibpI7VQ/WU UibY2LGCSQCAPlfaZemqqbzdxZoMpia+tmhW8fR2TIYEBf9J9fI1ATOgFAPDbe5f730R5A99iwd OUsb3ruBW7oOQhXm0cWGGFXMHKbOvOjJ+3KFkVqJxJkLsyUaiizTV09wDCP8k2UlD9/8RM/ACT4 zZ09mAH9wTHMTj2zkmha1B/TlrgFdKFYPS50u8WK X-Google-Smtp-Source: AGHT+IF6UBTxUuuyAcTKf8rQnOSHyu70o4o7V3asbYXTtACJ0yaZfal5HpwyaMEiDBwRIQmJHcTnlg== X-Received: by 2002:a05:6214:212c:b0:6d8:883b:142a with SMTP id 6a1803df08f44-6d8883b1960mr229854136d6.2.1733157263824; Mon, 02 Dec 2024 08:34:23 -0800 (PST) Received: from nathan (162-195-168-172.lightspeed.stlsmo.sbcglobal.net. [162.195.168.172]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-6d88880a3fesm37878256d6.74.2024.12.02.08.34.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Dec 2024 08:34:23 -0800 (PST) Date: Mon, 2 Dec 2024 10:34:21 -0600 From: Nathan Bossart To: Dean Rasheed Cc: pgsql-hackers@postgresql.org Subject: Re: revamp row-security tracking Message-ID: References: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="hOEVYYD8Jvq7AOUJ" Content-Disposition: inline In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --hOEVYYD8Jvq7AOUJ Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Fri, Nov 29, 2024 at 10:01:41AM +0000, Dean Rasheed wrote: > On Thu, 21 Nov 2024 at 18:00, Nathan Bossart wrote: >> The attached patch accomplishes this by establishing a global queue of >> row-security "nest levels" that the aforementioned higher-level callers can >> use. > > I'm not convinced that this is an improvement. Thanks for reviewing. > The code in check_sql_fn_retval() is building a Query struct from > scratch, so it seems perfectly natural for it to be responsible for > setting all the required fields, based on the information it has > available. With this patch, check_sql_fn_retval() is returning a > potentially incorrectly marked Query at the end of the querytree list, > which the caller is responsible for fixing up, which doesn't seem > ideal. While it is indeed natural for the code that builds a Query to be responsible for setting it correctly, unfortunately there's no backstop if someone forgets to do so (as was the case in the recent CVE). I don't think my v1 patch would necessarily prevent all such problems, but I do think it would help prevent some. > There is exactly one place where RLS policies are applied, and it > seems much more natural for it to have responsibility for setting this > flag. I think that a slightly neater way for it to handle that would > be to modify fireRIRrules(), adding an extra parameter "bool > *hasRowSecurity" that it would set to true if RLS is enabled for the > query it is rewriting. Doing that forces all callers to think about > whether or not that affects some outer query. For example, > ApplyRetrieveRule() would then do: > > rule_action = fireRIRrules(rule_action, activeRIRs, > &parsetree->hasRowSecurity); > > rather than having a separate second step to update the flag on > "parsetree", and similarly for fireRIRrules()'s recursive calls to > itself. If, in the future, it becomes necessary to invoke > fireRIRrules() on more parts of a Query, it's then much more likely > that the new code won't forget to update the parent query's flag. I've attempted this in the attached v2 patch. I do think this is an improvement over the status quo, but I worry that it doesn't go far enough. -- nathan --hOEVYYD8Jvq7AOUJ Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v2-0001-revamp-row-security-tracking.patch" From 0c81e04c3e62cd178dff9fc5b5f671e41fd39f28 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Mon, 2 Dec 2024 10:24:49 -0600 Subject: [PATCH v2 1/1] revamp row security tracking --- src/backend/rewrite/rewriteHandler.c | 42 +++++++++++++++++----------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index ab2e2cd647..c9ad429703 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -94,7 +94,7 @@ static void markQueryForLocking(Query *qry, Node *jtnode, bool pushedDown); static List *matchLocks(CmdType event, Relation relation, int varno, Query *parsetree, bool *hasUpdate); -static Query *fireRIRrules(Query *parsetree, List *activeRIRs); +static Query *fireRIRrules(Query *parsetree, List *activeRIRs, bool *hasRowSecurity); static Bitmapset *adjust_view_column_set(Bitmapset *cols, List *targetlist); @@ -1730,6 +1730,7 @@ ApplyRetrieveRule(Query *parsetree, RangeTblEntry *rte; RowMarkClause *rc; int numCols; + bool hasRowSecurity = false; if (list_length(rule->actions) != 1) elog(ERROR, "expected just one rule action"); @@ -1843,13 +1844,13 @@ ApplyRetrieveRule(Query *parsetree, /* * Recursively expand any view references inside the view. */ - rule_action = fireRIRrules(rule_action, activeRIRs); + rule_action = fireRIRrules(rule_action, activeRIRs, &hasRowSecurity); /* * Make sure the query is marked as having row security if the view query * does. */ - parsetree->hasRowSecurity |= rule_action->hasRowSecurity; + parsetree->hasRowSecurity |= hasRowSecurity; /* * Now, plug the view query in as a subselect, converting the relation's @@ -1971,15 +1972,17 @@ fireRIRonSubLink(Node *node, fireRIRonSubLink_context *context) if (IsA(node, SubLink)) { SubLink *sub = (SubLink *) node; + bool hasRowSecurity = false; /* Do what we came for */ sub->subselect = (Node *) fireRIRrules((Query *) sub->subselect, - context->activeRIRs); + context->activeRIRs, + &hasRowSecurity); /* * Remember if any of the sublinks have row security. */ - context->hasRowSecurity |= ((Query *) sub->subselect)->hasRowSecurity; + context->hasRowSecurity |= hasRowSecurity; /* Fall through to process lefthand args of SubLink */ } @@ -2000,7 +2003,7 @@ fireRIRonSubLink(Node *node, fireRIRonSubLink_context *context) * rules for, used to detect/reject recursion. */ static Query * -fireRIRrules(Query *parsetree, List *activeRIRs) +fireRIRrules(Query *parsetree, List *activeRIRs, bool *hasRowSecurity) { int origResultRelation = parsetree->resultRelation; int rt_index; @@ -2048,13 +2051,15 @@ fireRIRrules(Query *parsetree, List *activeRIRs) */ if (rte->rtekind == RTE_SUBQUERY) { - rte->subquery = fireRIRrules(rte->subquery, activeRIRs); + bool hasRowSecurity_internal = false; + + rte->subquery = fireRIRrules(rte->subquery, activeRIRs, &hasRowSecurity_internal); /* * While we are here, make sure the query is marked as having row * security if any of its subqueries do. */ - parsetree->hasRowSecurity |= rte->subquery->hasRowSecurity; + parsetree->hasRowSecurity |= hasRowSecurity_internal; continue; } @@ -2166,15 +2171,16 @@ fireRIRrules(Query *parsetree, List *activeRIRs) foreach(lc, parsetree->cteList) { CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc); + bool hasRowSecurity_internal = false; cte->ctequery = (Node *) - fireRIRrules((Query *) cte->ctequery, activeRIRs); + fireRIRrules((Query *) cte->ctequery, activeRIRs, &hasRowSecurity_internal); /* * While we are here, make sure the query is marked as having row * security if any of its CTEs do. */ - parsetree->hasRowSecurity |= ((Query *) cte->ctequery)->hasRowSecurity; + parsetree->hasRowSecurity |= hasRowSecurity_internal; } /* @@ -2211,7 +2217,7 @@ fireRIRrules(Query *parsetree, List *activeRIRs) Relation rel; List *securityQuals; List *withCheckOptions; - bool hasRowSecurity; + bool hasRowSecurity_internal = false; bool hasSubLinks; ++rt_index; @@ -2229,7 +2235,7 @@ fireRIRrules(Query *parsetree, List *activeRIRs) */ get_row_security_policies(parsetree, rte, rt_index, &securityQuals, &withCheckOptions, - &hasRowSecurity, &hasSubLinks); + &hasRowSecurity_internal, &hasSubLinks); if (securityQuals != NIL || withCheckOptions != NIL) { @@ -2278,10 +2284,10 @@ fireRIRrules(Query *parsetree, List *activeRIRs) /* * We can ignore the value of fire_context.hasRowSecurity - * since we only reach this code in cases where hasRowSecurity - * is already true. + * since we only reach this code in cases where + * hasRowSecurity_internal is already true. */ - Assert(hasRowSecurity); + Assert(hasRowSecurity_internal); activeRIRs = list_delete_last(activeRIRs); } @@ -2303,7 +2309,7 @@ fireRIRrules(Query *parsetree, List *activeRIRs) * Make sure the query is marked correctly if row-level security * applies, or if the new quals had sublinks. */ - if (hasRowSecurity) + if (hasRowSecurity_internal) parsetree->hasRowSecurity = true; if (hasSubLinks) parsetree->hasSubLinks = true; @@ -2311,6 +2317,7 @@ fireRIRrules(Query *parsetree, List *activeRIRs) table_close(rel, NoLock); } + *hasRowSecurity = parsetree->hasRowSecurity; return parsetree; } @@ -4463,8 +4470,9 @@ QueryRewrite(Query *parsetree) foreach(l, querylist) { Query *query = (Query *) lfirst(l); + bool hasRowSecurity pg_attribute_unused() = false; - query = fireRIRrules(query, NIL); + query = fireRIRrules(query, NIL, &hasRowSecurity); query->queryId = input_query_id; -- 2.39.5 (Apple Git-154) --hOEVYYD8Jvq7AOUJ--