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 1wMdpv-000Esf-1E for pgsql-hackers@arkaria.postgresql.org; Tue, 12 May 2026 03:31:11 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wMdps-0031tw-0Q for pgsql-hackers@arkaria.postgresql.org; Tue, 12 May 2026 03:31:08 +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 1wMdpr-0031tn-0o for pgsql-hackers@lists.postgresql.org; Tue, 12 May 2026 03:31:08 +0000 Received: from fhigh-a4-smtp.messagingengine.com ([103.168.172.155]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1wMdpm-000000009ak-42AF for pgsql-hackers@lists.postgresql.org; Tue, 12 May 2026 03:31:07 +0000 Received: from phl-compute-06.internal (phl-compute-06.internal [10.202.2.46]) by mailfhigh.phl.internal (Postfix) with ESMTP id 74D1A1400208; Mon, 11 May 2026 23:31:00 -0400 (EDT) Received: from phl-frontend-03 ([10.202.2.162]) by phl-compute-06.internal (MEProxy); Mon, 11 May 2026 23:31:00 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=paquier.xyz; h= cc:cc:content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:subject :subject:to:to; s=fm3; t=1778556660; x=1778643060; bh=8n2m6XoCSb VVfnMlpVIUTQOc3xdD7ch1rNiW940XV2A=; b=nz6i+gstM2fgYsI29ovMx/dKXj 3qUYHxBNvPi1pFPndBbBc24z17x/RYCsW+Mr1rfjsTliQyZiOZm/npYgsSlws/dE QluaycOxRSl4LBJ05zUHFj8kGoEitEVGiRc/0jetvlEsdOJSjCOQeX27o6R9aVpi 21Nk4U5mxkKVsWZm0bNg4UEVJyEsfMmTRQO47EpLWt3uOEewYxV0y8+GFPfSyOe2 ZOn5P1iUwk8nizrF2gC2WU0r46GUAHZQrz5LH3LfpypprX2UFDNokC1sLPM3arvh erP+KQoMClqdLnumvEN4g286nfQoc6R5OxhgZRglhhsEKKZivBqGco7UYU5A== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm3; t= 1778556660; x=1778643060; bh=8n2m6XoCSbVVfnMlpVIUTQOc3xdD7ch1rNi W940XV2A=; b=bwzsW6X2Fty6N5XTUEz3M0NWZpwy0IjKcXGHvbilGqqZ5VmSdg3 AByhWWThjdbs/0BBF0fc8nNEMkQNKScwvqbzSxDvx1azhyWiDd6MdDX4F3wu3G7P 6Y5vvVcT61dEtGreVmaxjshBjVgEMPFVjqCyiaFcRa7NNbdrsfyeU9UkxZ32Hh2k M4Ld4aDAxbPOfgRo7l3cbS/B7VT5+uVYrF3xZjnKNRJPQF9lyUPbg2slqofBnmua Q6YFhgMwPHyfXMw74kZZQjioo2jqm88473/Z3M0J2RaOl+zR1Er2LizbIAOwshiD ygOuoW+Qttk63loqMBwYFPJ6RKt2a+FfiKw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefhedrtddtgdduvddtjeefucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnegfrh hlucfvnfffucdljedtmdenucfjughrpeffhffvvefukfhfgggtuggjsehgtderredttddv necuhfhrohhmpefoihgthhgrvghlucfrrghquhhivghruceomhhitghhrggvlhesphgrqh huihgvrhdrgiihiieqnecuggftrfgrthhtvghrnhepteelieefudffhffhtdetleeggeeg fffhkeeuveetiefgudduvedutefggeeivdejnecuvehluhhsthgvrhfuihiivgeptdenuc frrghrrghmpehmrghilhhfrhhomhepmhhitghhrggvlhesphgrqhhuihgvrhdrgiihiidp nhgspghrtghpthhtohepvddpmhhouggvpehsmhhtphhouhhtpdhrtghpthhtoheplhhird gvvhgrnhdrtghhrghosehgmhgrihhlrdgtohhmpdhrtghpthhtohepphhgshhqlhdqhhgr tghkvghrsheslhhishhtshdrphhoshhtghhrvghsqhhlrdhorhhg X-ME-Proxy: Feedback-ID: i0fe9450f:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Mon, 11 May 2026 23:30:59 -0400 (EDT) Date: Tue, 12 May 2026 12:30:53 +0900 From: Michael Paquier To: Chao Li Cc: PostgreSQL Hackers Subject: Re: Fix unsafe PlannedStmt access in pg_stat_statements Message-ID: References: <2F91906A-F2B5-4A6B-9695-D136957D4545@gmail.com> MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha512; protocol="application/pgp-signature"; boundary="sf0x10Q1cKmw9eot" Content-Disposition: inline In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --sf0x10Q1cKmw9eot Content-Type: multipart/mixed; boundary="tGrYNh8ptNxdmksD" Content-Disposition: inline --tGrYNh8ptNxdmksD Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Mon, May 11, 2026 at 04:11:41PM +0800, Chao Li wrote: > On May 11, 2026, at 16:07, Chao Li wrote: >> In pgss_ProcessUtility(), there is this comment: >> ``` >> /* >> * CAUTION: do not access the *pstmt data structure again below here. >> * If it was a ROLLBACK or similar, that data structure may have been >> * freed. We must copy everything we still need into local variables, >> * which we did above. >> * >> * For the same reason, we can't risk restoring pstmt->queryId to its >> * former value, which'd otherwise be a good idea. >> */ >> ``` >>=20 >> The attached patch fixes this by saving pstmt->planOrigin, >> following the same pattern already used for queryId, stmt_location, >> and stmt_len. Yeah, you are right. This code should save the planOrigin but it does not do so. This is also pointing at a second issue that is not addressed by your patch: there is no coverage for the assumption of this comment, or we would have known about this issue when 3357471cf9f5 was committed. Running the regression tests of PGSS with valgrind enabled leads to no reports. And that's where things got hairy on my side because it took me a few hours before finding a case where I was able to get the following report, where a PlannedStmt can get freed while we are in the hook path: =3D=3D28059=3D=3D Invalid read of size 4 =3D=3D28059=3D=3D at 0x165E42DF: pgss_ProcessUtility (pg_stat_statements= =2Ec:1200) =3D=3D28059=3D=3D by 0x158AFCD: ProcessUtility (utility.c:524) =3D=3D28059=3D=3D by 0x1587F3B: PortalRunUtility (pquery.c:1149) =3D=3D28059=3D=3D by 0x15877E9: FillPortalStore (pquery.c:1022) This has come down to the extended protocol and a ROLLBACK inside a CALL procedure. The internal ROLLBACK is able to free the PlannedStmt in pgss_ProcessUtility() in this case, after two executions. > Oops! Forgot the attachment. No problem. All that said, I am attaching an updated version of the patch, for posterity. I'll apply that in a bit with your fix and the test on HEAD, after more checks. -- Michael --tGrYNh8ptNxdmksD Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=0001-pg_stat_statements-Fix-potential-use-after-free-of-P.patch Content-Transfer-Encoding: quoted-printable =46rom 19281d8708facd17cd65a9ab07dd7b775496a5c9 Mon Sep 17 00:00:00 2001 =46rom: Michael Paquier Date: Tue, 12 May 2026 12:24:52 +0900 Subject: [PATCH] pg_stat_statements: Fix potential use-after-free of PlannedStmt Blahblahblah. Author: Chao Li Co-authored-by: Michael Paquier Discussion: https://postgr.es/m/ --- .../pg_stat_statements/expected/plancache.out | 38 +++++++++++++++++++ .../pg_stat_statements/pg_stat_statements.c | 3 +- contrib/pg_stat_statements/sql/plancache.sql | 19 ++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/contrib/pg_stat_statements/expected/plancache.out b/contrib/pg= _stat_statements/expected/plancache.out index 32bf913b2861..d0796d5693c0 100644 --- a/contrib/pg_stat_statements/expected/plancache.out +++ b/contrib/pg_stat_statements/expected/plancache.out @@ -216,6 +216,44 @@ SELECT calls, generic_plan_calls, custom_plan_calls, t= oplevel, query FROM pg_sta =20 RESET pg_stat_statements.track; -- +-- Procedure with internal ROLLBACK and the extended query protocol. +-- The PlannedStmt used in pgss_ProcessUtility() is freed by the internal +-- ROLLBACK. +-- +CREATE OR REPLACE PROCEDURE rollback_proc(a INOUT int) AS $$ +BEGIN + ROLLBACK; +END; +$$ LANGUAGE plpgsql; +SELECT pg_stat_statements_reset() IS NOT NULL AS t; + t=20 +--- + t +(1 row) + +CALL rollback_proc($1) \parse stmt_rollback +\bind_named stmt_rollback 1 \g + a=20 +--- + 1 +(1 row) + +\bind_named stmt_rollback 2 \g + a=20 +--- + 2 +(1 row) + +SELECT calls, query FROM pg_stat_statements + WHERE query LIKE '%rollback_proc%' + ORDER BY query COLLATE "C"; + calls | query =20 +-------+------------------------ + 2 | CALL rollback_proc($1) +(1 row) + +DROP PROCEDURE rollback_proc; +-- -- Cleanup -- DROP FUNCTION select_one_func(int); diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_s= tat_statements/pg_stat_statements.c index 95a5411a39d9..a2d3ab770cc6 100644 --- a/contrib/pg_stat_statements/pg_stat_statements.c +++ b/contrib/pg_stat_statements/pg_stat_statements.c @@ -1099,6 +1099,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *q= ueryString, int64 saved_queryId =3D pstmt->queryId; int saved_stmt_location =3D pstmt->stmt_location; int saved_stmt_len =3D pstmt->stmt_len; + PlannedStmtOrigin saved_planOrigin =3D pstmt->planOrigin; bool enabled =3D pgss_track_utility && pgss_enabled(nesting_level); =20 /* @@ -1210,7 +1211,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *q= ueryString, NULL, 0, 0, - pstmt->planOrigin); + saved_planOrigin); } else { diff --git a/contrib/pg_stat_statements/sql/plancache.sql b/contrib/pg_stat= _statements/sql/plancache.sql index 160ced7add36..948d3e985180 100644 --- a/contrib/pg_stat_statements/sql/plancache.sql +++ b/contrib/pg_stat_statements/sql/plancache.sql @@ -87,6 +87,25 @@ SELECT calls, generic_plan_calls, custom_plan_calls, top= level, query FROM pg_sta =20 RESET pg_stat_statements.track; =20 +-- +-- Procedure with internal ROLLBACK and the extended query protocol. +-- The PlannedStmt used in pgss_ProcessUtility() is freed by the internal +-- ROLLBACK. +-- +CREATE OR REPLACE PROCEDURE rollback_proc(a INOUT int) AS $$ +BEGIN + ROLLBACK; +END; +$$ LANGUAGE plpgsql; +SELECT pg_stat_statements_reset() IS NOT NULL AS t; +CALL rollback_proc($1) \parse stmt_rollback +\bind_named stmt_rollback 1 \g +\bind_named stmt_rollback 2 \g +SELECT calls, query FROM pg_stat_statements + WHERE query LIKE '%rollback_proc%' + ORDER BY query COLLATE "C"; +DROP PROCEDURE rollback_proc; + -- -- Cleanup -- --=20 2.54.0 --tGrYNh8ptNxdmksD-- --sf0x10Q1cKmw9eot Content-Type: application/pgp-signature; name=signature.asc -----BEGIN PGP SIGNATURE----- iQIzBAEBCgAdFiEEG72nH6vTowiyblFKnvQgOdbyQH0FAmoCnu0ACgkQnvQgOdby QH17LxAAllX5tzkEeT61VjKP7netqRx7eKE2W99/VVhLKjRIn6tr/ImFLb9Qn0i1 V9/fECddMiLQouAEMqv6YhKSFI/x2FMcoG+rH5eiHOeRAm78OAQpgAl6AR3074Uz YP8ED1O67SLbBCxbzPUBqrgYd0+z6RH/8yqdUn0YUayn9inppfPzRrezAnbsFto6 /Ce3vjAdRPuA1926gxy4JK5LCZY7vEf5SBQclOB42T3R1EWRr2XOQaLcS1NnQ+kL 9TUXyvmkb66JGb/ZtN1rjAkljU0NO8Uz8JAjZ88R1HevVRubfmXBalQmNK3hoHnI c5ZGtAtLFu6XMA3FgUzKviyewdbBxp2jLXQa0dvooPG0DZRxKAclANF7gBEQic41 Rtn/GMR12+SGCF+6SiYgwpKZwMBZNf4V4QMOCUqMnGYxiI+5OrntRObPWckrQnDH S4wiFUufCEff0iIu2yFIMevnrQDXlDnrQxNIU8+nokfoVcnOGOQaRjO+AcL0VsU7 XxKyQ1yqxwfVECNGCvbIXrAkGSLCdJptzhBBE8OfeIgd2Gz6IjX0ZXxBN5jHzP3V KllzvE9BQqje6IzTksrg0NXWaxpFs4d3MY07c7EOHCAGs+UJxAh5p4FsUQc7bWRp 4VUIWQ7ZQoTB//WXsRig84vXhNhqXgGz6cxt88iMVtK6CWL3cxc= =lgwj -----END PGP SIGNATURE----- --sf0x10Q1cKmw9eot--