BUG #19508: pg_buffercache_pages() crashes the backend with an incompatible caller-supplied record definition

Started by PG Bug reporting form20 days ago10 messagesbugs
Jump to latest
#1PG Bug reporting form
noreply@postgresql.org

The following bug has been logged on the website:

Bug reference: 19508
Logged by: Nikita Kalinin
Email address: n.kalinin@postgrespro.ru
PostgreSQL version: 19beta1
Operating system: Fedora 44
Description:

Hello,

It appears that pg_buffercache_pages() trusts a caller-supplied record
descriptor without verifying that the declared column types match the actual
values returned by the function.

The crash is reproducible on the current master branch with a fresh cluster
after installing the extension:

CREATE EXTENSION pg_buffercache;

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;

postgres=# select version();
version
-------------------------------------------------------------------------------------------------------------
PostgreSQL 19beta1 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 16.1.1
20260515 (Red Hat 16.1.1-2), 64-bit
(1 row)

The only difference from the types documented for pg_buffercache_pages() is
that isdirty is declared as text instead of boolean.

git blame points to the following commit:

commit 257c8231bf97a77378f6fedb826b1243f0a41612 (HEAD)
Author: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Tue Apr 7 16:04:48 2026 +0300

Modernize and optimize pg_buffercache_pages()

Backtrace:

#0 0x00000000004b2565 in VARATT_CAN_MAKE_SHORT (PTR=<optimized out>)
at ../../../../src/include/varatt.h:419
#1 heap_compute_data_size (tupleDesc=tupleDesc@entry=0x3e5ba110,
values=values@entry=0x7ffd0dc219c0, isnull=isnull@entry=0x7ffd0dc219b4)
at heaptuple.c:239
#2 0x00000000004b3bff in heap_form_minimal_tuple
(tupleDescriptor=0x3e5ba110,
values=values@entry=0x7ffd0dc219c0, isnull=isnull@entry=0x7ffd0dc219b4,
extra=extra@entry=0)
at heaptuple.c:1434
#3 0x0000000000a6fa09 in tuplestore_putvalues (state=0x3e5cc0d8,
tdesc=<optimized out>,
values=values@entry=0x7ffd0dc219c0, isnull=isnull@entry=0x7ffd0dc219b4)
at tuplestore.c:791
#4 0x00007f180fa0447e in pg_buffercache_pages (fcinfo=<optimized out>)
at pg_buffercache_pages.c:202
#5 0x00000000006b7e35 in ExecMakeTableFunctionResult (setexpr=0x3e5b9e98,
econtext=0x3e5b9d38,
argContext=<optimized out>, expectedDesc=0x3e5ba110, randomAccess=false)
at execSRF.c:235
#6 0x00000000006ccc57 in FunctionNext (node=0x3e5b9b28) at
nodeFunctionscan.c:95
#7 0x00000000006daf22 in ExecProcNode (node=0x3e5b9b28)
at ../../../src/include/executor/executor.h:327
#8 ExecLimit (pstate=0x3e5b97b8) at nodeLimit.c:95
#9 0x00000000006ac39a in ExecProcNode (node=0x3e5b97b8)
at ../../../src/include/executor/executor.h:327
#10 ExecutePlan (queryDesc=0x3e5d7d80, operation=CMD_SELECT,
sendTuples=true, numberTuples=0,
direction=<optimized out>, dest=0x3e5e11f8) at execMain.c:1736
#11 standard_ExecutorRun (queryDesc=0x3e5d7d80, direction=<optimized out>,
count=0)
at execMain.c:377
#12 0x00000000008c61d8 in PortalRunSelect (portal=portal@entry=0x3e52f130,
forward=forward@entry=true, count=0, count@entry=9223372036854775807,
dest=dest@entry=0x3e5e11f8) at pquery.c:917
#13 0x00000000008c78be in PortalRun (portal=portal@entry=0x3e52f130,
count=count@entry=9223372036854775807, isTopLevel=isTopLevel@entry=true,
dest=dest@entry=0x3e5e11f8, altdest=altdest@entry=0x3e5e11f8,
qc=qc@entry=0x7ffd0dc21e20)
at pquery.c:761
#14 0x00000000008c3548 in exec_simple_query (
query_string=0x3e48b800 "SELECT *\nFROM pg_buffercache_pages() AS p(\n
bufferid integer,\n relfilenode oid,\n reltablespace oid,\n reldatabase
oid,\n relforknumber smallint,\n relblocknumber bigint,\n isdirty text,\n
usagecoun"...) at postgres.c:1290
#15 0x00000000008c5021 in PostgresMain (dbname=<optimized out>,
username=<optimized out>)
at postgres.c:4856
#16 0x00000000008bf01d in BackendMain (startup_data=<optimized out>,
startup_data_len=<optimized out>) at backend_startup.c:124
#17 0x00000000007fefae in postmaster_child_launch (child_type=<optimized
out>, child_slot=1,
startup_data=startup_data@entry=0x7ffd0dc22270,
startup_data_len=startup_data_len@entry=24,
client_sock=client_sock@entry=0x7ffd0dc22290) at launch_backend.c:268
#18 0x00000000008029b6 in BackendStartup (client_sock=0x7ffd0dc22290) at
postmaster.c:3627
#19 ServerLoop () at postmaster.c:1728
#20 0x0000000000804479 in PostmasterMain (argc=argc@entry=3,
argv=argv@entry=0x3e434fe0)
at postmaster.c:1415
#21 0x00000000004a1c18 in main (argc=3, argv=0x3e434fe0) at main.c:231

