public inbox for [email protected]
help / color / mirror / Atom feedFrom: Fujii Masao <[email protected]>
To: Ayush Tiwari <[email protected]>
Cc: [email protected]
Cc: [email protected]
Subject: Re: BUG #19508: pg_buffercache_pages() crashes the backend with an incompatible caller-supplied record definition
Date: Fri, 5 Jun 2026 16:58:07 +0900
Message-ID: <CAHGQGwHnVc97gVZU22zwRQjbhAWQy0Jgm0yrAErgxROSbuAsXg@mail.gmail.com> (raw)
In-Reply-To: <CAJTYsWX6b0UxeZ9HGGnju-XpWRmLr_y8AVeyWqd3gL+a0NkGtw@mail.gmail.com>
References: <[email protected]>
<CAHGQGwGaC8RNeVP3A4_R1OcHJ-DrXgr2HH2UEYrt-Ueu516Gng@mail.gmail.com>
<CAJTYsWX6b0UxeZ9HGGnju-XpWRmLr_y8AVeyWqd3gL+a0NkGtw@mail.gmail.com>
On Fri, Jun 5, 2026 at 12:42 PM Ayush Tiwari
<[email protected]> wrote:
> One small nit: build_buffercache_pages_tupledesc() names attribute 8
> "usage_count", while the existing pg_buffercache view and the test use
> "usagecount". This probably does not affect the tupledesc_match() check,
> but I think it would be better to keep the existing spelling for
> consistency.
Agreed. I've fixed that and attached an updated version of the patch.
Regards,
--
Fujii Masao
Attachments:
[application/octet-stream] v2-0001-pg_buffercache-restore-rowtype-verification-in-pg.patch (5.6K, 2-v2-0001-pg_buffercache-restore-rowtype-verification-in-pg.patch)
download | inline diff:
From 9881fc247daab96549d7aaaf60243227b3274059 Mon Sep 17 00:00:00 2001
From: "masao.fujii" <[email protected]’s-MacBook-Pro>
Date: Fri, 5 Jun 2026 10:37:12 +0900
Subject: [PATCH v2] pg_buffercache: restore rowtype verification in
pg_buffercache_pages()
Commit 257c8231bf9 changed pg_buffercache_pages() to materialize its output
directly into a tuplestore. As a result, the function ended up trusting
a caller-supplied RECORD descriptors. That could lead to crashes
if the supplied row definition did not match the actual returned values,
for example by passing bool Datums to tuplestore_putvalues() with
an incompatible descriptor.
Fix this by constructing the correct tuple descriptor for
pg_buffercache_pages() and assigning it to
rsinfo->setDesc after InitMaterializedSRF(). This restores the executor's
tupledesc_match() verification, so incompatible caller-supplied
row definitions are rejected with an error, as before commit 257c8231bf9.
Bug: #19508
Reported-by: Nikita Kalinin <[email protected]>
Author: Fujii Masao <[email protected]>
Reviewed-by: Ayush Tiwari <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
.../expected/pg_buffercache.out | 8 +++
contrib/pg_buffercache/pg_buffercache_pages.c | 51 +++++++++++++++++++
contrib/pg_buffercache/sql/pg_buffercache.sql | 6 +++
3 files changed, 65 insertions(+)
diff --git a/contrib/pg_buffercache/expected/pg_buffercache.out b/contrib/pg_buffercache/expected/pg_buffercache.out
index 886dea770f6..c52a8491ff9 100644
--- a/contrib/pg_buffercache/expected/pg_buffercache.out
+++ b/contrib/pg_buffercache/expected/pg_buffercache.out
@@ -73,6 +73,14 @@ SELECT count(*) > 0 FROM pg_buffercache_usage_counts();
t
(1 row)
+SELECT *
+FROM pg_buffercache_pages() AS p
+ (bufferid integer, relfilenode oid, reltablespace oid, reldatabase oid,
+ relforknumber smallint, relblocknumber bigint, isdirty text,
+ usagecount smallint)
+LIMIT 1;
+ERROR: function return row and query-specified return row do not match
+DETAIL: Returned type boolean at ordinal position 7, but query expects text.
RESET role;
------
---- Test pg_buffercache_evict* and pg_buffercache_mark_dirty* functions
diff --git a/contrib/pg_buffercache/pg_buffercache_pages.c b/contrib/pg_buffercache/pg_buffercache_pages.c
index bf2e6c97220..510455998aa 100644
--- a/contrib/pg_buffercache/pg_buffercache_pages.c
+++ b/contrib/pg_buffercache/pg_buffercache_pages.c
@@ -59,6 +59,8 @@ typedef struct
BufferCacheOsPagesRec *record;
} BufferCacheOsPagesContext;
+static TupleDesc build_buffercache_pages_tupledesc(int natts);
+
/*
* Function returning data from the shared buffer cache - buffer number,
@@ -86,6 +88,8 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc expected_tupledesc;
+ TupleDesc actual_tupledesc;
+ MemoryContext oldcontext;
int i;
/*
@@ -105,6 +109,21 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
InitMaterializedSRF(fcinfo, 0);
+ oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
+ actual_tupledesc = build_buffercache_pages_tupledesc(expected_tupledesc->natts);
+ MemoryContextSwitchTo(oldcontext);
+
+ /*
+ * Override the caller-supplied descriptor with the tuple descriptor that
+ * matches the values we actually return, so executor-side
+ * tupledesc_match() can verify the caller's row definition.
+ *
+ * Do not free the previous rsinfo->setDesc here: for RECORD results it
+ * can alias rsinfo->expectedDesc, which the executor still needs to
+ * reference.
+ */
+ rsinfo->setDesc = actual_tupledesc;
+
/*
* Scan through all the buffers, adding one row for each of the buffers to
* the tuplestore.
@@ -205,6 +224,38 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+static TupleDesc
+build_buffercache_pages_tupledesc(int natts)
+{
+ TupleDesc tupledesc;
+
+ tupledesc = CreateTemplateTupleDesc(natts);
+ TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid",
+ INT4OID, -1, 0);
+ TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode",
+ OIDOID, -1, 0);
+ TupleDescInitEntry(tupledesc, (AttrNumber) 3, "reltablespace",
+ OIDOID, -1, 0);
+ TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reldatabase",
+ OIDOID, -1, 0);
+ TupleDescInitEntry(tupledesc, (AttrNumber) 5, "relforknumber",
+ INT2OID, -1, 0);
+ TupleDescInitEntry(tupledesc, (AttrNumber) 6, "relblocknumber",
+ INT8OID, -1, 0);
+ TupleDescInitEntry(tupledesc, (AttrNumber) 7, "isdirty",
+ BOOLOID, -1, 0);
+ TupleDescInitEntry(tupledesc, (AttrNumber) 8, "usagecount",
+ INT2OID, -1, 0);
+
+ if (natts == NUM_BUFFERCACHE_PAGES_ELEM)
+ TupleDescInitEntry(tupledesc, (AttrNumber) 9, "pinning_backends",
+ INT4OID, -1, 0);
+
+ TupleDescFinalize(tupledesc);
+
+ return BlessTupleDesc(tupledesc);
+}
+
/*
* Inquire about OS pages mappings for shared buffers, with NUMA information,
* optionally.
diff --git a/contrib/pg_buffercache/sql/pg_buffercache.sql b/contrib/pg_buffercache/sql/pg_buffercache.sql
index 127d604905c..be89b5f5a3a 100644
--- a/contrib/pg_buffercache/sql/pg_buffercache.sql
+++ b/contrib/pg_buffercache/sql/pg_buffercache.sql
@@ -34,6 +34,12 @@ SELECT count(*) > 0 FROM pg_buffercache;
SELECT count(*) > 0 FROM pg_buffercache_os_pages;
SELECT buffers_used + buffers_unused > 0 FROM pg_buffercache_summary();
SELECT count(*) > 0 FROM pg_buffercache_usage_counts();
+SELECT *
+FROM pg_buffercache_pages() AS p
+ (bufferid integer, relfilenode oid, reltablespace oid, reldatabase oid,
+ relforknumber smallint, relblocknumber bigint, isdirty text,
+ usagecount smallint)
+LIMIT 1;
RESET role;
--
2.53.0
view thread (10+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected], [email protected]
Subject: Re: BUG #19508: pg_buffercache_pages() crashes the backend with an incompatible caller-supplied record definition
In-Reply-To: <CAHGQGwHnVc97gVZU22zwRQjbhAWQy0Jgm0yrAErgxROSbuAsXg@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox