0001-Fix-variable-stats-leak-in-_int_matchsel-early-retur.patch

application/octet-stream

Filename: 0001-Fix-variable-stats-leak-in-_int_matchsel-early-retur.patch
Type: application/octet-stream
Part: 0
Message: Re:BUG #19492: intarray: fix variable stats leak in _int_matchsel

Patch

Format: format-patch
Series: patch 0001
Subject: Fix variable stats leak in _int_matchsel early return path
File+
contrib/intarray/expected/_int.out 21 0
contrib/intarray/_int_selfuncs.c 3 0
contrib/intarray/sql/_int.sql 20 0
From 8e5606371df544f8be48937419fe54c96a358048 Mon Sep 17 00:00:00 2001
From: Man Zeng <zengman@halodbtech.com>
Date: Sun, 24 May 2026 12:36:36 +0800
Subject: [PATCH] Fix variable stats leak in _int_matchsel early return path

When vardata.vartype != INT4ARRAYOID, _int_matchsel returns
DEFAULT_EQ_SEL without releasing the variable stats previously
acquired via examine_variable(), causing a memory leak. Add the
missing ReleaseVariableStats() call.
---
 contrib/intarray/_int_selfuncs.c   |  3 +++
 contrib/intarray/expected/_int.out | 21 +++++++++++++++++++++
 contrib/intarray/sql/_int.sql      | 20 ++++++++++++++++++++
 3 files changed, 44 insertions(+)

diff --git a/contrib/intarray/_int_selfuncs.c b/contrib/intarray/_int_selfuncs.c
index 7fce743632f..c0161928074 100644
--- a/contrib/intarray/_int_selfuncs.c
+++ b/contrib/intarray/_int_selfuncs.c
@@ -151,7 +151,10 @@ _int_matchsel(PG_FUNCTION_ARGS)
 	 * query_int.
 	 */
 	if (vardata.vartype != INT4ARRAYOID)
+	{
+		ReleaseVariableStats(vardata);
 		PG_RETURN_FLOAT8(DEFAULT_EQ_SEL);
+	}
 
 	/*
 	 * Can't do anything useful if the something is not a constant, either.
diff --git a/contrib/intarray/expected/_int.out b/contrib/intarray/expected/_int.out
index fb4086a95ca..8ae730a535d 100644
--- a/contrib/intarray/expected/_int.out
+++ b/contrib/intarray/expected/_int.out
@@ -1026,3 +1026,24 @@ SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
 (1 row)
 
 RESET enable_seqscan;
+CREATE OR REPLACE FUNCTION my_text_eq(a text, b text) RETURNS boolean AS $$
+BEGIN
+    RETURN a = b;
+END;
+$$ LANGUAGE plpgsql;
+CREATE OPERATOR %% (
+    LEFTARG = text,
+    RIGHTARG = text,
+    FUNCTION = my_text_eq,
+    RESTRICT = _int_matchsel
+);
+CREATE TEMP TABLE test_vuln (t text);
+INSERT INTO test_vuln SELECT md5(i::text) FROM generate_series(1, 1000) i;
+ANALYZE test_vuln;
+SELECT * FROM test_vuln WHERE t %% 'test'::text;
+ t 
+---
+(0 rows)
+
+DROP FUNCTION my_text_eq CASCADE;
+NOTICE:  drop cascades to operator %%(text,text)
diff --git a/contrib/intarray/sql/_int.sql b/contrib/intarray/sql/_int.sql
index 0d30914725e..765b1138772 100644
--- a/contrib/intarray/sql/_int.sql
+++ b/contrib/intarray/sql/_int.sql
@@ -252,3 +252,23 @@ SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
 
 
 RESET enable_seqscan;
+
+CREATE OR REPLACE FUNCTION my_text_eq(a text, b text) RETURNS boolean AS $$
+BEGIN
+    RETURN a = b;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OPERATOR %% (
+    LEFTARG = text,
+    RIGHTARG = text,
+    FUNCTION = my_text_eq,
+    RESTRICT = _int_matchsel
+);
+
+CREATE TEMP TABLE test_vuln (t text);
+INSERT INTO test_vuln SELECT md5(i::text) FROM generate_series(1, 1000) i;
+ANALYZE test_vuln;
+
+SELECT * FROM test_vuln WHERE t %% 'test'::text;
+DROP FUNCTION my_text_eq CASCADE;
-- 
2.45.2