[PATCH] Fix improper tuple deallocation in import_pg_statist()

Started by yonghao_lee@qq.com5 days ago3 messages
Jump to latest
#1yonghao_lee@qq.com
yonghao_lee@qq.com

Hi hackers,

I found a tuple deallocation issue in the import_pg_statistic() function
in src/backend/statistics/extended_stats_funcs.c. The code uses pfree() to release a
HeapTuple, which doesn't properly free the underlying tuple data.

Bug Description:
================

In import_pg_statistic(), after heap_form_tuple() creates a HeapTuple and
heap_copy_tuple_as_datum() copies it to a Datum, the code attempts to free
the temporary HeapTuple using pfree():

    pgstup = heap_form_tuple(RelationGetDescr(pgsd), values, nulls);
    pgstdat = heap_copy_tuple_as_datum(pgstup, RelationGetDescr(pgsd));
&nbsp; &nbsp; pfree(pgstup);&nbsp; /* <-- BUG: Improper tuple release */

The Problem:
============

HeapTuple is a pointer to HeapTupleData structure, which contains a nested
pointer t_data pointing to the actual tuple header and data:

&nbsp; &nbsp; typedef struct HeapTupleData {
&nbsp; &nbsp; &nbsp; &nbsp; uint32&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t_len;
&nbsp; &nbsp; &nbsp; &nbsp; ItemPointerData t_self;
&nbsp; &nbsp; &nbsp; &nbsp; Oid&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;t_tableOid;
&nbsp; &nbsp; &nbsp; &nbsp; HeapTupleHeader t_data;
&nbsp; &nbsp; } HeapTupleData;

Using pfree(pgstup) only frees the HeapTupleData structure itself
, but leaves the underlying tuple data unfreed.

The Fix:
========

Replace pfree(pgstup) with heap_freetuple(pgstup), which properly frees both
the HeapTupleData structure and the underlying t_data:

&nbsp; &nbsp; -&nbsp; &nbsp; pfree(pgstup);
&nbsp; &nbsp; +&nbsp; &nbsp; heap_freetuple(pgstup);

Patch:
======
---
&nbsp;src/backend/statistics/extended_stats_funcs.c | 2 +-
&nbsp;1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/backend/statistics/extended_stats_funcs.c b/src/backend/statistics/extended_stats_funcs.c
index 0ec77a6..9279904 100644
--- a/src/backend/statistics/extended_stats_funcs.c
+++ b/src/backend/statistics/extended_stats_funcs.c
@@ -1509,7 +1509,7 @@ import_pg_statistic(Relation pgsd, JsonbContainer *cont,
&nbsp;	pgstup = heap_form_tuple(RelationGetDescr(pgsd), values, nulls);
&nbsp;	pgstdat = heap_copy_tuple_as_datum(pgstup, RelationGetDescr(pgsd));
&nbsp;
-	pfree(pgstup);
+	heap_freetuple(pgstup);
&nbsp;
&nbsp;	*pg_statistic_ok = true;
&nbsp;
--

Regards,
Yonghao Lee

Attachments:

0001-Fix-improper-tuple-deallocation-in-import_pg_statist.patchapplication/octet-stream; charset=ISO-8859-1; name=0001-Fix-improper-tuple-deallocation-in-import_pg_statist.patchDownload+1-2
#2Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: yonghao_lee@qq.com (#1)
Re: [PATCH] Fix improper tuple deallocation in import_pg_statist()

On 3/5/26 01:40, yonghao_lee wrote:

Hi hackers,

I found a tuple deallocation issue in the import_pg_statistic() function
in src/backend/statistics/extended_stats_funcs.c. The code uses pfree()
to release a
HeapTuple, which doesn't properly free the underlying tuple data.

Bug Description:
================

In import_pg_statistic(), after heap_form_tuple() creates a HeapTuple and
heap_copy_tuple_as_datum() copies it to a Datum, the code attempts to free
the temporary HeapTuple using pfree():

    pgstup = heap_form_tuple(RelationGetDescr(pgsd), values, nulls);
    pgstdat = heap_copy_tuple_as_datum(pgstup, RelationGetDescr(pgsd));
    pfree(pgstup);  /* <-- BUG: Improper tuple release */

The Problem:
============

HeapTuple is a pointer to HeapTupleData structure, which contains a nested
pointer t_data pointing to the actual tuple header and data:

    typedef struct HeapTupleData {
        uint32          t_len;
        ItemPointerData t_self;
        Oid             t_tableOid;
        HeapTupleHeader t_data;
    } HeapTupleData;

Using pfree(pgstup) only frees the HeapTupleData structure itself
, but leaves the underlying tuple data unfreed.

The Fix:
========

Replace pfree(pgstup) with heap_freetuple(pgstup), which properly frees both
the HeapTupleData structure and the underlying t_data:

    -    pfree(pgstup);
    +    heap_freetuple(pgstup);

Except that heap_freetuple is defined like this:

void
heap_freetuple(HeapTuple htup)
{
pfree(htup);
}

so this does not change anything. This report seems to miss how tuples
are allocated in heap_form_tuple.

--
Tomas Vondra

#3Michael Paquier
michael@paquier.xyz
In reply to: Tomas Vondra (#2)
Re: [PATCH] Fix improper tuple deallocation in import_pg_statist()

On Thu, Mar 05, 2026 at 02:01:32AM +0100, Tomas Vondra wrote:

Except that heap_freetuple is defined like this:

void
heap_freetuple(HeapTuple htup)
{
pfree(htup);
}

so this does not change anything. This report seems to miss how tuples
are allocated in heap_form_tuple.

Right, what is obviously an LLM analysis forgot the context of how
HeapTuple allocations happen when these are formed.

Saying that, this is new code, that I had the idea to commit like
this, and I don't like fresh inconsistencies. We also use
heap_freetuple() in upsert_pg_statistic_ext_data(). Hence, at the
end, done. It was harmless as-is, still..
--
Michael