For comparison, the same query executed on REL_18_STABLE is rejected with a
regular error:

ERROR: function return row and query-specified return row do not match
DETAIL: Returned type boolean at ordinal position 7, but query expects
text.

#2Fujii Masao
masao.fujii@gmail.com
In reply to: PG Bug reporting form (#1)
Re: BUG #19508: pg_buffercache_pages() crashes the backend with an incompatible caller-supplied record definition

On Fri, Jun 5, 2026 at 12:49 AM PG Bug reporting form
<noreply@postgresql.org> wrote:

The following bug has been logged on the website:

Bug reference: 19508
Logged by: Nikita Kalinin
Email address: n.kalinin@postgrespro.ru
PostgreSQL version: 19beta1
Operating system: Fedora 44
Description:

Hello,

It appears that pg_buffercache_pages() trusts a caller-supplied record
descriptor without verifying that the declared column types match the actual
values returned by the function.

The crash is reproducible on the current master branch with a fresh cluster
after installing the extension:

Thanks for the report! I could reproduce this as well.

git blame points to the following commit:

commit 257c8231bf97a77378f6fedb826b1243f0a41612 (HEAD)
Author: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Tue Apr 7 16:04:48 2026 +0300

Modernize and optimize pg_buffercache_pages()

Commit 257c8231bf9 changed pg_buffercache_pages() to materialize rows directly
into a tuplestore. As a result, the function started using the caller-supplied
RECORD descriptor as rsinfo->setDesc, so a mismatched column definition list
could cause tuplestore_putvalues() to interpret returned Datums with incorrect
types.

Before that change, pg_buffercache_pages() exposed its actual tuple descriptor
to the executor, allowing the executor's existing rowtype checks to reject
incompatible definitions with a normal error.

The attached patch restores that behavior while keeping the materialized-SRF
implementation. Thoughts?

Regards,

--
Fujii Masao

Attachments:

v1-0001-pg_buffercache-restore-rowtype-verification-in-pg.patchapplication/octet-stream; name=v1-0001-pg_buffercache-restore-rowtype-verification-in-pg.patchDownload+65-1
#3Ayush Tiwari
ayushtiwari.slg01@gmail.com
In reply to: Fujii Masao (#2)
Re: BUG #19508: pg_buffercache_pages() crashes the backend with an incompatible caller-supplied record definition

Hi,

On Fri, 5 Jun 2026 at 08:49, Fujii Masao <masao.fujii@gmail.com> wrote:

Commit 257c8231bf9 changed pg_buffercache_pages() to materialize rows
directly
into a tuplestore. As a result, the function started using the
caller-supplied
RECORD descriptor as rsinfo->setDesc, so a mismatched column definition
list
could cause tuplestore_putvalues() to interpret returned Datums with
incorrect
types.

Before that change, pg_buffercache_pages() exposed its actual tuple
descriptor
to the executor, allowing the executor's existing rowtype checks to reject
incompatible definitions with a normal error.

The attached patch restores that behavior while keeping the
materialized-SRF
implementation. Thoughts?

Thanks for the patch, Fujii-san!

I was looking into the bug last night, and the approach looks right to me.

This still means InitMaterializedSRF() briefly creates the caller-derived
descriptor before rsinfo->setDesc is replaced. That seems acceptable here:
the descriptor lives only in the per-query context, and avoiding a local
copy of InitMaterializedSRF() keeps the fix much smaller and less fragile.

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.

Regards,
Ayush

#4Fujii Masao
masao.fujii@gmail.com
In reply to: Ayush Tiwari (#3)
Re: BUG #19508: pg_buffercache_pages() crashes the backend with an incompatible caller-supplied record definition

On Fri, Jun 5, 2026 at 12:42 PM Ayush Tiwari
<ayushtiwari.slg01@gmail.com> 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:

v2-0001-pg_buffercache-restore-rowtype-verification-in-pg.patchapplication/octet-stream; name=v2-0001-pg_buffercache-restore-rowtype-verification-in-pg.patchDownload+65-1
#5Ayush Tiwari
ayushtiwari.slg01@gmail.com
In reply to: Fujii Masao (#4)
Re: BUG #19508: pg_buffercache_pages() crashes the backend with an incompatible caller-supplied record definition

Hi,

On Fri, 5 Jun, 2026, 13:28 Fujii Masao, <masao.fujii@gmail.com> wrote:

On Fri, Jun 5, 2026 at 12:42 PM Ayush Tiwari
<ayushtiwari.slg01@gmail.com> 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.

LGTM.

Regards,
Ayush

Show quoted text
#6Ashutosh Sharma
ashu.coek88@gmail.com
In reply to: Fujii Masao (#2)
Re: BUG #19508: pg_buffercache_pages() crashes the backend with an incompatible caller-supplied record definition

Hi Fujii-san,

On Fri, Jun 5, 2026 at 8:49 AM Fujii Masao <masao.fujii@gmail.com> wrote:

On Fri, Jun 5, 2026 at 12:49 AM PG Bug reporting form
<noreply@postgresql.org> wrote:

The following bug has been logged on the website:

Bug reference: 19508
Logged by: Nikita Kalinin
Email address: n.kalinin@postgrespro.ru
PostgreSQL version: 19beta1
Operating system: Fedora 44
Description:

Hello,

It appears that pg_buffercache_pages() trusts a caller-supplied record
descriptor without verifying that the declared column types match the actual
values returned by the function.

The crash is reproducible on the current master branch with a fresh cluster
after installing the extension:

Thanks for the report! I could reproduce this as well.

git blame points to the following commit:

commit 257c8231bf97a77378f6fedb826b1243f0a41612 (HEAD)
Author: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Tue Apr 7 16:04:48 2026 +0300

Modernize and optimize pg_buffercache_pages()

Commit 257c8231bf9 changed pg_buffercache_pages() to materialize rows directly
into a tuplestore. As a result, the function started using the caller-supplied
RECORD descriptor as rsinfo->setDesc, so a mismatched column definition list
could cause tuplestore_putvalues() to interpret returned Datums with incorrect
types.

Before that change, pg_buffercache_pages() exposed its actual tuple descriptor
to the executor, allowing the executor's existing rowtype checks to reject
incompatible definitions with a normal error.

The attached patch restores that behavior while keeping the materialized-SRF
implementation. Thoughts?

pg_buffercache_pages uses RETURNS SETOF RECORD whereas other
extensions like pgstattuple define explicit IN/OUT parameters at the
SQL level. Is there a specific reason this pattern was kept, or is it
simply a legacy design that hasn't been modernized? Had we followed
the IN/OUT parameter style, this sort of issue could have been
avoided, no?

--
With Regards,
Ashutosh Sharma.

#7Fujii Masao
masao.fujii@gmail.com
In reply to: Ashutosh Sharma (#6)
Re: BUG #19508: pg_buffercache_pages() crashes the backend with an incompatible caller-supplied record definition

On Sat, Jun 6, 2026 at 12:29 AM Ashutosh Sharma <ashu.coek88@gmail.com> wrote:

pg_buffercache_pages uses RETURNS SETOF RECORD whereas other
extensions like pgstattuple define explicit IN/OUT parameters at the
SQL level. Is there a specific reason this pattern was kept, or is it
simply a legacy design that hasn't been modernized? Had we followed
the IN/OUT parameter style, this sort of issue could have been
avoided, no?

Probably yes. But if we do that, we would likely need to bump pg_buffercache
version. I'm not sure that's worthwhile just for this change.

Regards,

--
Fujii Masao

#8Fujii Masao
masao.fujii@gmail.com
In reply to: Ayush Tiwari (#5)
Re: BUG #19508: pg_buffercache_pages() crashes the backend with an incompatible caller-supplied record definition

On Fri, Jun 5, 2026 at 5:48 PM Ayush Tiwari <ayushtiwari.slg01@gmail.com> wrote:

Agreed. I've fixed that and attached an updated version of the patch.

LGTM.

Barring any objections, I will commit the patch.

Regards,

--
Fujii Masao

#9Ashutosh Sharma
ashu.coek88@gmail.com
In reply to: Fujii Masao (#7)
Re: BUG #19508: pg_buffercache_pages() crashes the backend with an incompatible caller-supplied record definition

On Fri, Jun 5, 2026 at 9:08 PM Fujii Masao <masao.fujii@gmail.com> wrote:

On Sat, Jun 6, 2026 at 12:29 AM Ashutosh Sharma <ashu.coek88@gmail.com> wrote:

pg_buffercache_pages uses RETURNS SETOF RECORD whereas other
extensions like pgstattuple define explicit IN/OUT parameters at the
SQL level. Is there a specific reason this pattern was kept, or is it
simply a legacy design that hasn't been modernized? Had we followed
the IN/OUT parameter style, this sort of issue could have been
avoided, no?

Probably yes. But if we do that, we would likely need to bump pg_buffercache
version. I'm not sure that's worthwhile just for this change.

Okay, that makes perfect sense, thanks for the confirmation.

--
With Regards,
Ashutosh Sharma.

#10Fujii Masao
masao.fujii@gmail.com
In reply to: Fujii Masao (#8)
Re: BUG #19508: pg_buffercache_pages() crashes the backend with an incompatible caller-supplied record definition

On Tue, Jun 9, 2026 at 8:44 AM Fujii Masao <masao.fujii@gmail.com> wrote:

On Fri, Jun 5, 2026 at 5:48 PM Ayush Tiwari <ayushtiwari.slg01@gmail.com> wrote:

Agreed. I've fixed that and attached an updated version of the patch.

LGTM.

Barring any objections, I will commit the patch.

I've pushed the patch. Thanks!

Regards,

--
Fujii Masao